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