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