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