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