Merge from vendor branch BSDINSTALLER:
[dragonfly.git] / games / cribbage / crib.c
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 }