xref: /openbsd-src/games/cribbage/support.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: support.c,v 1.10 2006/03/27 00:10:15 tedu Exp $	*/
2 /*	$NetBSD: support.c,v 1.3 1995/03/21 15:08:59 cgd Exp $	*/
3 
4 /*-
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)support.c	8.1 (Berkeley) 5/31/93";
36 #else
37 static char rcsid[] = "$OpenBSD: support.c,v 1.10 2006/03/27 00:10:15 tedu Exp $";
38 #endif
39 #endif /* not lint */
40 
41 #include <curses.h>
42 #include <string.h>
43 
44 #include "deck.h"
45 #include "cribbage.h"
46 #include "cribcur.h"
47 
48 #define	NTV	10		/* number scores to test */
49 
50 /* score to test reachability of, and order to test them in */
51 int tv[NTV] = {8, 7, 9, 6, 11, 12, 13, 14, 10, 5};
52 
53 /*
54  * computer chooses what to play in pegging...
55  * only called if no playable card will score points
56  */
57 int
58 cchose(CARD h[], int n, int s)
59 {
60 	int i, j, l;
61 
62 	if (n <= 1)
63 		return (0);
64 	if (s < 4) {		/* try for good value */
65 		if ((j = anysumto(h, n, s, 4)) >= 0)
66 			return (j);
67 		if ((j = anysumto(h, n, s, 3)) >= 0 && s == 0)
68 			return (j);
69 	}
70 	if (s > 0 && s < 20) {
71 				/* try for retaliation to 31 */
72 		for (i = 1; i <= 10; i++) {
73 			if ((j = anysumto(h, n, s, 21 - i)) >= 0) {
74 				if ((l = numofval(h, n, i)) > 0) {
75 					if (l > 1 || VAL(h[j].rank) != i)
76 						return (j);
77 				}
78 			}
79 		}
80 	}
81 	if (s < 15) {
82 				/* for retaliation after 15 */
83 		for (i = 0; i < NTV; i++) {
84 			if ((j = anysumto(h, n, s, tv[i])) >= 0) {
85 				if ((l = numofval(h, n, 15 - tv[i])) > 0) {
86 					if (l > 1 ||
87 					    VAL(h[j].rank) != 15 - tv[i])
88 						return (j);
89 				}
90 			}
91 		}
92 	}
93 	j = -1;
94 				/* remember: h is sorted */
95 	for (i = n - 1; i >= 0; --i) {
96 		l = s + VAL(h[i].rank);
97 		if (l > 31)
98 			continue;
99 		if (l != 5 && l != 10 && l != 21) {
100 			j = i;
101 			break;
102 		}
103 	}
104 	if (j >= 0)
105 		return (j);
106 	for (i = n - 1; i >= 0; --i) {
107 		l = s + VAL(h[i].rank);
108 		if (l > 31)
109 			continue;
110 		if (j < 0)
111 			j = i;
112 		if (l != 5 && l != 21) {
113 			j = i;
114 			break;
115 		}
116 	}
117 	if (j < 0)
118 		errx("cchose internal error %d %d", j, n);
119 	return (j);
120 }
121 
122 /*
123  * plyrhand:
124  *	Evaluate and score a player hand or crib
125  */
126 int
127 plyrhand(CARD hand[], char *s)
128 {
129 	static char prompt[BUFSIZ];
130 	int i, j;
131 	bool win;
132 
133 	prhand(hand, CINHAND, Playwin, FALSE);
134 	(void) snprintf(prompt, sizeof prompt, "Your %s scores ", s);
135 	i = scorehand(hand, turnover, CINHAND, strcmp(s, "crib") == 0, explain);
136 	if ((j = number(0, 29, prompt)) == 19)
137 		j = 0;
138 	if (i != j) {
139 		if (i < j) {
140 			win = chkscr(&pscore, i);
141 			if (!win) {
142 				msg("It's really only %d points; I get %d", i, 2);
143 				win = chkscr(&cscore, 2);
144 			} else
145 				msg("It's really only %d points.", i);
146 		} else {
147 			win = chkscr(&pscore, j);
148 			msg("You should have taken %d, not %d!", i, j);
149 			if (!win && muggins) {
150 				msg("Muggins!  I score %d", i - j);
151 				win = chkscr(&cscore, i - j);
152 			}
153 		}
154 		if (explain)
155 			msg("Explanation: %s", expl_string);
156 		do_wait();
157 	} else
158 		win = chkscr(&pscore, i);
159 	return (win);
160 }
161 
162 /*
163  * comphand:
164  *	Handle scoring and displaying the computers hand
165  */
166 int
167 comphand(CARD h[], char *s)
168 {
169 	int j;
170 
171 	j = scorehand(h, turnover, CINHAND, strcmp(s, "crib") == 0, FALSE);
172 	prhand(h, CINHAND, Compwin, FALSE);
173 	msg("My %s scores %d", s, (j == 0 ? 19 : j));
174 	return (chkscr(&cscore, j));
175 }
176 
177 /*
178  * chkscr:
179  *	Add inc to scr and test for > glimit, printing on the scoring
180  *	board while we're at it.
181  */
182 int Lastscore[2] = {-1, -1};
183 
184 int
185 chkscr(int *scr, int inc)
186 {
187 	bool myturn;
188 
189 	myturn = (scr == &cscore);
190 	if (inc != 0) {
191 		prpeg(Lastscore[(int)myturn], '.', myturn);
192 		Lastscore[(int)myturn] = *scr;
193 		*scr += inc;
194 		prpeg(*scr, PEG, myturn);
195 		refresh();
196 	}
197 	return (*scr >= glimit);
198 }
199 
200 /*
201  * prpeg:
202  *	Put out the peg character on the score board and put the
203  *	score up on the board.
204  */
205 void
206 prpeg(int score, int peg, bool myturn)
207 {
208 	int y, x;
209 
210 	if (!myturn)
211 		y = SCORE_Y + 2;
212 	else
213 		y = SCORE_Y + 5;
214 
215 	if (score <= 0 || score >= glimit) {
216 		if (peg == '.')
217 			peg = ' ';
218 		if (score == 0)
219 			x = SCORE_X + 2;
220 		else {
221 			x = SCORE_X + 2;
222 			y++;
223 		}
224 	} else {
225 		x = (score - 1) % 30;
226 		if (score > 90 || (score > 30 && score <= 60)) {
227 			y++;
228 			x = 29 - x;
229 		}
230 		x += x / 5;
231 		x += SCORE_X + 3;
232 	}
233 	mvaddch(y, x, peg);
234 	mvprintw(SCORE_Y + (myturn ? 7 : 1), SCORE_X + 10, "%3d", score);
235 }
236 
237 /*
238  * cdiscard -- the computer figures out what is the best discard for
239  * the crib and puts the best two cards at the end
240  */
241 void
242 cdiscard(bool mycrib)
243 {
244 	CARD    d[CARDS], h[FULLHAND], cb[2];
245 	int i, j, k;
246 	int     nc, ns;
247 	long    sums[15];
248 	static int undo1[15] = {0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4};
249 	static int undo2[15] = {1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5};
250 
251 	makedeck(d);
252 	nc = CARDS;
253 	for (i = 0; i < knownum; i++) {	/* get all other cards */
254 		cremove(known[i], d, nc--);
255 	}
256 	for (i = 0; i < 15; i++)
257 		sums[i] = 0L;
258 	ns = 0;
259 	for (i = 0; i < (FULLHAND - 1); i++) {
260 		cb[0] = chand[i];
261 		for (j = i + 1; j < FULLHAND; j++) {
262 			cb[1] = chand[j];
263 			for (k = 0; k < FULLHAND; k++)
264 				h[k] = chand[k];
265 			cremove(chand[i], h, FULLHAND);
266 			cremove(chand[j], h, FULLHAND - 1);
267 			for (k = 0; k < nc; k++) {
268 				sums[ns] +=
269 				    scorehand(h, d[k], CINHAND, TRUE, FALSE);
270 				if (mycrib)
271 					sums[ns] += adjust(cb, d[k]);
272 				else
273 					sums[ns] -= adjust(cb, d[k]);
274 			}
275 			++ns;
276 		}
277 	}
278 	j = 0;
279 	for (i = 1; i < 15; i++)
280 		if (sums[i] > sums[j])
281 			j = i;
282 	for (k = 0; k < FULLHAND; k++)
283 		h[k] = chand[k];
284 	cremove(h[undo1[j]], chand, FULLHAND);
285 	cremove(h[undo2[j]], chand, FULLHAND - 1);
286 	chand[4] = h[undo1[j]];
287 	chand[5] = h[undo2[j]];
288 }
289 
290 /*
291  * returns true if some card in hand can be played without exceeding 31
292  */
293 int
294 anymove(CARD hand[], int n, int sum)
295 {
296 	int i, j;
297 
298 	if (n < 1)
299 		return (FALSE);
300 	j = hand[0].rank;
301 	for (i = 1; i < n; i++) {
302 		if (hand[i].rank < j)
303 			j = hand[i].rank;
304 	}
305 	return (sum + VAL(j) <= 31);
306 }
307 
308 /*
309  * anysumto returns the index (0 <= i < n) of the card in hand that brings
310  * the s up to t, or -1 if there is none
311  */
312 int
313 anysumto(CARD hand[], int n, int s, int t)
314 {
315 	int i;
316 
317 	for (i = 0; i < n; i++) {
318 		if (s + VAL(hand[i].rank) == t)
319 			return (i);
320 	}
321 	return (-1);
322 }
323 
324 /*
325  * return the number of cards in h having the given rank value
326  */
327 int
328 numofval(CARD h[], int n, int v)
329 {
330 	int i, j;
331 
332 	j = 0;
333 	for (i = 0; i < n; i++) {
334 		if (VAL(h[i].rank) == v)
335 			++j;
336 	}
337 	return (j);
338 }
339 
340 /*
341  * makeknown remembers all n cards in h for future recall
342  */
343 void
344 makeknown(CARD h[], int n)
345 {
346 	int i;
347 
348 	for (i = 0; i < n; i++)
349 		known[knownum++] = h[i];
350 }
351