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