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