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