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