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