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