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