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