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