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