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