xref: /netbsd-src/games/mille/move.c (revision 32d1c65c71fbdb65a012e8392a62a757dd6853e9)
1 /*	$NetBSD: move.c,v 1.19 2019/02/03 03:19:25 mrg 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.19 2019/02/03 03:19:25 mrg 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 		/* FALLTHROUGH */
236 	  case C_100:	case C_75:
237 		if (pp->speed == C_LIMIT)
238 			return error("limit of 50");
239 		/* FALLTHROUGH */
240 	  case C_50:
241 		if (pp->mileage + Value[card] > End)
242 			return error("puts you over %d", End);
243 		/* FALLTHROUGH */
244 	  case C_25:
245 		if (!pp->can_go)
246 			return error("cannot move now");
247 		pp->nummiles[card]++;
248 		v = Value[card];
249 		pp->total += v;
250 		pp->hand_tot += v;
251 		if ((pp->mileage += v) == End)
252 			check_ext(FALSE);
253 		break;
254 
255 	  case C_GAS:	case C_SPARE:	case C_REPAIRS:
256 		if (pp->battle != opposite(card))
257 			return error("can't play \"%s\"", C_name[card]);
258 		pp->battle = card;
259 		if (pp->safety[S_RIGHT_WAY] == S_PLAYED)
260 			pp->can_go = TRUE;
261 		break;
262 
263 	  case C_GO:
264 		if (pp->battle != C_INIT && pp->battle != C_STOP
265 		    && !is_repair(pp->battle))
266 			return error("cannot play \"Go\" on a \"%s\"",
267 			    C_name[pp->battle]);
268 		pp->battle = C_GO;
269 		pp->can_go = TRUE;
270 		break;
271 
272 	  case C_END_LIMIT:
273 		if (pp->speed != C_LIMIT)
274 			return error("not limited");
275 		pp->speed = C_END_LIMIT;
276 		break;
277 
278 	  case C_EMPTY:	case C_FLAT:	case C_CRASH:
279 	  case C_STOP:
280 		pp = &Player[other(Play)];
281 		if (!pp->can_go)
282 			return error("opponent cannot go");
283 		else if (pp->safety[safety(card) - S_CONV] == S_PLAYED)
284 protected:
285 			return error("opponent is protected");
286 		pp->battle = card;
287 		pp->new_battle = TRUE;
288 		pp->can_go = FALSE;
289 		pp = &Player[Play];
290 		break;
291 
292 	  case C_LIMIT:
293 		pp = &Player[other(Play)];
294 		if (pp->speed == C_LIMIT)
295 			return error("opponent has limit");
296 		if (pp->safety[S_RIGHT_WAY] == S_PLAYED)
297 			goto protected;
298 		pp->speed = C_LIMIT;
299 		pp->new_speed = TRUE;
300 		pp = &Player[Play];
301 		break;
302 
303 	  case C_GAS_SAFE:	case C_SPARE_SAFE:
304 	  case C_DRIVE_SAFE:	case C_RIGHT_WAY:
305 		if (pp->battle == opposite(card)
306 		    || (card == C_RIGHT_WAY && pp->speed == C_LIMIT)) {
307 			if (!(card == C_RIGHT_WAY && !is_repair(pp->battle))) {
308 				pp->battle = C_GO;
309 				pp->can_go = TRUE;
310 			}
311 			if (card == C_RIGHT_WAY && pp->speed == C_LIMIT)
312 				pp->speed = C_INIT;
313 			if (pp->new_battle
314 			    || (pp->new_speed && card == C_RIGHT_WAY)) {
315 				pp->coups[card - S_CONV] = TRUE;
316 				pp->total += SC_COUP;
317 				pp->hand_tot += SC_COUP;
318 				pp->coupscore += SC_COUP;
319 			}
320 		}
321 		/*
322 		 * if not coup, must pick first
323 		 */
324 		else if (pp->hand[0] == C_INIT && Topcard > Deck)
325 			goto mustpick;
326 		pp->safety[card - S_CONV] = S_PLAYED;
327 		pp->total += SC_SAFETY;
328 		pp->hand_tot += SC_SAFETY;
329 		if ((pp->safescore += SC_SAFETY) == NUM_SAFE * SC_SAFETY) {
330 			pp->total += SC_ALL_SAFE;
331 			pp->hand_tot += SC_ALL_SAFE;
332 		}
333 		if (card == C_RIGHT_WAY) {
334 			if (pp->speed == C_LIMIT)
335 				pp->speed = C_INIT;
336 			if (pp->battle == C_STOP || pp->battle == C_INIT) {
337 				pp->can_go = TRUE;
338 				pp->battle = C_INIT;
339 			}
340 			if (!pp->can_go && is_repair(pp->battle))
341 				pp->can_go = TRUE;
342 		}
343 		Next = -1;
344 		break;
345 
346 	  case C_INIT:
347 		error("no card there");
348 		Next = -1;
349 		break;
350 	}
351 	if (pp == &Player[PLAYER])
352 		account(card);
353 	pp->hand[Card_no] = C_INIT;
354 	Next = (Next == (bool)-1 ? FALSE : TRUE);
355 	return TRUE;
356 }
357 
358 static void
359 getmove(void)
360 {
361 	char	c;
362 #ifdef EXTRAP
363 	static bool	last_ex = FALSE;	/* set if last command was E */
364 
365 	if (last_ex) {
366 		undoex();
367 		prboard();
368 		last_ex = FALSE;
369 	}
370 #endif
371 	for (;;) {
372 		prompt(MOVEPROMPT);
373 		leaveok(Board, FALSE);
374 		refresh();
375 		while ((c = readch()) == killchar() || c == erasechar())
376 			continue;
377 		if (islower((unsigned char)c))
378 			c = toupper((unsigned char)c);
379 		if (isprint((unsigned char)c) && !isspace((unsigned char)c)) {
380 			addch(c);
381 			refresh();
382 		}
383 		switch (c) {
384 		  case 'P':		/* Pick */
385 			Movetype = M_DRAW;
386 			goto ret;
387 		  case 'U':		/* Use Card */
388 		  case 'D':		/* Discard Card */
389 			if ((Card_no = getcard()) < 0)
390 				break;
391 			Movetype = (c == 'U' ? M_PLAY : M_DISCARD);
392 			goto ret;
393 		  case 'O':		/* Order */
394 			Order = !Order;
395 			if (Window == W_SMALL) {
396 				if (!Order)
397 					mvwaddstr(Score, 12, 21,
398 						  "o: order hand");
399 				else
400 					mvwaddstr(Score, 12, 21,
401 						  "o: stop ordering");
402 				wclrtoeol(Score);
403 			}
404 			Movetype = M_ORDER;
405 			goto ret;
406 		  case 'Q':		/* Quit */
407 			rub(0);		/* Same as a rubout */
408 			break;
409 		  case 'W':		/* Window toggle */
410 			Window = nextwin(Window);
411 			newscore();
412 			prscore(TRUE);
413 			wrefresh(Score);
414 			break;
415 		  case 'R':		/* Redraw screen */
416 		  case CTRL('L'):
417 			wrefresh(curscr);
418 			break;
419 		  case 'S':		/* Save game */
420 			On_exit = FALSE;
421 			save();
422 			break;
423 		  case 'E':		/* Extrapolate */
424 #ifdef EXTRAP
425 			if (last_ex)
426 				break;
427 			Finished = TRUE;
428 			if (Window != W_FULL)
429 				newscore();
430 			prscore(FALSE);
431 			wrefresh(Score);
432 			last_ex = TRUE;
433 			Finished = FALSE;
434 #else
435 			error("%c: command not implemented", c);
436 #endif
437 			break;
438 		  case '\r':		/* Ignore RETURNs and	*/
439 		  case '\n':		/* Line Feeds		*/
440 		  case ' ':		/* Spaces		*/
441 		  case '\0':		/* and nulls		*/
442 			break;
443 #ifdef DEBUG
444 		  case 'Z':		/* Debug code */
445 			if (!Debug && outf == NULL) {
446 				char	buf[MAXPATHLEN];
447 				char	*sp;
448 
449 				prompt(FILEPROMPT);
450 				leaveok(Board, FALSE);
451 				refresh();
452 over:
453 				sp = buf;
454 				while ((*sp = readch()) != '\n') {
455 					if (*sp == killchar())
456 						goto over;
457 					else if (*sp == erasechar()) {
458 						if (--sp < buf)
459 							sp = buf;
460 						else {
461 							addch('\b');
462 							if (*sp < ' ')
463 							    addch('\b');
464 							clrtoeol();
465 						}
466 					}
467 					else
468 						addstr(unctrl(*sp++));
469 					refresh();
470 				}
471 				*sp = '\0';
472 				leaveok(Board, TRUE);
473 				if ((outf = fopen(buf, "w")) == NULL)
474 					warn("%s", buf);
475 				setbuf(outf, NULL);
476 			}
477 			Debug = !Debug;
478 			break;
479 #endif
480 		  default:
481 			error("unknown command: %s", unctrl(c));
482 			break;
483 		}
484 	}
485 ret:
486 	leaveok(Board, TRUE);
487 }
488 
489 /*
490  * return whether or not the player has picked
491  */
492 static int
493 haspicked(const PLAY *pp)
494 {
495 	int	card;
496 
497 	if (Topcard <= Deck)
498 		return TRUE;
499 	switch (pp->hand[Card_no]) {
500 	  case C_GAS_SAFE:	case C_SPARE_SAFE:
501 	  case C_DRIVE_SAFE:	case C_RIGHT_WAY:
502 		card = 1;
503 		break;
504 	  default:
505 		card = 0;
506 		break;
507 	}
508 	return (pp->hand[card] != C_INIT);
509 }
510 
511 void
512 account(CARD card)
513 {
514 	CARD	oppos;
515 
516 	if (card == C_INIT)
517 		return;
518 	++Numseen[card];
519 	if (Play == COMP)
520 		switch (card) {
521 		  case C_GAS_SAFE:
522 		  case C_SPARE_SAFE:
523 		  case C_DRIVE_SAFE:
524 			oppos = opposite(card);
525 			Numgos += Numcards[oppos] - Numseen[oppos];
526 			break;
527 		  case C_CRASH:
528 		  case C_FLAT:
529 		  case C_EMPTY:
530 		  case C_STOP:
531 			Numgos++;
532 			break;
533 		}
534 }
535 
536 void
537 prompt(int promptno)
538 {
539 	static const char	*const names[] = {
540 				">>:Move:",
541 				"Really?",
542 				"Another hand?",
543 				"Another game?",
544 				"Save game?",
545 				"Same file?",
546 				"file:",
547 				"Extension?",
548 				"Overwrite file?",
549 			};
550 	static int	last_prompt = -1;
551 
552 	if (promptno == last_prompt)
553 		move(MOVE_Y, MOVE_X + strlen(names[promptno]) + 1);
554 	else {
555 		move(MOVE_Y, MOVE_X);
556 		if (promptno == MOVEPROMPT)
557 			standout();
558 		addstr(names[promptno]);
559 		if (promptno == MOVEPROMPT)
560 			standend();
561 		addch(' ');
562 		last_prompt = promptno;
563 	}
564 	clrtoeol();
565 }
566 
567 void
568 sort(CARD *hand)
569 {
570 	CARD	*cp, *tp;
571 	CARD	temp;
572 
573 	cp = hand;
574 	hand += HAND_SZ;
575 	for ( ; cp < &hand[-1]; cp++)
576 		for (tp = cp + 1; tp < hand; tp++)
577 			if (*cp > *tp) {
578 				temp = *cp;
579 				*cp = *tp;
580 				*tp = temp;
581 			}
582 }
583