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