comp.c revision 1.14
1/*	$NetBSD: comp.c,v 1.14 2019/02/04 03:29:41 mrg 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.14 2019/02/04 03:29:41 mrg 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(void)
51{
52	CARD		card;
53	int		*value;
54	PLAY		*pp, *op;
55	bool		foundend, canstop, foundlow;
56	int		cango;
57	unsigned int	i, count200, badcount, nummin, nummax, diff;
58	int		curmin, curmax;
59	CARD		safe, oppos;
60	int		valbuf[HAND_SZ], count[NUM_CARDS];
61	bool		playit[HAND_SZ];
62
63	wmove(Score, ERR_Y, ERR_X);	/* get rid of error messages	*/
64	wclrtoeol(Score);
65	pp = &Player[COMP];
66	op = &Player[PLAYER];
67	safe = 0;
68	cango = 0;
69	canstop = FALSE;
70	foundend = FALSE;
71
72	/* Try for a Coup Forre, and see what we have. */
73	for (i = 0; i < NUM_CARDS; i++)
74		count[i] = 0;
75	for (i = 0; i < HAND_SZ; i++) {
76		card = pp->hand[i];
77		switch (card) {
78		  case C_STOP:	case C_CRASH:
79		  case C_FLAT:	case C_EMPTY:
80			if ((playit[i] = canplay(pp, op, card)) != 0)
81				canstop = TRUE;
82			goto norm;
83		  case C_LIMIT:
84			if ((playit[i] = canplay(pp, op, card))
85			    && Numseen[C_25] == Numcards[C_25]
86			    && Numseen[C_50] == Numcards[C_50])
87				canstop = TRUE;
88			goto norm;
89		  case C_25:	case C_50:	case C_75:
90		  case C_100:	case C_200:
91			if ((playit[i] = canplay(pp, op, card))
92			    && pp->mileage + Value[card] == End)
93				foundend = TRUE;
94			goto norm;
95		  default:
96			playit[i] = canplay(pp, op, card);
97norm:
98			if (playit[i])
99				++cango;
100			break;
101		  case C_GAS_SAFE:	case C_DRIVE_SAFE:
102		  case C_SPARE_SAFE:	case C_RIGHT_WAY:
103			if (pp->battle == opposite(card) ||
104			    (pp->speed == C_LIMIT && card == C_RIGHT_WAY)) {
105				Movetype = M_PLAY;
106				Card_no = i;
107				return;
108			}
109			++safe;
110			playit[i] = TRUE;
111			break;
112		}
113		if (card >= 0)
114			++count[card];
115	}
116
117	/* No Coup Forre.  Draw to fill hand, then restart, as needed. */
118	if (pp->hand[0] == C_INIT && Topcard > Deck) {
119		Movetype = M_DRAW;
120		return;
121	}
122
123#ifdef DEBUG
124	if (Debug)
125		fprintf(outf, "CALCMOVE: cango = %d, canstop = %d, safe = %d\n",
126			cango, canstop, safe);
127#endif
128	if (foundend)
129		foundend = !check_ext(TRUE);
130	for (i = 0; safe && i < HAND_SZ; i++) {
131		if (is_safety(pp->hand[i])) {
132			if (onecard(op) || (foundend && cango && !canstop)) {
133#ifdef DEBUG
134				if (Debug)
135					fprintf(outf,
136						"CALCMOVE: onecard(op) = %d, foundend = %d\n",
137						onecard(op), foundend);
138#endif
139playsafe:
140				Movetype = M_PLAY;
141				Card_no = i;
142				return;
143			}
144			oppos = opposite(pp->hand[i]);
145			if (Numseen[oppos] == Numcards[oppos] &&
146			    !(pp->hand[i] == C_RIGHT_WAY &&
147			      Numseen[C_LIMIT] != Numcards[C_LIMIT]))
148				goto playsafe;
149			else if (!cango
150			    && (op->can_go || !pp->can_go || Topcard < Deck)) {
151				card = (Topcard - Deck) - roll(1, 10);
152				if ((!pp->mileage) != (!op->mileage))
153					card -= 7;
154#ifdef DEBUG
155				if (Debug)
156					fprintf(outf,
157						"CALCMOVE: card = %d, DECK_SZ / 4 = %d\n",
158						card, DECK_SZ / 4);
159#endif
160				if (card < DECK_SZ / 4)
161					goto playsafe;
162			}
163			safe--;
164			playit[i] = cango;
165		}
166	}
167	if (!pp->can_go && !is_repair(pp->battle))
168		Numneed[opposite(pp->battle)]++;
169redoit:
170	foundlow = (cango || count[C_END_LIMIT] != 0
171			  || Numseen[C_LIMIT] == Numcards[C_LIMIT]
172			  || pp->safety[S_RIGHT_WAY] != S_UNKNOWN);
173	foundend = FALSE;
174	count200 = pp->nummiles[C_200];
175	badcount = 0;
176	curmax = -1;
177	curmin = 101;
178	nummin = -1;
179	nummax = -1;
180	value = valbuf;
181	for (i = 0; i < HAND_SZ; i++) {
182		card = pp->hand[i];
183		if (is_safety(card) || playit[i] == (cango != 0)) {
184#ifdef DEBUG
185			if (Debug)
186				fprintf(outf, "CALCMOVE: switch(\"%s\")\n",
187					C_name[card]);
188#endif
189			switch (card) {
190			  case C_25:	case C_50:
191				diff = End - pp->mileage;
192				/* avoid getting too close */
193				if (Topcard > Deck && cango && diff <= 100
194				    && (int)diff / Value[card] > count[card]
195				    && (card == C_25 || diff % 50 == 0)) {
196					if (card == C_50 && diff - 50 == 25
197					    && count[C_25] > 0)
198						goto okay;
199					*value = 0;
200					if (--cango <= 0)
201						goto redoit;
202					break;
203				}
204okay:
205				*value = (Value[card] >> 3);
206				if (pp->speed == C_LIMIT)
207					++*value;
208				else
209					--*value;
210				if (!foundlow
211				   && (card == C_50 || count[C_50] == 0)) {
212					*value = (pp->mileage ? 10 : 20);
213					foundlow = TRUE;
214				}
215				goto miles;
216			  case C_200:
217				if (++count200 > 2) {
218					*value = 0;
219					break;
220				}
221				/* FALLTHROUGH */
222			  case C_75:	case C_100:
223				*value = (Value[card] >> 3);
224				if (pp->speed == C_LIMIT)
225					--*value;
226				else
227					++*value;
228miles:
229				if (pp->mileage + Value[card] > End)
230					*value = (End == 700 ? card : 0);
231				else if (pp->mileage + Value[card] == End) {
232					*value = (foundend ? card : V_VALUABLE);
233					foundend = TRUE;
234				}
235				break;
236			  case C_END_LIMIT:
237				if (pp->safety[S_RIGHT_WAY] != S_UNKNOWN)
238					*value = (pp->safety[S_RIGHT_WAY] ==
239						  S_PLAYED ? -1 : 1);
240				else if (pp->speed == C_LIMIT &&
241					 End - pp->mileage <= 50)
242					*value = 1;
243				else if (pp->speed == C_LIMIT
244				    || Numseen[C_LIMIT] != Numcards[C_LIMIT]) {
245					safe = S_RIGHT_WAY;
246					oppos = C_LIMIT;
247					goto repair;
248				}
249				else {
250					*value = 0;
251					--count[C_END_LIMIT];
252				}
253				break;
254			  case C_REPAIRS:	case C_SPARE:	case C_GAS:
255				safe = safety(card) - S_CONV;
256				oppos = opposite(card);
257				if (pp->safety[safe] != S_UNKNOWN)
258					*value = (pp->safety[safe] ==
259						  S_PLAYED ? -1 : 1);
260				else if (pp->battle != oppos
261				    && (Numseen[oppos] == Numcards[oppos] ||
262					Numseen[oppos] + count[card] >
263					Numcards[oppos])) {
264					*value = 0;
265					--count[card];
266				}
267				else {
268repair:
269					*value = Numcards[oppos] * 6;
270					*value += Numseen[card] -
271						  Numseen[oppos];
272					if (!cango)
273					    *value /= (count[card]*count[card]);
274					count[card]--;
275				}
276				break;
277			  case C_GO:
278				if (pp->safety[S_RIGHT_WAY] != S_UNKNOWN)
279					*value = (pp->safety[S_RIGHT_WAY] ==
280						  S_PLAYED ? -1 : 2);
281				else if (pp->can_go
282				 && Numgos + count[C_GO] == Numneed[C_GO]) {
283					*value = 0;
284					--count[C_GO];
285				}
286				else {
287					*value = Numneed[C_GO] * 3;
288					*value += (Numseen[C_GO] - Numgos);
289					*value /= (count[C_GO] * count[C_GO]);
290					count[C_GO]--;
291				}
292				break;
293			  case C_LIMIT:
294				if (op->mileage + 50 >= End) {
295					*value = (End == 700 && !cango);
296					break;
297				}
298				if (canstop || (cango && !op->can_go))
299					*value = 1;
300				else {
301					*value = (pp->safety[S_RIGHT_WAY] !=
302						  S_UNKNOWN ? 2 : 3);
303					safe = S_RIGHT_WAY;
304					oppos = C_END_LIMIT;
305					goto normbad;
306				}
307				break;
308			  case C_CRASH:	case C_EMPTY:	case C_FLAT:
309				safe = safety(card) - S_CONV;
310				oppos = opposite(card);
311				*value = (pp->safety[safe]!=S_UNKNOWN ? 3 : 4);
312normbad:
313				if (op->safety[safe] == S_PLAYED)
314					*value = -1;
315				else {
316					*value *= Numneed[oppos] +
317						  Numseen[oppos] + 2;
318					if (!pp->mileage || foundend ||
319					    onecard(op))
320						*value += 5;
321					if (op->mileage == 0 || onecard(op))
322						*value += 5;
323					if (op->speed == C_LIMIT)
324						*value -= 3;
325					if (cango &&
326					    pp->safety[safe] != S_UNKNOWN)
327						*value += 3;
328					if (!cango)
329						*value /= ++badcount;
330				}
331				break;
332			  case C_STOP:
333				if (op->safety[S_RIGHT_WAY] == S_PLAYED)
334					*value = -1;
335				else {
336					*value = (pp->safety[S_RIGHT_WAY] !=
337						  S_UNKNOWN ? 3 : 4);
338					*value *= Numcards[C_STOP] +
339						  Numseen[C_GO];
340					if (!pp->mileage || foundend ||
341					    onecard(op))
342						*value += 5;
343					if (!cango)
344						*value /= ++badcount;
345					if (op->mileage == 0)
346						*value += 5;
347					if (op->speed == C_LIMIT || !op->can_go)
348						*value -= 5;
349					if (cango && pp->safety[S_RIGHT_WAY] !=
350						     S_UNKNOWN)
351						*value += 5;
352				}
353				break;
354			  case C_GAS_SAFE:	case C_DRIVE_SAFE:
355			  case C_SPARE_SAFE:	case C_RIGHT_WAY:
356				*value = cango ? 0 : 101;
357				break;
358			  case C_INIT:
359				*value = 0;
360				break;
361			}
362		}
363		else
364			*value = cango ? 0 : 101;
365		if (card != C_INIT) {
366			if (*value >= curmax) {
367				nummax = i;
368				curmax = *value;
369			}
370			if (*value <= curmin) {
371				nummin = i;
372				curmin = *value;
373			}
374		}
375#ifdef DEBUG
376		if (Debug)
377			mvprintw(i + 6, 2, "%3d %-14s", *value,
378				 C_name[pp->hand[i]]);
379#endif
380		value++;
381	}
382	if (!pp->can_go && !is_repair(pp->battle))
383		Numneed[opposite(pp->battle)]++;
384	if (cango) {
385play_it:
386		mvaddstr(MOVE_Y + 1, MOVE_X, "PLAY\n");
387		Movetype = M_PLAY;
388		Card_no = nummax;
389	}
390	else {
391		if (is_safety(pp->hand[nummin])) { /* NEVER discard a safety */
392			nummax = nummin;
393			goto play_it;
394		}
395		mvaddstr(MOVE_Y + 1, MOVE_X, "DISCARD\n");
396		Movetype = M_DISCARD;
397		Card_no = nummin;
398	}
399	mvprintw(MOVE_Y + 2, MOVE_X, "%16s", C_name[pp->hand[Card_no]]);
400}
401
402/*
403 * Return true if the given player could conceivably win with his next card.
404 */
405int
406onecard(const PLAY *pp)
407{
408	CARD	bat, spd, card;
409
410	bat = pp->battle;
411	spd = pp->speed;
412	card = -1;
413	if (pp->can_go || ((is_repair(bat) || bat == C_STOP || spd == C_LIMIT) &&
414			   Numseen[S_RIGHT_WAY] != 0) ||
415	    (bat >= 0 && Numseen[safety(bat)] != 0))
416		switch (End - pp->mileage) {
417		  case 200:
418			if (pp->nummiles[C_200] == 2)
419				return FALSE;
420			card = C_200;
421			/* FALLTHROUGH */
422		  case 100:
423		  case 75:
424			if (card == -1)
425				card = (End - pp->mileage == 75 ? C_75 : C_100);
426			if (spd == C_LIMIT)
427				return Numseen[S_RIGHT_WAY] == 0;
428			/* FALLTHROUGH */
429		  case 50:
430		  case 25:
431			if (card == -1)
432				card = (End - pp->mileage == 25 ? C_25 : C_50);
433			return Numseen[card] != Numcards[card];
434		}
435	return FALSE;
436}
437
438int
439canplay(const PLAY *pp, const PLAY *op, 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