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