xref: /openbsd/games/cribbage/crib.c (revision 9b7c3dbb)
1 /*	$OpenBSD: crib.c,v 1.23 2016/03/07 12:07:56 mestre Exp $	*/
2 /*	$NetBSD: crib.c,v 1.7 1997/07/10 06:47:29 mikel 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 <signal.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 
38 #include "cribbage.h"
39 #include "cribcur.h"
40 
41 int
42 main(int argc, char *argv[])
43 {
44 	bool playing;
45 	int ch;
46 
47 	if (pledge("stdio rpath tty proc exec", NULL) == -1)
48 		err(1, "pledge");
49 
50 	while ((ch = getopt(argc, argv, "ehmqr")) != -1)
51 		switch (ch) {
52 		case 'e':
53 			explain = TRUE;
54 			break;
55 		case 'm':
56 			muggins = TRUE;
57 			break;
58 		case 'q':
59 			quiet = TRUE;
60 			break;
61 		case 'r':
62 			rflag = TRUE;
63 			break;
64 		case 'h':
65 		default:
66 			(void) fprintf(stderr, "usage: %s [-emqr]\n",
67 			    getprogname());
68 			return 1;
69 		}
70 
71 	initscr();
72 	(void)signal(SIGINT, rintsig);
73 	cbreak();
74 	noecho();
75 
76 	Playwin = subwin(stdscr, PLAY_Y, PLAY_X, 0, 0);
77 	Tablewin = subwin(stdscr, TABLE_Y, TABLE_X, 0, PLAY_X);
78 	Compwin = subwin(stdscr, COMP_Y, COMP_X, 0, TABLE_X + PLAY_X);
79 	Msgwin = subwin(stdscr, MSG_Y, MSG_X, Y_MSG_START, SCORE_X + 1);
80 
81 	leaveok(Playwin, TRUE);
82 	leaveok(Tablewin, TRUE);
83 	leaveok(Compwin, TRUE);
84 	clearok(stdscr, FALSE);
85 
86 	if (!quiet) {
87 		msg("Do you need instructions for cribbage? ");
88 		if (getuchar() == 'Y') {
89 			endwin();
90 			clear();
91 			mvcur(0, COLS - 1, LINES - 1, 0);
92 			fflush(stdout);
93 			instructions();
94 			cbreak();
95 			noecho();
96 			clear();
97 			refresh();
98 			msg("For cribbage rules, use \"man cribbage\"");
99 		}
100 	}
101 
102 	if (pledge("stdio tty", NULL) == -1)
103 		err(1, "pledge");
104 
105 	playing = TRUE;
106 	do {
107 		wclrtobot(Msgwin);
108 		msg(quiet ? "L or S? " : "Long (to 121) or Short (to 61)? ");
109 		if (glimit == SGAME)
110 			glimit = (getuchar() == 'L' ? LGAME : SGAME);
111 		else
112 			glimit = (getuchar() == 'S' ? SGAME : LGAME);
113 		game();
114 		msg("Another game? ");
115 		playing = (getuchar() == 'Y');
116 	} while (playing);
117 
118 	bye();
119 	return 0;
120 }
121 
122 /*
123  * makeboard:
124  *	Print out the initial board on the screen
125  */
126 void
127 makeboard(void)
128 {
129 	mvaddstr(SCORE_Y + 0, SCORE_X,
130 	    "+---------------------------------------+");
131 	mvaddstr(SCORE_Y + 1, SCORE_X,
132 	    "|  Score:   0     YOU                   |");
133 	mvaddstr(SCORE_Y + 2, SCORE_X,
134 	    "| *.....:.....:.....:.....:.....:.....  |");
135 	mvaddstr(SCORE_Y + 3, SCORE_X,
136 	    "| *.....:.....:.....:.....:.....:.....  |");
137 	mvaddstr(SCORE_Y + 4, SCORE_X,
138 	    "|                                       |");
139 	mvaddstr(SCORE_Y + 5, SCORE_X,
140 	    "| *.....:.....:.....:.....:.....:.....  |");
141 	mvaddstr(SCORE_Y + 6, SCORE_X,
142 	    "| *.....:.....:.....:.....:.....:.....  |");
143 	mvaddstr(SCORE_Y + 7, SCORE_X,
144 	    "|  Score:   0      ME                   |");
145 	mvaddstr(SCORE_Y + 8, SCORE_X,
146 	    "+---------------------------------------+");
147 	gamescore();
148 }
149 
150 /*
151  * gamescore:
152  *	Print out the current game score
153  */
154 void
155 gamescore(void)
156 {
157 	if (pgames || cgames) {
158 		mvprintw(SCORE_Y + 1, SCORE_X + 28, "Games: %3d", pgames);
159 		mvprintw(SCORE_Y + 7, SCORE_X + 28, "Games: %3d", cgames);
160 	}
161 	Lastscore[0] = -1;
162 	Lastscore[1] = -1;
163 }
164 
165 /*
166  * game:
167  *	Play one game up to glimit points.  Actually, we only ASK the
168  *	player what card to turn.  We do a random one, anyway.
169  */
170 void
171 game(void)
172 {
173 	int i, j;
174 	bool flag;
175 	bool compcrib;
176 
177 	makedeck(deck);
178 	shuffle(deck);
179 	if (gamecount == 0) {
180 		flag = TRUE;
181 		do {
182 			if (!rflag) {			/* player cuts deck */
183 				char *foo;
184 
185 				/* This is silly, but we should parse user input
186 				 * even if we're not actually going to use it.
187 				 */
188 				do {
189 					msg(quiet ? "Cut for crib? " :
190 				    "Cut to see whose crib it is -- low card wins? ");
191 					foo = get_line();
192 					if (*foo != '\0' && ((i = atoi(foo)) < 4 || i > 48))
193 						msg("Invalid cut");
194 					else
195 						*foo = '\0';
196 				} while (*foo != '\0');
197 			}
198 			i = arc4random_uniform(CARDS);	/* random cut */
199 			do {	/* comp cuts deck */
200 				j = arc4random_uniform(CARDS);
201 			} while (j == i);
202 			addmsg(quiet ? "You cut " : "You cut the ");
203 			msgcard(deck[i], FALSE);
204 			endmsg();
205 			addmsg(quiet ? "I cut " : "I cut the ");
206 			msgcard(deck[j], FALSE);
207 			endmsg();
208 			flag = (deck[i].rank == deck[j].rank);
209 			if (flag) {
210 				msg(quiet ? "We tied..." :
211 				    "We tied and have to try again...");
212 				shuffle(deck);
213 				continue;
214 			} else
215 				compcrib = (deck[i].rank > deck[j].rank);
216 		} while (flag);
217 		do_wait();
218 		clear();
219 		makeboard();
220 		refresh();
221 	} else {
222 		makeboard();
223 		refresh();
224 		werase(Tablewin);
225 		wrefresh(Tablewin);
226 		werase(Compwin);
227 		wrefresh(Compwin);
228 		msg("Loser (%s) gets first crib", (iwon ? "you" : "me"));
229 		compcrib = !iwon;
230 	}
231 
232 	pscore = cscore = 0;
233 	flag = TRUE;
234 	do {
235 		shuffle(deck);
236 		flag = !playhand(compcrib);
237 		compcrib = !compcrib;
238 	} while (flag);
239 	++gamecount;
240 	if (cscore < pscore) {
241 		if (glimit - cscore > 60) {
242 			msg("YOU DOUBLE SKUNKED ME!");
243 			pgames += 4;
244 		} else
245 			if (glimit - cscore > 30) {
246 				msg("YOU SKUNKED ME!");
247 				pgames += 2;
248 			} else {
249 				msg("YOU WON!");
250 				++pgames;
251 			}
252 		iwon = FALSE;
253 	} else {
254 		if (glimit - pscore > 60) {
255 			msg("I DOUBLE SKUNKED YOU!");
256 			cgames += 4;
257 		} else
258 			if (glimit - pscore > 30) {
259 				msg("I SKUNKED YOU!");
260 				cgames += 2;
261 			} else {
262 				msg("I WON!");
263 				++cgames;
264 			}
265 		iwon = TRUE;
266 	}
267 	gamescore();
268 }
269 
270 /*
271  * playhand:
272  *	Do up one hand of the game
273  */
274 int
275 playhand(bool mycrib)
276 {
277 	int deckpos;
278 
279 	werase(Compwin);
280 	wrefresh(Compwin);
281 	werase(Tablewin);
282 	wrefresh(Tablewin);
283 
284 	knownum = 0;
285 	deckpos = deal(mycrib);
286 	sorthand(chand, FULLHAND);
287 	sorthand(phand, FULLHAND);
288 	makeknown(chand, FULLHAND);
289 	prhand(phand, FULLHAND, Playwin, FALSE);
290 	discard(mycrib);
291 	if (cut(mycrib, deckpos))
292 		return TRUE;
293 	if (peg(mycrib))
294 		return TRUE;
295 	werase(Tablewin);
296 	wrefresh(Tablewin);
297 	if (score(mycrib))
298 		return TRUE;
299 	return FALSE;
300 }
301 
302 /*
303  * deal cards to both players from deck
304  */
305 int
306 deal(bool mycrib)
307 {
308 	int i, j;
309 
310 	for (i = j = 0; i < FULLHAND; i++) {
311 		if (mycrib) {
312 			phand[i] = deck[j++];
313 			chand[i] = deck[j++];
314 		} else {
315 			chand[i] = deck[j++];
316 			phand[i] = deck[j++];
317 		}
318 	}
319 	return (j);
320 }
321 
322 /*
323  * discard:
324  *	Handle players discarding into the crib...
325  * Note: we call cdiscard() after prining first message so player doesn't wait
326  */
327 void
328 discard(bool mycrib)
329 {
330 	char *prompt;
331 	CARD crd;
332 
333 	prcrib(mycrib, TRUE);
334 	prompt = (quiet ? "Discard --> " : "Discard a card --> ");
335 	cdiscard(mycrib);	/* puts best discard at end */
336 	crd = phand[infrom(phand, FULLHAND, prompt)];
337 	cremove(crd, phand, FULLHAND);
338 	prhand(phand, FULLHAND, Playwin, FALSE);
339 	crib[0] = crd;
340 
341 	/* Next four lines same as last four except for cdiscard(). */
342 	crd = phand[infrom(phand, FULLHAND - 1, prompt)];
343 	cremove(crd, phand, FULLHAND - 1);
344 	prhand(phand, FULLHAND, Playwin, FALSE);
345 	crib[1] = crd;
346 	crib[2] = chand[4];
347 	crib[3] = chand[5];
348 	chand[4].rank = chand[4].suit = chand[5].rank = chand[5].suit = EMPTY;
349 }
350 
351 /*
352  * cut:
353  *	Cut the deck and set turnover.  Actually, we only ASK the
354  *	player what card to turn.  We do a random one, anyway.
355  */
356 int
357 cut(bool mycrib, int pos)
358 {
359 	int i;
360 	bool win;
361 
362 	win = FALSE;
363 	if (mycrib) {
364 		if (!rflag) {	/* random cut */
365 			char *foo;
366 
367 			/* This is silly, but we should parse user input,
368 			 * even if we're not actually going to use it.
369 			 */
370 			do {
371 				msg(quiet ? "Cut the deck? " :
372 				    "How many cards down do you wish to cut the deck? ");
373 				foo = get_line();
374 				if (*foo != '\0' && ((i = atoi(foo)) < 4 || i > 36))
375 					msg("Invalid cut");
376 				else
377 					*foo = '\0';
378 			} while (*foo != '\0');
379 		}
380 		i = arc4random_uniform(CARDS - pos);
381 		turnover = deck[i + pos];
382 		addmsg(quiet ? "You cut " : "You cut the ");
383 		msgcard(turnover, FALSE);
384 		endmsg();
385 		prcrib(mycrib, FALSE);
386 		if (turnover.rank == JACK) {
387 			msg("I get two for his heels");
388 			win = chkscr(&cscore, 2);
389 		}
390 	} else {
391 		i = arc4random_uniform(CARDS - pos) + pos;
392 		turnover = deck[i];
393 		addmsg(quiet ? "I cut " : "I cut the ");
394 		msgcard(turnover, FALSE);
395 		endmsg();
396 		prcrib(mycrib, FALSE);
397 		if (turnover.rank == JACK) {
398 			msg("You get two for his heels");
399 			win = chkscr(&pscore, 2);
400 		}
401 	}
402 	makeknown(&turnover, 1);
403 	return (win);
404 }
405 
406 /*
407  * prcrib:
408  *	Print out the turnover card with crib indicator
409  */
410 void
411 prcrib(bool mycrib, bool blank)
412 {
413 	int y, cardx;
414 
415 	if (mycrib)
416 		cardx = CRIB_X;
417 	else
418 		cardx = 0;
419 
420 	mvaddstr(CRIB_Y, cardx + 1, "CRIB");
421 	prcard(stdscr, CRIB_Y + 1, cardx, turnover, blank);
422 
423 	if (mycrib)
424 		cardx = 0;
425 	else
426 		cardx = CRIB_X;
427 
428 	for (y = CRIB_Y; y <= CRIB_Y + 5; y++)
429 		mvaddstr(y, cardx, "       ");
430 	refresh();
431 }
432 
433 /*
434  * peg:
435  *	Handle all the pegging...
436  */
437 static CARD Table[14];
438 static int Tcnt;
439 
440 int
441 peg(bool mycrib)
442 {
443 	static CARD ch[CINHAND], ph[CINHAND];
444 	int i, j, k;
445 	int l;
446 	int cnum, pnum, sum;
447 	bool myturn, mego, ugo, last, played;
448 	CARD crd;
449 
450 	played = FALSE;
451 	cnum = pnum = CINHAND;
452 	for (i = 0; i < CINHAND; i++) {	/* make copies of hands */
453 		ch[i] = chand[i];
454 		ph[i] = phand[i];
455 	}
456 	Tcnt = 0;		/* index to table of cards played */
457 	sum = 0;		/* sum of cards played */
458 	mego = ugo = FALSE;
459 	myturn = !mycrib;
460 	for (;;) {
461 		last = TRUE;	/* enable last flag */
462 		prhand(ph, pnum, Playwin, FALSE);
463 		prhand(ch, cnum, Compwin, TRUE);
464 		prtable(sum);
465 		if (myturn) {
466 			if (!anymove(ch, cnum, sum)) {	/* if no card to play */
467 				if (!mego && cnum) {	/* go for comp? */
468 					msg("GO");
469 					mego = TRUE;
470 				}
471 							/* can player move? */
472 				if (anymove(ph, pnum, sum))
473 					myturn = !myturn;
474 				else {			/* give him his point */
475 					msg(quiet ? "You get one" :
476 					    "You get one point");
477 					do_wait();
478 					if (chkscr(&pscore, 1))
479 						return TRUE;
480 					sum = 0;
481 					mego = ugo = FALSE;
482 					Tcnt = 0;
483 				}
484 			} else {
485 				played = TRUE;
486 				j = -1;
487 				k = 0;
488 							/* maximize score */
489 				for (i = 0; i < cnum; i++) {
490 					l = pegscore(ch[i], Table, Tcnt, sum);
491 					if (l > k) {
492 						k = l;
493 						j = i;
494 					}
495 				}
496 				if (j < 0)		/* if nothing scores */
497 					j = cchose(ch, cnum, sum);
498 				crd = ch[j];
499 				cremove(crd, ch, cnum--);
500 				sum += VAL(crd.rank);
501 				Table[Tcnt++] = crd;
502 				if (k > 0) {
503 					addmsg(quiet ? "I get %d playing " :
504 					    "I get %d points playing ", k);
505 					msgcard(crd, FALSE);
506 					endmsg();
507 					prhand(ph, pnum, Playwin, FALSE);
508 					prhand(ch, cnum, Compwin, TRUE);
509 					prtable(sum);
510 					if (chkscr(&cscore, k))
511 						return TRUE;
512 				}
513 				myturn = !myturn;
514 			}
515 		} else {
516 			if (!anymove(ph, pnum, sum)) {	/* can player move? */
517 				if (!ugo && pnum) {	/* go for player */
518 					msg("You have a GO");
519 					ugo = TRUE;
520 				}
521 							/* can computer play? */
522 				if (anymove(ch, cnum, sum))
523 					myturn = !myturn;
524 				else {
525 					msg(quiet ? "I get one" :
526 					    "I get one point");
527 					do_wait();
528 					prhand(ph, pnum, Playwin, FALSE);
529 					prhand(ch, cnum, Compwin, TRUE);
530 					prtable(sum);
531 					if (chkscr(&cscore, 1))
532 						return TRUE;
533 					sum = 0;
534 					mego = ugo = FALSE;
535 					Tcnt = 0;
536 				}
537 			} else {			/* player plays */
538 				played = FALSE;
539 				if (pnum == 1) {
540 					crd = ph[0];
541 					msg("You play your last card");
542 				} else
543 					for (;;) {
544 						prhand(ph,
545 						    pnum, Playwin, FALSE);
546 						crd = ph[infrom(ph,
547 						    pnum, "Your play: ")];
548 						if (sum + VAL(crd.rank) <= 31)
549 							break;
550 						else
551 					msg("Total > 31 -- try again");
552 					}
553 				makeknown(&crd, 1);
554 				cremove(crd, ph, pnum--);
555 				i = pegscore(crd, Table, Tcnt, sum);
556 				sum += VAL(crd.rank);
557 				Table[Tcnt++] = crd;
558 				if (i > 0) {
559 					msg(quiet ? "You got %d" :
560 					    "You got %d points", i);
561 					if (pnum == 0)
562 						do_wait();
563 					prhand(ph, pnum, Playwin, FALSE);
564 					prhand(ch, cnum, Compwin, TRUE);
565 					prtable(sum);
566 					if (chkscr(&pscore, i))
567 						return TRUE;
568 				}
569 				myturn = !myturn;
570 			}
571 		}
572 		if (sum >= 31) {
573 			if (!myturn)
574 				do_wait();
575 			sum = 0;
576 			mego = ugo = FALSE;
577 			Tcnt = 0;
578 			last = FALSE;			/* disable last flag */
579 		}
580 		if (!pnum && !cnum)
581 			break;				/* both done */
582 	}
583 	prhand(ph, pnum, Playwin, FALSE);
584 	prhand(ch, cnum, Compwin, TRUE);
585 	prtable(sum);
586 	if (last) {
587 		if (played) {
588 			msg(quiet ? "I get one for last" :
589 			    "I get one point for last");
590 			do_wait();
591 			if (chkscr(&cscore, 1))
592 				return TRUE;
593 		} else {
594 			msg(quiet ? "You get one for last" :
595 			    "You get one point for last");
596 			do_wait();
597 			if (chkscr(&pscore, 1))
598 				return TRUE;
599 		}
600 	}
601 	return (FALSE);
602 }
603 
604 /*
605  * prtable:
606  *	Print out the table with the current score
607  */
608 void
609 prtable(int score)
610 {
611 	prhand(Table, Tcnt, Tablewin, FALSE);
612 	mvwprintw(Tablewin, (Tcnt + 2) * 2, Tcnt + 1, "%2d", score);
613 	wrefresh(Tablewin);
614 }
615 
616 /*
617  * score:
618  *	Handle the scoring of the hands
619  */
620 int
621 score(bool mycrib)
622 {
623 	sorthand(crib, CINHAND);
624 	if (mycrib) {
625 		if (plyrhand(phand, "hand"))
626 			return (TRUE);
627 		if (comphand(chand, "hand"))
628 			return (TRUE);
629 		do_wait();
630 		if (comphand(crib, "crib"))
631 			return (TRUE);
632 		do_wait();
633 	} else {
634 		if (comphand(chand, "hand"))
635 			return (TRUE);
636 		if (plyrhand(phand, "hand"))
637 			return (TRUE);
638 		if (plyrhand(crib, "crib"))
639 			return (TRUE);
640 	}
641 	return (FALSE);
642 }
643