Clean up:
[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.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 }