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