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