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