xref: /netbsd-src/games/mille/move.c (revision 17306b8fd0952c7489f93f0230818481e5a1e2c9)
1 /*	$NetBSD: move.c,v 1.12 1999/10/01 17:06:01 jsm Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)move.c	8.1 (Berkeley) 5/31/93";
40 #else
41 __RCSID("$NetBSD: move.c,v 1.12 1999/10/01 17:06:01 jsm Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include <termios.h>
46 
47 #include	"mille.h"
48 #ifndef	unctrl
49 #include	"unctrl.h"
50 #endif
51 
52 /*
53  * @(#)move.c	1.2 (Berkeley) 3/28/83
54  */
55 
56 #undef	CTRL
57 #define	CTRL(c)		(c - 'A' + 1)
58 
59 void
60 domove()
61 {
62 	PLAY	*pp;
63 	int	i, j;
64 	bool	goodplay;
65 
66 	pp = &Player[Play];
67 	if (Play == PLAYER)
68 		getmove();
69 	else
70 		calcmove();
71 	Next = FALSE;
72 	goodplay = TRUE;
73 	switch (Movetype) {
74 	  case M_DISCARD:
75 		if (haspicked(pp)) {
76 			if (pp->hand[Card_no] == C_INIT)
77 				if (Card_no == 6)
78 					Finished = TRUE;
79 				else
80 					error("no card there");
81 			else {
82 				if (is_safety(pp->hand[Card_no])) {
83 					error("discard a safety?");
84 					goodplay = FALSE;
85 					break;
86 				}
87 				Discard = pp->hand[Card_no];
88 				pp->hand[Card_no] = C_INIT;
89 				Next = TRUE;
90 				if (Play == PLAYER)
91 					account(Discard);
92 			}
93 		}
94 		else
95 			error("must pick first");
96 		break;
97 	  case M_PLAY:
98 		goodplay = playcard(pp);
99 		break;
100 	  case M_DRAW:
101 		Card_no = 0;
102 		if (Topcard <= Deck)
103 			error("no more cards");
104 		else if (haspicked(pp))
105 			error("already picked");
106 		else {
107 			pp->hand[0] = *--Topcard;
108 #ifdef DEBUG
109 			if (Debug)
110 				fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]);
111 #endif
112 acc:
113 			if (Play == COMP) {
114 				account(*Topcard);
115 				if (is_safety(*Topcard))
116 					pp->safety[*Topcard-S_CONV] = S_IN_HAND;
117 			}
118 			if (pp->hand[1] == C_INIT && Topcard > Deck) {
119 				Card_no = 1;
120 				pp->hand[1] = *--Topcard;
121 #ifdef DEBUG
122 				if (Debug)
123 					fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]);
124 #endif
125 				goto acc;
126 			}
127 			pp->new_battle = FALSE;
128 			pp->new_speed = FALSE;
129 		}
130 		break;
131 
132 	  case M_ORDER:
133 		break;
134 	}
135 	/*
136 	 * move blank card to top by one of two methods.  If the
137 	 * computer's hand was sorted, the randomness for picking
138 	 * between equally valued cards would be lost
139 	 */
140 	if (Order && Movetype != M_DRAW && goodplay && pp == &Player[PLAYER])
141 		sort(pp->hand);
142 	else
143 		for (i = 1; i < HAND_SZ; i++)
144 			if (pp->hand[i] == C_INIT) {
145 				for (j = 0; pp->hand[j] == C_INIT; j++)
146 					if (j >= HAND_SZ) {
147 						j = 0;
148 						break;
149 					}
150 				pp->hand[i] = pp->hand[j];
151 				pp->hand[j] = C_INIT;
152 			}
153 	if (Topcard <= Deck)
154 		check_go();
155 	if (Next)
156 		nextplay();
157 }
158 
159 /*
160  *	Check and see if either side can go.  If they cannot,
161  * the game is over
162  */
163 void
164 check_go()
165 {
166 	CARD	card;
167 	PLAY	*pp, *op;
168 	int	i;
169 
170 	for (pp = Player; pp < &Player[2]; pp++) {
171 		op = (pp == &Player[COMP] ? &Player[PLAYER] : &Player[COMP]);
172 		for (i = 0; i < HAND_SZ; i++) {
173 			card = pp->hand[i];
174 			if (is_safety(card) || canplay(pp, op, card)) {
175 #ifdef DEBUG
176 				if (Debug) {
177 					fprintf(outf, "CHECK_GO: can play %s (%d), ", C_name[card], card);
178 					fprintf(outf, "is_safety(card) = %d, ", is_safety(card));
179 					fprintf(outf, "canplay(pp, op, card) = %d\n", canplay(pp, op, card));
180 				}
181 #endif
182 				return;
183 			}
184 #ifdef DEBUG
185 			else if (Debug)
186 				fprintf(outf, "CHECK_GO: cannot play %s\n",
187 				    C_name[card]);
188 #endif
189 		}
190 	}
191 	Finished = TRUE;
192 }
193 
194 int
195 playcard(pp)
196 	PLAY	*pp;
197 {
198 	int	v;
199 	CARD	card;
200 
201 	/*
202 	 * check and see if player has picked
203 	 */
204 	switch (pp->hand[Card_no]) {
205 	  default:
206 		if (!haspicked(pp))
207 mustpick:
208 			return error("must pick first");
209 	  case C_GAS_SAFE:	case C_SPARE_SAFE:
210 	  case C_DRIVE_SAFE:	case C_RIGHT_WAY:
211 		break;
212 	}
213 
214 	card = pp->hand[Card_no];
215 #ifdef DEBUG
216 	if (Debug)
217 		fprintf(outf, "PLAYCARD: Card = %s\n", C_name[card]);
218 #endif
219 	Next = FALSE;
220 	switch (card) {
221 	  case C_200:
222 		if (pp->nummiles[C_200] == 2)
223 			return error("only two 200's per hand");
224 	  case C_100:	case C_75:
225 		if (pp->speed == C_LIMIT)
226 			return error("limit of 50");
227 	  case C_50:
228 		if (pp->mileage + Value[card] > End)
229 			return error("puts you over %d", End);
230 	  case C_25:
231 		if (!pp->can_go)
232 			return error("cannot move now");
233 		pp->nummiles[card]++;
234 		v = Value[card];
235 		pp->total += v;
236 		pp->hand_tot += v;
237 		if ((pp->mileage += v) == End)
238 			check_ext(FALSE);
239 		break;
240 
241 	  case C_GAS:	case C_SPARE:	case C_REPAIRS:
242 		if (pp->battle != opposite(card))
243 			return error("can't play \"%s\"", C_name[card]);
244 		pp->battle = card;
245 		if (pp->safety[S_RIGHT_WAY] == S_PLAYED)
246 			pp->can_go = TRUE;
247 		break;
248 
249 	  case C_GO:
250 		if (pp->battle != C_INIT && pp->battle != C_STOP
251 		    && !is_repair(pp->battle))
252 			return error("cannot play \"Go\" on a \"%s\"",
253 			    C_name[pp->battle]);
254 		pp->battle = C_GO;
255 		pp->can_go = TRUE;
256 		break;
257 
258 	  case C_END_LIMIT:
259 		if (pp->speed != C_LIMIT)
260 			return error("not limited");
261 		pp->speed = C_END_LIMIT;
262 		break;
263 
264 	  case C_EMPTY:	case C_FLAT:	case C_CRASH:
265 	  case C_STOP:
266 		pp = &Player[other(Play)];
267 		if (!pp->can_go)
268 			return error("opponent cannot go");
269 		else if (pp->safety[safety(card) - S_CONV] == S_PLAYED)
270 protected:
271 			return error("opponent is protected");
272 		pp->battle = card;
273 		pp->new_battle = TRUE;
274 		pp->can_go = FALSE;
275 		pp = &Player[Play];
276 		break;
277 
278 	  case C_LIMIT:
279 		pp = &Player[other(Play)];
280 		if (pp->speed == C_LIMIT)
281 			return error("opponent has limit");
282 		if (pp->safety[S_RIGHT_WAY] == S_PLAYED)
283 			goto protected;
284 		pp->speed = C_LIMIT;
285 		pp->new_speed = TRUE;
286 		pp = &Player[Play];
287 		break;
288 
289 	  case C_GAS_SAFE:	case C_SPARE_SAFE:
290 	  case C_DRIVE_SAFE:	case C_RIGHT_WAY:
291 		if (pp->battle == opposite(card)
292 		    || (card == C_RIGHT_WAY && pp->speed == C_LIMIT)) {
293 			if (!(card == C_RIGHT_WAY && !is_repair(pp->battle))) {
294 				pp->battle = C_GO;
295 				pp->can_go = TRUE;
296 			}
297 			if (card == C_RIGHT_WAY && pp->speed == C_LIMIT)
298 				pp->speed = C_INIT;
299 			if (pp->new_battle
300 			    || (pp->new_speed && card == C_RIGHT_WAY)) {
301 				pp->coups[card - S_CONV] = TRUE;
302 				pp->total += SC_COUP;
303 				pp->hand_tot += SC_COUP;
304 				pp->coupscore += SC_COUP;
305 			}
306 		}
307 		/*
308 		 * if not coup, must pick first
309 		 */
310 		else if (pp->hand[0] == C_INIT && Topcard > Deck)
311 			goto mustpick;
312 		pp->safety[card - S_CONV] = S_PLAYED;
313 		pp->total += SC_SAFETY;
314 		pp->hand_tot += SC_SAFETY;
315 		if ((pp->safescore += SC_SAFETY) == NUM_SAFE * SC_SAFETY) {
316 			pp->total += SC_ALL_SAFE;
317 			pp->hand_tot += SC_ALL_SAFE;
318 		}
319 		if (card == C_RIGHT_WAY) {
320 			if (pp->speed == C_LIMIT)
321 				pp->speed = C_INIT;
322 			if (pp->battle == C_STOP || pp->battle == C_INIT) {
323 				pp->can_go = TRUE;
324 				pp->battle = C_INIT;
325 			}
326 			if (!pp->can_go && is_repair(pp->battle))
327 				pp->can_go = TRUE;
328 		}
329 		Next = -1;
330 		break;
331 
332 	  case C_INIT:
333 		error("no card there");
334 		Next = -1;
335 		break;
336 	}
337 	if (pp == &Player[PLAYER])
338 		account(card);
339 	pp->hand[Card_no] = C_INIT;
340 	Next = (Next == (bool)-1 ? FALSE : TRUE);
341 	return TRUE;
342 }
343 
344 void
345 getmove()
346 {
347 	char	c;
348 #ifdef EXTRAP
349 	static bool	last_ex = FALSE;	/* set if last command was E */
350 
351 	if (last_ex) {
352 		undoex();
353 		prboard();
354 		last_ex = FALSE;
355 	}
356 #endif
357 	for (;;) {
358 		prompt(MOVEPROMPT);
359 		leaveok(Board, FALSE);
360 		refresh();
361 		while ((c = readch()) == killchar() || c == erasechar())
362 			continue;
363 		if (islower(c))
364 			c = toupper(c);
365 		if (isprint(c) && !isspace(c)) {
366 			addch(c);
367 			refresh();
368 		}
369 		switch (c) {
370 		  case 'P':		/* Pick */
371 			Movetype = M_DRAW;
372 			goto ret;
373 		  case 'U':		/* Use Card */
374 		  case 'D':		/* Discard Card */
375 			if ((Card_no = getcard()) < 0)
376 				break;
377 			Movetype = (c == 'U' ? M_PLAY : M_DISCARD);
378 			goto ret;
379 		  case 'O':		/* Order */
380 			Order = !Order;
381 			if (Window == W_SMALL) {
382 				if (!Order)
383 					mvwaddstr(Score, 12, 21,
384 						  "o: order hand");
385 				else
386 					mvwaddstr(Score, 12, 21,
387 						  "o: stop ordering");
388 				wclrtoeol(Score);
389 			}
390 			Movetype = M_ORDER;
391 			goto ret;
392 		  case 'Q':		/* Quit */
393 			rub(0);		/* Same as a rubout */
394 			break;
395 		  case 'W':		/* Window toggle */
396 			Window = nextwin(Window);
397 			newscore();
398 			prscore(TRUE);
399 			wrefresh(Score);
400 			break;
401 		  case 'R':		/* Redraw screen */
402 		  case CTRL('L'):
403 			wrefresh(curscr);
404 			break;
405 		  case 'S':		/* Save game */
406 			On_exit = FALSE;
407 			save();
408 			break;
409 		  case 'E':		/* Extrapolate */
410 #ifdef EXTRAP
411 			if (last_ex)
412 				break;
413 			Finished = TRUE;
414 			if (Window != W_FULL)
415 				newscore();
416 			prscore(FALSE);
417 			wrefresh(Score);
418 			last_ex = TRUE;
419 			Finished = FALSE;
420 #else
421 			error("%c: command not implemented", c);
422 #endif
423 			break;
424 		  case '\r':		/* Ignore RETURNs and	*/
425 		  case '\n':		/* Line Feeds		*/
426 		  case ' ':		/* Spaces		*/
427 		  case '\0':		/* and nulls		*/
428 			break;
429 #ifdef DEBUG
430 		  case 'Z':		/* Debug code */
431 			if (!Debug && outf == NULL) {
432 				char	buf[MAXPATHLEN];
433 
434 				prompt(FILEPROMPT);
435 				leaveok(Board, FALSE);
436 				refresh();
437 				sp = buf;
438 				while ((*sp = readch()) != '\n') {
439 					if (*sp == killchar())
440 						goto over;
441 					else if (*sp == erasechar()) {
442 						if (--sp < buf)
443 							sp = buf;
444 						else {
445 							addch('\b');
446 							if (*sp < ' ')
447 							    addch('\b');
448 							clrtoeol();
449 						}
450 					}
451 					else
452 						addstr(unctrl(*sp++));
453 					refresh();
454 				}
455 				*sp = '\0';
456 				leaveok(Board, TRUE);
457 				if ((outf = fopen(buf, "w")) == NULL)
458 					warn("%s", buf);
459 				setbuf(outf, (char *)NULL);
460 			}
461 			Debug = !Debug;
462 			break;
463 #endif
464 		  default:
465 			error("unknown command: %s", unctrl(c));
466 			break;
467 		}
468 	}
469 ret:
470 	leaveok(Board, TRUE);
471 }
472 
473 /*
474  * return whether or not the player has picked
475  */
476 int
477 haspicked(pp)
478 	const PLAY	*pp;
479 {
480 	int	card;
481 
482 	if (Topcard <= Deck)
483 		return TRUE;
484 	switch (pp->hand[Card_no]) {
485 	  case C_GAS_SAFE:	case C_SPARE_SAFE:
486 	  case C_DRIVE_SAFE:	case C_RIGHT_WAY:
487 		card = 1;
488 		break;
489 	  default:
490 		card = 0;
491 		break;
492 	}
493 	return (pp->hand[card] != C_INIT);
494 }
495 
496 void
497 account(card)
498 	CARD	card;
499 {
500 	CARD	oppos;
501 
502 	if (card == C_INIT)
503 		return;
504 	++Numseen[card];
505 	if (Play == COMP)
506 		switch (card) {
507 		  case C_GAS_SAFE:
508 		  case C_SPARE_SAFE:
509 		  case C_DRIVE_SAFE:
510 			oppos = opposite(card);
511 			Numgos += Numcards[oppos] - Numseen[oppos];
512 			break;
513 		  case C_CRASH:
514 		  case C_FLAT:
515 		  case C_EMPTY:
516 		  case C_STOP:
517 			Numgos++;
518 			break;
519 		}
520 }
521 
522 void
523 prompt(promptno)
524 	int	promptno;
525 {
526 	static const char	*const names[] = {
527 				">>:Move:",
528 				"Really?",
529 				"Another hand?",
530 				"Another game?",
531 				"Save game?",
532 				"Same file?",
533 				"file:",
534 				"Extension?",
535 				"Overwrite file?",
536 			};
537 	static int	last_prompt = -1;
538 
539 	if (promptno == last_prompt)
540 		move(MOVE_Y, MOVE_X + strlen(names[promptno]) + 1);
541 	else {
542 		move(MOVE_Y, MOVE_X);
543 		if (promptno == MOVEPROMPT)
544 			standout();
545 		addstr(names[promptno]);
546 		if (promptno == MOVEPROMPT)
547 			standend();
548 		addch(' ');
549 		last_prompt = promptno;
550 	}
551 	clrtoeol();
552 }
553 
554 void
555 sort(hand)
556 	CARD	*hand;
557 {
558 	CARD	*cp, *tp;
559 	CARD	temp;
560 
561 	cp = hand;
562 	hand += HAND_SZ;
563 	for ( ; cp < &hand[-1]; cp++)
564 		for (tp = cp + 1; tp < hand; tp++)
565 			if (*cp > *tp) {
566 				temp = *cp;
567 				*cp = *tp;
568 				*tp = temp;
569 			}
570 }
571