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