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