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