xref: /netbsd-src/games/cribbage/support.c (revision e0ba63fe83af6cca8b4e2eb24d8948572d2853d2)
1 /*	$NetBSD: support.c,v 1.14 2009/08/12 05:48:04 dholland Exp $	*/
2 
3 /*-
4  * Copyright (c) 1980, 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
35 static char sccsid[] = "@(#)support.c	8.1 (Berkeley) 5/31/93";
36 #else
37 __RCSID("$NetBSD: support.c,v 1.14 2009/08/12 05:48:04 dholland Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <curses.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #include "deck.h"
46 #include "cribbage.h"
47 #include "cribcur.h"
48 
49 #define	NTV	10		/* number scores to test */
50 
51 /* score to test reachability of, and order to test them in */
52 static const int tv[NTV] = {8, 7, 9, 6, 11, 12, 13, 14, 10, 5};
53 
54 static int anysumto(const CARD[], int, int, int);
55 static void prpeg(int, int, BOOLEAN);
56 static int numofval(const CARD[], int, int);
57 
58 /*
59  * computer chooses what to play in pegging...
60  * only called if no playable card will score points
61  */
62 int
cchose(const CARD h[],int n,int s)63 cchose(const CARD h[], int n, int s)
64 {
65 	int i, j, l;
66 
67 	if (n <= 1)
68 		return (0);
69 	if (s < 4) {		/* try for good value */
70 		if ((j = anysumto(h, n, s, 4)) >= 0)
71 			return (j);
72 		if ((j = anysumto(h, n, s, 3)) >= 0 && s == 0)
73 			return (j);
74 	}
75 	if (s > 0 && s < 20) {
76 				/* try for retaliation to 31 */
77 		for (i = 1; i <= 10; i++) {
78 			if ((j = anysumto(h, n, s, 21 - i)) >= 0) {
79 				if ((l = numofval(h, n, i)) > 0) {
80 					if (l > 1 || VAL(h[j].rank) != i)
81 						return (j);
82 				}
83 			}
84 		}
85 	}
86 	if (s < 15) {
87 				/* for retaliation after 15 */
88 		for (i = 0; i < NTV; i++) {
89 			if ((j = anysumto(h, n, s, tv[i])) >= 0) {
90 				if ((l = numofval(h, n, 15 - tv[i])) > 0) {
91 					if (l > 1 ||
92 					    VAL(h[j].rank) != 15 - tv[i])
93 						return (j);
94 				}
95 			}
96 		}
97 	}
98 	j = -1;
99 				/* remember: h is sorted */
100 	for (i = n - 1; i >= 0; --i) {
101 		l = s + VAL(h[i].rank);
102 		if (l > 31)
103 			continue;
104 		if (l != 5 && l != 10 && l != 21) {
105 			j = i;
106 			break;
107 		}
108 	}
109 	if (j >= 0)
110 		return (j);
111 	for (i = n - 1; i >= 0; --i) {
112 		l = s + VAL(h[i].rank);
113 		if (l > 31)
114 			continue;
115 		if (j < 0)
116 			j = i;
117 		if (l != 5 && l != 21) {
118 			j = i;
119 			break;
120 		}
121 	}
122 	if (j < 0) {
123 		printf("\ncchose: internal error %d %d\n", j, n);
124 		exit(93);
125 	}
126 	return (j);
127 }
128 
129 /*
130  * plyrhand:
131  *	Evaluate and score a player hand or crib
132  */
133 int
plyrhand(const CARD hand[],const char * s)134 plyrhand(const CARD hand[], const char *s)
135 {
136 	static char prompt[BUFSIZ];
137 	int i, j;
138 	BOOLEAN win;
139 
140 	prhand(hand, CINHAND, Playwin, FALSE);
141 	(void) snprintf(prompt, sizeof(prompt), "Your %s scores ", s);
142 	i = scorehand(hand, turnover, CINHAND, strcmp(s, "crib") == 0, explain);
143 	if ((j = number(0, 29, prompt)) == 19)
144 		j = 0;
145 	if (i != j) {
146 		if (i < j) {
147 			win = chkscr(&pscore, i);
148 			msg("It's really only %d points; I get %d", i, 2);
149 			if (!win)
150 				win = chkscr(&cscore, 2);
151 		} else {
152 			win = chkscr(&pscore, j);
153 			msg("You should have taken %d, not %d!", i, j);
154 		}
155 		if (explain)
156 			msg("Explanation: %s", explan);
157 		do_wait();
158 	} else
159 		win = chkscr(&pscore, i);
160 	return (win);
161 }
162 
163 /*
164  * comphand:
165  *	Handle scoring and displaying the computers hand
166  */
167 int
comphand(const CARD h[],const char * s)168 comphand(const CARD h[], const char *s)
169 {
170 	int j;
171 
172 	j = scorehand(h, turnover, CINHAND, strcmp(s, "crib") == 0, FALSE);
173 	prhand(h, CINHAND, Compwin, FALSE);
174 	msg("My %s scores %d", s, (j == 0 ? 19 : j));
175 	return (chkscr(&cscore, j));
176 }
177 
178 /*
179  * chkscr:
180  *	Add inc to scr and test for > glimit, printing on the scoring
181  *	board while we're at it.
182  */
183 int Lastscore[2] = {-1, -1};
184 
185 int
chkscr(int * scr,int inc)186 chkscr(int *scr, int inc)
187 {
188 	BOOLEAN myturn;
189 
190 	myturn = (scr == &cscore);
191 	if (inc != 0) {
192 		prpeg(Lastscore[(int)myturn], '.', myturn);
193 		Lastscore[(int)myturn] = *scr;
194 		*scr += inc;
195 		prpeg(*scr, PEG, myturn);
196 		refresh();
197 	}
198 	return (*scr >= glimit);
199 }
200 
201 /*
202  * prpeg:
203  *	Put out the peg character on the score board and put the
204  *	score up on the board.
205  */
206 static void
prpeg(int curscore,int pegc,BOOLEAN myturn)207 prpeg(int curscore, int pegc, BOOLEAN myturn)
208 {
209 	int y, x;
210 
211 	if (!myturn)
212 		y = SCORE_Y + 2;
213 	else
214 		y = SCORE_Y + 5;
215 
216 	if (curscore <= 0 || curscore >= glimit) {
217 		if (pegc == '.')
218 			pegc = ' ';
219 		if (curscore == 0)
220 			x = SCORE_X + 2;
221 		else {
222 			x = SCORE_X + 2;
223 			y++;
224 		}
225 	} else {
226 		x = (curscore - 1) % 30;
227 		if (curscore > 90 || (curscore > 30 && curscore <= 60)) {
228 			y++;
229 			x = 29 - x;
230 		}
231 		x += x / 5;
232 		x += SCORE_X + 3;
233 	}
234 	mvaddch(y, x, pegc);
235 	mvprintw(SCORE_Y + (myturn ? 7 : 1), SCORE_X + 10, "%3d", curscore);
236 }
237 
238 /*
239  * cdiscard -- the computer figures out what is the best discard for
240  * the crib and puts the best two cards at the end
241  */
242 void
cdiscard(BOOLEAN mycrib)243 cdiscard(BOOLEAN mycrib)
244 {
245 	CARD    d[CARDS], h[FULLHAND], cb[2];
246 	int i, j, k;
247 	int     nc, ns;
248 	long    sums[15];
249 	static int undo1[15] = {0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4};
250 	static int undo2[15] = {1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5};
251 
252 	makedeck(d);
253 	nc = CARDS;
254 	for (i = 0; i < knownum; i++) {	/* get all other cards */
255 		cremove(known[i], d, nc--);
256 	}
257 	for (i = 0; i < 15; i++)
258 		sums[i] = 0L;
259 	ns = 0;
260 	for (i = 0; i < (FULLHAND - 1); i++) {
261 		cb[0] = chand[i];
262 		for (j = i + 1; j < FULLHAND; j++) {
263 			cb[1] = chand[j];
264 			for (k = 0; k < FULLHAND; k++)
265 				h[k] = chand[k];
266 			cremove(chand[i], h, FULLHAND);
267 			cremove(chand[j], h, FULLHAND - 1);
268 			for (k = 0; k < nc; k++) {
269 				sums[ns] +=
270 				    scorehand(h, d[k], CINHAND, TRUE, FALSE);
271 				if (mycrib)
272 					sums[ns] += adjust(cb, d[k]);
273 				else
274 					sums[ns] -= adjust(cb, d[k]);
275 			}
276 			++ns;
277 		}
278 	}
279 	j = 0;
280 	for (i = 1; i < 15; i++)
281 		if (sums[i] > sums[j])
282 			j = i;
283 	for (k = 0; k < FULLHAND; k++)
284 		h[k] = chand[k];
285 	cremove(h[undo1[j]], chand, FULLHAND);
286 	cremove(h[undo2[j]], chand, FULLHAND - 1);
287 	chand[4] = h[undo1[j]];
288 	chand[5] = h[undo2[j]];
289 }
290 
291 /*
292  * returns true if some card in hand can be played without exceeding 31
293  */
294 int
anymove(const CARD hand[],int n,int sum)295 anymove(const CARD hand[], int n, int sum)
296 {
297 	int i, j;
298 
299 	if (n < 1)
300 		return (FALSE);
301 	j = hand[0].rank;
302 	for (i = 1; i < n; i++) {
303 		if (hand[i].rank < j)
304 			j = hand[i].rank;
305 	}
306 	return (sum + VAL(j) <= 31);
307 }
308 
309 /*
310  * anysumto returns the index (0 <= i < n) of the card in hand that brings
311  * the s up to t, or -1 if there is none
312  */
313 static int
anysumto(const CARD hand[],int n,int s,int t)314 anysumto(const CARD hand[], int n, int s, int t)
315 {
316 	int i;
317 
318 	for (i = 0; i < n; i++) {
319 		if (s + VAL(hand[i].rank) == t)
320 			return (i);
321 	}
322 	return (-1);
323 }
324 
325 /*
326  * return the number of cards in h having the given rank value
327  */
328 static int
numofval(const CARD h[],int n,int v)329 numofval(const CARD h[], int n, int v)
330 {
331 	int i, j;
332 
333 	j = 0;
334 	for (i = 0; i < n; i++) {
335 		if (VAL(h[i].rank) == v)
336 			++j;
337 	}
338 	return (j);
339 }
340 
341 /*
342  * makeknown remembers all n cards in h for future recall
343  */
344 void
makeknown(const CARD h[],int n)345 makeknown(const CARD h[], int n)
346 {
347 	int i;
348 
349 	for (i = 0; i < n; i++)
350 		known[knownum++] = h[i];
351 }
352