comp.c revision 1.10
1/*	$NetBSD: comp.c,v 1.10 2006/03/19 00:29:27 christos Exp $	*/
2
3/*
4 * Copyright (c) 1982, 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. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)comp.c	8.1 (Berkeley) 5/31/93";
36#else
37__RCSID("$NetBSD: comp.c,v 1.10 2006/03/19 00:29:27 christos Exp $");
38#endif
39#endif /* not lint */
40
41# include	"mille.h"
42
43/*
44 * @(#)comp.c	1.1 (Berkeley) 4/1/82
45 */
46
47# define	V_VALUABLE	40
48
49void
50calcmove()
51{
52	CARD		card;
53	int		*value;
54	PLAY		*pp, *op;
55	bool		foundend, cango, canstop, foundlow;
56	unsgn int	i, count200, badcount, nummin, nummax, diff;
57	int		curmin, curmax;
58	CARD		safe, oppos;
59	int		valbuf[HAND_SZ], count[NUM_CARDS];
60	bool		playit[HAND_SZ];
61
62	wmove(Score, ERR_Y, ERR_X);	/* get rid of error messages	*/
63	wclrtoeol(Score);
64	pp = &Player[COMP];
65	op = &Player[PLAYER];
66	safe = 0;
67	cango = 0;
68	canstop = FALSE;
69	foundend = FALSE;
70
71	/* Try for a Coup Forre, and see what we have. */
72	for (i = 0; i < NUM_CARDS; i++)
73		count[i] = 0;
74	for (i = 0; i < HAND_SZ; i++) {
75		card = pp->hand[i];
76		switch (card) {
77		  case C_STOP:	case C_CRASH:
78		  case C_FLAT:	case C_EMPTY:
79			if ((playit[i] = canplay(pp, op, card)) != 0)
80				canstop = TRUE;
81			goto norm;
82		  case C_LIMIT:
83			if ((playit[i] = canplay(pp, op, card))
84			    && Numseen[C_25] == Numcards[C_25]
85			    && Numseen[C_50] == Numcards[C_50])
86				canstop = TRUE;
87			goto norm;
88		  case C_25:	case C_50:	case C_75:
89		  case C_100:	case C_200:
90			if ((playit[i] = canplay(pp, op, card))
91			    && pp->mileage + Value[card] == End)
92				foundend = TRUE;
93			goto norm;
94		  default:
95			playit[i] = canplay(pp, op, card);
96norm:
97			if (playit[i])
98				++cango;
99			break;
100		  case C_GAS_SAFE:	case C_DRIVE_SAFE:
101		  case C_SPARE_SAFE:	case C_RIGHT_WAY:
102			if (pp->battle == opposite(card) ||
103			    (pp->speed == C_LIMIT && card == C_RIGHT_WAY)) {
104				Movetype = M_PLAY;
105				Card_no = i;
106				return;
107			}
108			++safe;
109			playit[i] = TRUE;
110			break;
111		}
112		if (card >= 0)
113			++count[card];
114	}
115
116	/* No Coup Forre.  Draw to fill hand, then restart, as needed. */
117	if (pp->hand[0] == C_INIT && Topcard > Deck) {
118		Movetype = M_DRAW;
119		return;
120	}
121
122#ifdef DEBUG
123	if (Debug)
124		fprintf(outf, "CALCMOVE: cango = %d, canstop = %d, safe = %d\n",
125			cango, canstop, safe);
126#endif
127	if (foundend)
128		foundend = !check_ext(TRUE);
129	for (i = 0; safe && i < HAND_SZ; i++) {
130		if (is_safety(pp->hand[i])) {
131			if (onecard(op) || (foundend && cango && !canstop)) {
132#ifdef DEBUG
133				if (Debug)
134					fprintf(outf,
135						"CALCMOVE: onecard(op) = %d, foundend = %d\n",
136						onecard(op), foundend);
137#endif
138playsafe:
139				Movetype = M_PLAY;
140				Card_no = i;
141				return;
142			}
143			oppos = opposite(pp->hand[i]);
144			if (Numseen[oppos] == Numcards[oppos] &&
145			    !(pp->hand[i] == C_RIGHT_WAY &&
146			      Numseen[C_LIMIT] != Numcards[C_LIMIT]))
147				goto playsafe;
148			else if (!cango
149			    && (op->can_go || !pp->can_go || Topcard < Deck)) {
150				card = (Topcard - Deck) - roll(1, 10);
151				if ((!pp->mileage) != (!op->mileage))
152					card -= 7;
153#ifdef DEBUG
154				if (Debug)
155					fprintf(outf,
156						"CALCMOVE: card = %d, DECK_SZ / 4 = %d\n",
157						card, DECK_SZ / 4);
158#endif
159				if (card < DECK_SZ / 4)
160					goto playsafe;
161			}
162			safe--;
163			playit[i] = cango;
164		}
165	}
166	if (!pp->can_go && !is_repair(pp->battle))
167		Numneed[opposite(pp->battle)]++;
168redoit:
169	foundlow = (cango || count[C_END_LIMIT] != 0
170			  || Numseen[C_LIMIT] == Numcards[C_LIMIT]
171			  || pp->safety[S_RIGHT_WAY] != S_UNKNOWN);
172	foundend = FALSE;
173	count200 = pp->nummiles[C_200];
174	badcount = 0;
175	curmax = -1;
176	curmin = 101;
177	nummin = -1;
178	nummax = -1;
179	value = valbuf;
180	for (i = 0; i < HAND_SZ; i++) {
181		card = pp->hand[i];
182		if (is_safety(card) || playit[i] == (cango != 0)) {
183#ifdef DEBUG
184			if (Debug)
185				fprintf(outf, "CALCMOVE: switch(\"%s\")\n",
186					C_name[card]);
187#endif
188			switch (card) {
189			  case C_25:	case C_50:
190				diff = End - pp->mileage;
191				/* avoid getting too close */
192				if (Topcard > Deck && cango && diff <= 100
193				    && (int)diff / Value[card] > count[card]
194				    && (card == C_25 || diff % 50 == 0)) {
195					if (card == C_50 && diff - 50 == 25
196					    && count[C_25] > 0)
197						goto okay;
198					*value = 0;
199					if (--cango <= 0)
200						goto redoit;
201					break;
202				}
203okay:
204				*value = (Value[card] >> 3);
205				if (pp->speed == C_LIMIT)
206					++*value;
207				else
208					--*value;
209				if (!foundlow
210				   && (card == C_50 || count[C_50] == 0)) {
211					*value = (pp->mileage ? 10 : 20);
212					foundlow = TRUE;
213				}
214				goto miles;
215			  case C_200:
216				if (++count200 > 2) {
217					*value = 0;
218					break;
219				}
220			  case C_75:	case C_100:
221				*value = (Value[card] >> 3);
222				if (pp->speed == C_LIMIT)
223					--*value;
224				else
225					++*value;
226miles:
227				if (pp->mileage + Value[card] > End)
228					*value = (End == 700 ? card : 0);
229				else if (pp->mileage + Value[card] == End) {
230					*value = (foundend ? card : V_VALUABLE);
231					foundend = TRUE;
232				}
233				break;
234			  case C_END_LIMIT:
235				if (pp->safety[S_RIGHT_WAY] != S_UNKNOWN)
236					*value = (pp->safety[S_RIGHT_WAY] ==
237						  S_PLAYED ? -1 : 1);
238				else if (pp->speed == C_LIMIT &&
239					 End - pp->mileage <= 50)
240					*value = 1;
241				else if (pp->speed == C_LIMIT
242				    || Numseen[C_LIMIT] != Numcards[C_LIMIT]) {
243					safe = S_RIGHT_WAY;
244					oppos = C_LIMIT;
245					goto repair;
246				}
247				else {
248					*value = 0;
249					--count[C_END_LIMIT];
250				}
251				break;
252			  case C_REPAIRS:	case C_SPARE:	case C_GAS:
253				safe = safety(card) - S_CONV;
254				oppos = opposite(card);
255				if (pp->safety[safe] != S_UNKNOWN)
256					*value = (pp->safety[safe] ==
257						  S_PLAYED ? -1 : 1);
258				else if (pp->battle != oppos
259				    && (Numseen[oppos] == Numcards[oppos] ||
260					Numseen[oppos] + count[card] >
261					Numcards[oppos])) {
262					*value = 0;
263					--count[card];
264				}
265				else {
266repair:
267					*value = Numcards[oppos] * 6;
268					*value += Numseen[card] -
269						  Numseen[oppos];
270					if (!cango)
271					    *value /= (count[card]*count[card]);
272					count[card]--;
273				}
274				break;
275			  case C_GO:
276				if (pp->safety[S_RIGHT_WAY] != S_UNKNOWN)
277					*value = (pp->safety[S_RIGHT_WAY] ==
278						  S_PLAYED ? -1 : 2);
279				else if (pp->can_go
280				 && Numgos + count[C_GO] == Numneed[C_GO]) {
281					*value = 0;
282					--count[C_GO];
283				}
284				else {
285					*value = Numneed[C_GO] * 3;
286					*value += (Numseen[C_GO] - Numgos);
287					*value /= (count[C_GO] * count[C_GO]);
288					count[C_GO]--;
289				}
290				break;
291			  case C_LIMIT:
292				if (op->mileage + 50 >= End) {
293					*value = (End == 700 && !cango);
294					break;
295				}
296				if (canstop || (cango && !op->can_go))
297					*value = 1;
298				else {
299					*value = (pp->safety[S_RIGHT_WAY] !=
300						  S_UNKNOWN ? 2 : 3);
301					safe = S_RIGHT_WAY;
302					oppos = C_END_LIMIT;
303					goto normbad;
304				}
305				break;
306			  case C_CRASH:	case C_EMPTY:	case C_FLAT:
307				safe = safety(card) - S_CONV;
308				oppos = opposite(card);
309				*value = (pp->safety[safe]!=S_UNKNOWN ? 3 : 4);
310normbad:
311				if (op->safety[safe] == S_PLAYED)
312					*value = -1;
313				else {
314					*value *= Numneed[oppos] +
315						  Numseen[oppos] + 2;
316					if (!pp->mileage || foundend ||
317					    onecard(op))
318						*value += 5;
319					if (op->mileage == 0 || onecard(op))
320						*value += 5;
321					if (op->speed == C_LIMIT)
322						*value -= 3;
323					if (cango &&
324					    pp->safety[safe] != S_UNKNOWN)
325						*value += 3;
326					if (!cango)
327						*value /= ++badcount;
328				}
329				break;
330			  case C_STOP:
331				if (op->safety[S_RIGHT_WAY] == S_PLAYED)
332					*value = -1;
333				else {
334					*value = (pp->safety[S_RIGHT_WAY] !=
335						  S_UNKNOWN ? 3 : 4);
336					*value *= Numcards[C_STOP] +
337						  Numseen[C_GO];
338					if (!pp->mileage || foundend ||
339					    onecard(op))
340						*value += 5;
341					if (!cango)
342						*value /= ++badcount;
343					if (op->mileage == 0)
344						*value += 5;
345					if (op->speed == C_LIMIT || !op->can_go)
346						*value -= 5;
347					if (cango && pp->safety[S_RIGHT_WAY] !=
348						     S_UNKNOWN)
349						*value += 5;
350				}
351				break;
352			  case C_GAS_SAFE:	case C_DRIVE_SAFE:
353			  case C_SPARE_SAFE:	case C_RIGHT_WAY:
354				*value = cango ? 0 : 101;
355				break;
356			  case C_INIT:
357				*value = 0;
358				break;
359			}
360		}
361		else
362			*value = cango ? 0 : 101;
363		if (card != C_INIT) {
364			if (*value >= curmax) {
365				nummax = i;
366				curmax = *value;
367			}
368			if (*value <= curmin) {
369				nummin = i;
370				curmin = *value;
371			}
372		}
373#ifdef DEBUG
374		if (Debug)
375			mvprintw(i + 6, 2, "%3d %-14s", *value,
376				 C_name[pp->hand[i]]);
377#endif
378		value++;
379	}
380	if (!pp->can_go && !is_repair(pp->battle))
381		Numneed[opposite(pp->battle)]++;
382	if (cango) {
383play_it:
384		mvaddstr(MOVE_Y + 1, MOVE_X, "PLAY\n");
385		Movetype = M_PLAY;
386		Card_no = nummax;
387	}
388	else {
389		if (is_safety(pp->hand[nummin])) { /* NEVER discard a safety */
390			nummax = nummin;
391			goto play_it;
392		}
393		mvaddstr(MOVE_Y + 1, MOVE_X, "DISCARD\n");
394		Movetype = M_DISCARD;
395		Card_no = nummin;
396	}
397	mvprintw(MOVE_Y + 2, MOVE_X, "%16s", C_name[pp->hand[Card_no]]);
398}
399
400/*
401 * Return true if the given player could conceivably win with his next card.
402 */
403int
404onecard(pp)
405	const PLAY	*pp;
406{
407	CARD	bat, spd, card;
408
409	bat = pp->battle;
410	spd = pp->speed;
411	card = -1;
412	if (pp->can_go || ((is_repair(bat) || bat == C_STOP || spd == C_LIMIT) &&
413			   Numseen[S_RIGHT_WAY] != 0) ||
414	    (bat >= 0 && Numseen[safety(bat)] != 0))
415		switch (End - pp->mileage) {
416		  case 200:
417			if (pp->nummiles[C_200] == 2)
418				return FALSE;
419			card = C_200;
420			/* FALLTHROUGH */
421		  case 100:
422		  case 75:
423			if (card == -1)
424				card = (End - pp->mileage == 75 ? C_75 : C_100);
425			if (spd == C_LIMIT)
426				return Numseen[S_RIGHT_WAY] == 0;
427		  case 50:
428		  case 25:
429			if (card == -1)
430				card = (End - pp->mileage == 25 ? C_25 : C_50);
431			return Numseen[card] != Numcards[card];
432		}
433	return FALSE;
434}
435
436int
437canplay(pp, op, card)
438	const PLAY	*pp, *op;
439	CARD	card;
440{
441	switch (card) {
442	  case C_200:
443		if (pp->nummiles[C_200] == 2)
444			break;
445		/* FALLTHROUGH */
446	  case C_75:	case C_100:
447		if (pp->speed == C_LIMIT)
448			break;
449		/* FALLTHROUGH */
450	  case C_50:
451		if (pp->mileage + Value[card] > End)
452			break;
453		/* FALLTHROUGH */
454	  case C_25:
455		if (pp->can_go)
456			return TRUE;
457		break;
458	  case C_EMPTY:	case C_FLAT:	case C_CRASH:
459	  case C_STOP:
460		if (op->can_go && op->safety[safety(card) - S_CONV] != S_PLAYED)
461			return TRUE;
462		break;
463	  case C_LIMIT:
464		if (op->speed != C_LIMIT &&
465		    op->safety[S_RIGHT_WAY] != S_PLAYED &&
466		    op->mileage + 50 < End)
467			return TRUE;
468		break;
469	  case C_GAS:	case C_SPARE:	case C_REPAIRS:
470		if (pp->battle == opposite(card))
471			return TRUE;
472		break;
473	  case C_GO:
474		if (!pp->can_go &&
475		    (is_repair(pp->battle) || pp->battle == C_STOP))
476			return TRUE;
477		break;
478	  case C_END_LIMIT:
479		if (pp->speed == C_LIMIT)
480			return TRUE;
481	}
482	return FALSE;
483}
484