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