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