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