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