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