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