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