move.c revision 1.4
1/*	$OpenBSD: move.c,v 1.4 1998/09/22 04:08:24 pjanzen Exp $	*/
2/*	$NetBSD: move.c,v 1.4 1995/03/24 05:01:57 cgd Exp $	*/
3
4/*
5 * Copyright (c) 1983, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38#if 0
39static char sccsid[] = "@(#)move.c	8.1 (Berkeley) 5/31/93";
40#else
41static char rcsid[] = "$OpenBSD: move.c,v 1.4 1998/09/22 04:08:24 pjanzen Exp $";
42#endif
43#endif /* not lint */
44
45#ifdef DEBUG
46#include <sys/param.h>
47#endif
48
49#include <termios.h>
50#include	"mille.h"
51
52# ifdef	attron
53#	include	<term.h>
54#	define	_tty	cur_term->Nttyb
55# endif	attron
56
57/*
58 * @(#)move.c	1.2 (Berkeley) 3/28/83
59 */
60
61#undef	CTRL
62#define	CTRL(c)		(c - 'A' + 1)
63
64char	*Movenames[] = {
65		"M_DISCARD", "M_DRAW", "M_PLAY", "M_ORDER"
66	};
67
68void
69domove()
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
121acc:
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 */
172void
173check_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
203int
204playcard(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))
216mustpick:
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)
279protected:
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 == (bool)-1 ? FALSE : TRUE);
350	return TRUE;
351}
352
353void
354getmove()
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];
445over:
446				prompt(FILEPROMPT);
447				leaveok(Board, FALSE);
448				refresh();
449				sp = buf;
450				while ((*sp = readch()) != '\n' && *sp != '\r'
451				    && (sp - buf < sizeof(buf))) {
452					if (*sp == killchar())
453						goto over;
454					else if (*sp == erasechar()) {
455						if (--sp < buf)
456							sp = buf;
457						else {
458							addch('\b');
459							if (*sp < ' ')
460							    addch('\b');
461							clrtoeol();
462						}
463					}
464					else
465						addstr(unctrl(*sp++));
466					refresh();
467				}
468				*sp = '\0';
469				leaveok(Board, TRUE);
470				if ((outf = fopen(buf, "w")) == NULL)
471					warn("%s", buf);
472				setbuf(outf, (char *)NULL);
473			}
474			Debug = !Debug;
475			break;
476#endif
477		  default:
478			error("unknown command: %s", unctrl(c));
479			break;
480		}
481	}
482ret:
483	leaveok(Board, TRUE);
484}
485
486/*
487 * return whether or not the player has picked
488 */
489int
490haspicked(pp)
491	PLAY	*pp;
492{
493	int	card;
494
495	if (Topcard <= Deck)
496		return TRUE;
497	switch (pp->hand[Card_no]) {
498	  case C_GAS_SAFE:	case C_SPARE_SAFE:
499	  case C_DRIVE_SAFE:	case C_RIGHT_WAY:
500		card = 1;
501		break;
502	  default:
503		card = 0;
504		break;
505	}
506	return (pp->hand[card] != C_INIT);
507}
508
509void
510account(card)
511	CARD	card;
512{
513	CARD	oppos;
514
515	if (card == C_INIT)
516		return;
517	++Numseen[card];
518	if (Play == COMP)
519		switch (card) {
520		  case C_GAS_SAFE:
521		  case C_SPARE_SAFE:
522		  case C_DRIVE_SAFE:
523			oppos = opposite(card);
524			Numgos += Numcards[oppos] - Numseen[oppos];
525			break;
526		  case C_CRASH:
527		  case C_FLAT:
528		  case C_EMPTY:
529		  case C_STOP:
530			Numgos++;
531			break;
532		}
533}
534
535void
536prompt(promptno)
537	int	promptno;
538{
539	static char	*names[] = {
540				">>:Move:",
541				"Really?",
542				"Another hand?",
543				"Another game?",
544				"Save game?",
545				"Same file?",
546				"file:",
547				"Extension?",
548				"Overwrite file?",
549			};
550	static int	last_prompt = -1;
551
552	if (promptno == last_prompt)
553		move(MOVE_Y, MOVE_X + strlen(names[promptno]) + 1);
554	else {
555		move(MOVE_Y, MOVE_X);
556		if (promptno == MOVEPROMPT)
557			standout();
558		addstr(names[promptno]);
559		if (promptno == MOVEPROMPT)
560			standend();
561		addch(' ');
562		last_prompt = promptno;
563	}
564	clrtoeol();
565}
566
567void
568sort(hand)
569	CARD	*hand;
570{
571	CARD	*cp, *tp;
572	CARD	temp;
573
574	cp = hand;
575	hand += HAND_SZ;
576	for ( ; cp < &hand[-1]; cp++)
577		for (tp = cp + 1; tp < hand; tp++)
578			if (*cp > *tp) {
579				temp = *cp;
580				*cp = *tp;
581				*tp = temp;
582			}
583}
584