1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)canfield.c 4.5 05/10/83"; 4 5 /* 6 * The canfield program 7 * 8 * Authors: 9 * Originally written: Steve Levine 10 * Converted to use curses and debugged: Steve Feldman 11 * Card counting: Kirk McKusick and Mikey Olson 12 * User interface cleanups: Eric Allman and Kirk McKusick 13 * Betting by Kirk McKusick 14 */ 15 16 #include <curses.h> 17 #include <ctype.h> 18 #include <signal.h> 19 #include <sys/types.h> 20 21 #define decksize 52 22 #define originrow 0 23 #define origincol 0 24 #define basecol 1 25 #define boxcol 42 26 #define tboxrow 2 27 #define bboxrow 17 28 #define movecol 43 29 #define moverow 16 30 #define msgcol 43 31 #define msgrow 15 32 #define titlecol 30 33 #define titlerow 0 34 #define sidecol 1 35 #define ottlrow 6 36 #define foundcol 11 37 #define foundrow 3 38 #define stockcol 2 39 #define stockrow 8 40 #define fttlcol 10 41 #define fttlrow 1 42 #define taloncol 2 43 #define talonrow 13 44 #define tabrow 8 45 #define ctoprow 21 46 #define cbotrow 23 47 #define cinitcol 14 48 #define cheightcol 1 49 #define cwidthcol 4 50 #define handstatrow 21 51 #define handstatcol 7 52 #define talonstatrow 22 53 #define talonstatcol 7 54 #define stockstatrow 23 55 #define stockstatcol 7 56 #define Ace 1 57 #define Jack 11 58 #define Queen 12 59 #define King 13 60 #define atabcol 11 61 #define btabcol 18 62 #define ctabcol 25 63 #define dtabcol 32 64 65 #define spades 's' 66 #define clubs 'c' 67 #define hearts 'h' 68 #define diamonds 'd' 69 #define black 'b' 70 #define red 'r' 71 72 #define stk 1 73 #define tal 2 74 #define tab 3 75 #define INCRHAND(row, col) {\ 76 row -= cheightcol;\ 77 if (row < ctoprow) {\ 78 row = cbotrow;\ 79 col += cwidthcol;\ 80 }\ 81 } 82 #define DECRHAND(row, col) {\ 83 row += cheightcol;\ 84 if (row > cbotrow) {\ 85 row = ctoprow;\ 86 col -= cwidthcol;\ 87 }\ 88 } 89 90 91 struct cardtype { 92 char suit; 93 char color; 94 bool visible; 95 bool paid; 96 int rank; 97 struct cardtype *next; 98 }; 99 100 #define NIL ((struct cardtype *) -1) 101 102 struct cardtype *deck[decksize]; 103 struct cardtype cards[decksize]; 104 struct cardtype *bottom[4], *found[4], *tableau[4]; 105 struct cardtype *talon, *hand, *stock, *basecard; 106 int length[4]; 107 int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru; 108 char suitmap[4] = {spades, clubs, hearts, diamonds}; 109 char colormap[4] = {black, black, red, red}; 110 char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol}; 111 char srcpile, destpile; 112 int mtforigin, tempbase; 113 int coldcol, cnewcol, coldrow, cnewrow; 114 bool errmsg, done; 115 bool mtfdone, Cflag = FALSE; 116 #define INSTRUCTIONBOX 1 117 #define BETTINGBOX 2 118 #define NOBOX 3 119 int status = INSTRUCTIONBOX; 120 121 /* 122 * Basic betting costs 123 */ 124 #define costofhand 13 125 #define costofinspection 13 126 #define costofgame 26 127 #define costofrunthroughhand 5 128 #define costofinformation 1 129 #define secondsperdollar 60 130 #define valuepercardup 5 131 /* 132 * Variables associated with betting 133 */ 134 struct betinfo { 135 long hand; /* cost of dealing hand */ 136 long inspection; /* cost of inspecting hand */ 137 long game; /* cost of buying game */ 138 long runs; /* cost of running through hands */ 139 long information; /* cost of information */ 140 long thinktime; /* cost of thinking time */ 141 long wins; /* total winnings */ 142 long worth; /* net worth after costs */ 143 }; 144 struct betinfo this, total; 145 bool startedgame = FALSE, infullgame = FALSE; 146 time_t acctstart; 147 int dbfd = -1; 148 149 /* 150 * The following procedures print the board onto the screen using the 151 * addressible cursor. The end of these procedures will also be 152 * separated from the rest of the program. 153 * 154 * procedure to set the move command box 155 */ 156 movebox() 157 { 158 switch (status) { 159 case BETTINGBOX: 160 printtopbettingbox(); 161 break; 162 case NOBOX: 163 clearabovemovebox(); 164 break; 165 case INSTRUCTIONBOX: 166 printtopinstructions(); 167 break; 168 } 169 move(moverow, boxcol); 170 printw("| |"); 171 move(msgrow, boxcol); 172 printw("| |"); 173 switch (status) { 174 case BETTINGBOX: 175 printbottombettingbox(); 176 break; 177 case NOBOX: 178 clearbelowmovebox(); 179 break; 180 case INSTRUCTIONBOX: 181 printbottominstructions(); 182 break; 183 } 184 refresh(); 185 } 186 187 /* 188 * print directions above move box 189 */ 190 printtopinstructions() 191 { 192 move(tboxrow, boxcol); 193 printw("*--------------------------*"); 194 move(tboxrow + 1, boxcol); 195 printw("| MOVES |"); 196 move(tboxrow + 2, boxcol); 197 printw("|s# = stock to tableau |"); 198 move(tboxrow + 3, boxcol); 199 printw("|sf = stock to foundation |"); 200 move(tboxrow + 4, boxcol); 201 printw("|t# = talon to tableau |"); 202 move(tboxrow + 5, boxcol); 203 printw("|tf = talon to foundation |"); 204 move(tboxrow + 6, boxcol); 205 printw("|## = tableau to tableau |"); 206 move(tboxrow + 7, boxcol); 207 printw("|#f = tableau to foundation|"); 208 move(tboxrow + 8, boxcol); 209 printw("|ht = hand to talon |"); 210 move(tboxrow + 9, boxcol); 211 printw("|c = toggle card counting |"); 212 move(tboxrow + 10, boxcol); 213 printw("|b = present betting info |"); 214 move(tboxrow + 11, boxcol); 215 printw("|q = quit to end the game |"); 216 move(tboxrow + 12, boxcol); 217 printw("|==========================|"); 218 } 219 220 /* 221 * Print the betting box. 222 */ 223 printtopbettingbox() 224 { 225 226 move(tboxrow, boxcol); 227 printw(" "); 228 move(tboxrow + 1, boxcol); 229 printw("*--------------------------*"); 230 move(tboxrow + 2, boxcol); 231 printw("|Costs Hand Total |"); 232 move(tboxrow + 3, boxcol); 233 printw("| Hands |"); 234 move(tboxrow + 4, boxcol); 235 printw("| Inspections |"); 236 move(tboxrow + 5, boxcol); 237 printw("| Games |"); 238 move(tboxrow + 6, boxcol); 239 printw("| Runs |"); 240 move(tboxrow + 7, boxcol); 241 printw("| Information |"); 242 move(tboxrow + 8, boxcol); 243 printw("| Think time |"); 244 move(tboxrow + 9, boxcol); 245 printw("|Total Costs |"); 246 move(tboxrow + 10, boxcol); 247 printw("|Winnings |"); 248 move(tboxrow + 11, boxcol); 249 printw("|Net Worth |"); 250 move(tboxrow + 12, boxcol); 251 printw("|==========================|"); 252 } 253 254 /* 255 * clear info above move box 256 */ 257 clearabovemovebox() 258 { 259 int i; 260 261 for (i = 0; i <= 11; i++) { 262 move(tboxrow + i, boxcol); 263 printw(" "); 264 } 265 move(tboxrow + 12, boxcol); 266 printw("*--------------------------*"); 267 } 268 269 /* 270 * print instructions below move box 271 */ 272 printbottominstructions() 273 { 274 move(bboxrow, boxcol); 275 printw("|Replace # with the number |"); 276 move(bboxrow + 1, boxcol); 277 printw("|of the tableau you want. |"); 278 move(bboxrow + 2, boxcol); 279 printw("*--------------------------*"); 280 } 281 282 /* 283 * print betting information below move box 284 */ 285 printbottombettingbox() 286 { 287 move(bboxrow, boxcol); 288 printw("|x = toggle information box|"); 289 move(bboxrow + 1, boxcol); 290 printw("|i = list instructions |"); 291 move(bboxrow + 2, boxcol); 292 printw("*--------------------------*"); 293 } 294 295 /* 296 * clear info below move box 297 */ 298 clearbelowmovebox() 299 { 300 int i; 301 302 move(bboxrow, boxcol); 303 printw("*--------------------------*"); 304 for (i = 1; i <= 2; i++) { 305 move(bboxrow + i, boxcol); 306 printw(" "); 307 } 308 } 309 310 /* 311 * procedure to put the board on the screen using addressable cursor 312 */ 313 makeboard() 314 { 315 clear(); 316 refresh(); 317 move(titlerow, titlecol); 318 printw("=-> CANFIELD <-="); 319 move(fttlrow, fttlcol); 320 printw("foundation"); 321 move(foundrow - 1, fttlcol); 322 printw("=---= =---= =---= =---="); 323 move(foundrow, fttlcol); 324 printw("| | | | | | | |"); 325 move(foundrow + 1, fttlcol); 326 printw("=---= =---= =---= =---="); 327 move(ottlrow, sidecol); 328 printw("stock tableau"); 329 move(stockrow - 1, sidecol); 330 printw("=---="); 331 move(stockrow, sidecol); 332 printw("| |"); 333 move(stockrow + 1, sidecol); 334 printw("=---="); 335 move(talonrow - 2, sidecol); 336 printw("talon"); 337 move(talonrow - 1, sidecol); 338 printw("=---="); 339 move(talonrow, sidecol); 340 printw("| |"); 341 move(talonrow + 1, sidecol); 342 printw("=---="); 343 move(tabrow - 1, atabcol); 344 printw("-1- -2- -3- -4-"); 345 movebox(); 346 } 347 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 348 349 /* clean up the board for another game */ 350 cleanupboard() 351 { 352 int cnt, row, col; 353 struct cardtype *ptr; 354 355 if (Cflag) { 356 clearstat(); 357 for(ptr = stock, row = stockrow; 358 ptr != NIL; 359 ptr = ptr->next, row++) { 360 move(row, sidecol); 361 printw(" "); 362 } 363 move(row, sidecol); 364 printw(" "); 365 move(stockrow + 1, sidecol); 366 printw("=---="); 367 move(talonrow - 2, sidecol); 368 printw("talon"); 369 move(talonrow - 1, sidecol); 370 printw("=---="); 371 move(talonrow + 1, sidecol); 372 printw("=---="); 373 } 374 move(stockrow, sidecol); 375 printw("| |"); 376 move(talonrow, sidecol); 377 printw("| |"); 378 move(foundrow, fttlcol); 379 printw("| | | | | | | |"); 380 for (cnt = 0; cnt < 4; cnt++) { 381 switch(cnt) { 382 case 0: 383 col = atabcol; 384 break; 385 case 1: 386 col = btabcol; 387 break; 388 case 2: 389 col = ctabcol; 390 break; 391 case 3: 392 col = dtabcol; 393 break; 394 } 395 for(ptr = tableau[cnt], row = tabrow; 396 ptr != NIL; 397 ptr = ptr->next, row++) 398 removecard(col, row); 399 } 400 } 401 402 /* procedure to create a deck of cards */ 403 initdeck(deck) 404 struct cardtype *deck[]; 405 { 406 int i; 407 int scnt; 408 char s; 409 int r; 410 411 i = 0; 412 for (scnt=0; scnt<4; scnt++) { 413 s = suitmap[scnt]; 414 for (r=Ace; r<=King; r++) { 415 deck[i] = &cards[i]; 416 cards[i].rank = r; 417 cards[i].suit = s; 418 cards[i].color = colormap[scnt]; 419 cards[i].next = NIL; 420 i++; 421 } 422 } 423 } 424 425 /* procedure to shuffle the deck */ 426 shuffle(deck) 427 struct cardtype *deck[]; 428 { 429 int i,j; 430 struct cardtype *temp; 431 432 for (i=0; i<decksize; i++) { 433 deck[i]->visible = FALSE; 434 deck[i]->paid = FALSE; 435 } 436 for (i = decksize-1; i>=0; i--) { 437 j = random() % decksize; 438 if (i != j) { 439 temp = deck[i]; 440 deck[i] = deck[j]; 441 deck[j] = temp; 442 } 443 } 444 } 445 446 /* procedure to remove the card from the board */ 447 removecard(a, b) 448 { 449 move(b, a); 450 printw(" "); 451 } 452 453 /* procedure to print the cards on the board */ 454 printrank(a, b, cp, inverse) 455 struct cardtype *cp; 456 bool inverse; 457 { 458 move(b, a); 459 if (cp->rank != 10) 460 addch(' '); 461 if (inverse) 462 standout(); 463 switch (cp->rank) { 464 case 2: case 3: case 4: case 5: case 6: case 7: 465 case 8: case 9: case 10: 466 printw("%d", cp->rank); 467 break; 468 case Ace: 469 addch('A'); 470 break; 471 case Jack: 472 addch('J'); 473 break; 474 case Queen: 475 addch('Q'); 476 break; 477 case King: 478 addch('K'); 479 } 480 if (inverse) 481 standend(); 482 } 483 484 printcard(a, b, cp) 485 int a,b; 486 struct cardtype *cp; 487 { 488 if (cp == NIL) 489 removecard(a, b); 490 else if (cp->visible == FALSE) { 491 move(b, a); 492 printw(" ? "); 493 } else { 494 bool inverse = (cp->suit == 'd' || cp->suit == 'h'); 495 496 printrank(a, b, cp, inverse); 497 if (inverse) 498 standout(); 499 addch(cp->suit); 500 if (inverse) 501 standend(); 502 } 503 } 504 505 /* 506 * procedure to move the top card from one location to the top 507 * of another location. The pointers always point to the top 508 * of the piles. 509 */ 510 transit(source, dest) 511 struct cardtype **source, **dest; 512 { 513 struct cardtype *temp; 514 515 temp = *source; 516 *source = (*source)->next; 517 temp->next = *dest; 518 *dest = temp; 519 } 520 521 /* 522 * Procedure to set the cards on the foundation base when available. 523 * Note that it is only called on a foundation pile at the beginning of 524 * the game, so the pile will have exactly one card in it. 525 */ 526 527 fndbase(cp, column, row) 528 struct cardtype **cp; 529 { 530 bool nomore; 531 532 if (*cp != NIL) 533 do { 534 if ((*cp)->rank == basecard->rank) { 535 base++; 536 printcard(pilemap[base], foundrow, *cp); 537 if (*cp == tableau[0]) 538 length[0] = length[0] - 1; 539 if (*cp == tableau[1]) 540 length[1] = length[1] - 1; 541 if (*cp == tableau[2]) 542 length[2] = length[2] - 1; 543 if (*cp == tableau[3]) 544 length[3] = length[3] - 1; 545 transit(cp, &found[base]); 546 if (cp == &talon) 547 usedtalon(); 548 if (cp == &stock) 549 usedstock(); 550 if (*cp != NIL) { 551 printcard(column, row, *cp); 552 nomore = FALSE; 553 } else { 554 removecard(column, row); 555 nomore = TRUE; 556 } 557 cardsoff++; 558 if (infullgame) { 559 this.wins += valuepercardup; 560 total.wins += valuepercardup; 561 } 562 } else 563 nomore = TRUE; 564 } while (nomore == FALSE); 565 } 566 567 /* 568 * procedure to initialize the things necessary for the game 569 */ 570 initgame() 571 { 572 register i; 573 574 for (i=0; i<18; i++) { 575 deck[i]->visible = TRUE; 576 deck[i]->paid = TRUE; 577 } 578 stockcnt = 13; 579 stock = deck[12]; 580 for (i=12; i>=1; i--) 581 deck[i]->next = deck[i - 1]; 582 deck[0]->next = NIL; 583 found[0] = deck[13]; 584 deck[13]->next = NIL; 585 for (i=1; i<4; i++) 586 found[i] = NIL; 587 basecard = found[0]; 588 for (i=14; i<18; i++) { 589 tableau[i - 14] = deck[i]; 590 deck[i]->next = NIL; 591 } 592 for (i=0; i<4; i++) { 593 bottom[i] = tableau[i]; 594 length[i] = tabrow; 595 } 596 hand = deck[18]; 597 for (i=18; i<decksize-1; i++) 598 deck[i]->next = deck[i + 1]; 599 deck[decksize-1]->next = NIL; 600 talon = NIL; 601 base = 0; 602 cinhand = 34; 603 taloncnt = 0; 604 timesthru = 0; 605 cardsoff = 1; 606 coldrow = ctoprow; 607 coldcol = cinitcol; 608 cnewrow = ctoprow; 609 cnewcol = cinitcol + cwidthcol; 610 } 611 612 /* procedure to print the beginning cards and to start each game */ 613 startgame() 614 { 615 register int j; 616 617 shuffle(deck); 618 initgame(); 619 this.hand = costofhand; 620 total.hand += costofhand; 621 this.inspection = 0; 622 this.game = 0; 623 this.runs = 0; 624 this.information = 0; 625 this.wins = 0; 626 infullgame = FALSE; 627 startedgame = FALSE; 628 printcard(foundcol, foundrow, found[0]); 629 printcard(stockcol, stockrow, stock); 630 printcard(atabcol, tabrow, tableau[0]); 631 printcard(btabcol, tabrow, tableau[1]); 632 printcard(ctabcol, tabrow, tableau[2]); 633 printcard(dtabcol, tabrow, tableau[3]); 634 printcard(taloncol, talonrow, talon); 635 move(foundrow - 2, basecol); 636 printw("Base"); 637 move(foundrow - 1, basecol); 638 printw("Rank"); 639 printrank(basecol, foundrow, found[0], 0); 640 for (j=0; j<=3; j++) 641 fndbase(&tableau[j], pilemap[j], tabrow); 642 fndbase(&stock, stockcol, stockrow); 643 showstat(); /* show card counting info to cheaters */ 644 movetotalon(); 645 updatebettinginfo(); 646 } 647 648 649 /* procedure to clear the message printed from an error */ 650 clearmsg() 651 { 652 int i; 653 654 if (errmsg == TRUE) { 655 errmsg = FALSE; 656 move(msgrow, msgcol); 657 for (i=0; i<25; i++) 658 addch(' '); 659 refresh(); 660 } 661 } 662 663 /* procedure to print an error message if the move is not listed */ 664 dumberror() 665 { 666 errmsg = TRUE; 667 move(msgrow, msgcol); 668 printw("Not a proper move "); 669 } 670 671 /* procedure to print an error message if the move is not possible */ 672 destinerror() 673 { 674 errmsg = TRUE; 675 move(msgrow, msgcol); 676 printw("Error: Can't move there"); 677 } 678 679 /* function to see if the source has cards in it */ 680 bool 681 notempty(cp) 682 struct cardtype *cp; 683 { 684 if (cp == NIL) { 685 errmsg = TRUE; 686 move(msgrow, msgcol); 687 printw("Error: no cards to move"); 688 return (FALSE); 689 } else 690 return (TRUE); 691 } 692 693 694 /* function to see if the rank of one card is less than another */ 695 696 bool 697 ranklower(cp1, cp2) 698 struct cardtype *cp1, *cp2; 699 { 700 if (cp2->rank == Ace) 701 if (cp1->rank == King) 702 return (TRUE); 703 else 704 return (FALSE); 705 else if (cp1->rank + 1 == cp2->rank) 706 return (TRUE); 707 else 708 return (FALSE); 709 } 710 711 /* function to check the cardcolor for moving to a tableau */ 712 bool 713 diffcolor(cp1, cp2) 714 struct cardtype *cp1, *cp2; 715 { 716 if (cp1->color == cp2->color) 717 return (FALSE); 718 else 719 return (TRUE); 720 } 721 722 /* function to see if the card can move to the tableau */ 723 bool 724 tabok(cp, des) 725 struct cardtype *cp; 726 { 727 if ((cp == stock) && (tableau[des] == NIL)) 728 return (TRUE); 729 else if (tableau[des] == NIL) 730 if (stock == NIL) 731 return (TRUE); 732 else 733 return (FALSE); 734 else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des])) 735 return (TRUE); 736 else 737 return (FALSE); 738 } 739 740 /* 741 * procedure to turn the cards onto the talon from the deck 742 */ 743 movetotalon() 744 { 745 int i, fin; 746 747 if (cinhand >= 3) 748 fin = 3; 749 else if (cinhand > 0) 750 fin = cinhand; 751 else if (talon != NIL) { 752 timesthru++; 753 errmsg = TRUE; 754 move(msgrow, msgcol); 755 if (timesthru != 4) { 756 printw("Talon is now the new hand"); 757 this.runs += costofrunthroughhand; 758 total.runs += costofrunthroughhand; 759 while (talon != NIL) { 760 transit(&talon, &hand); 761 cinhand++; 762 } 763 if (cinhand >= 3) 764 fin = 3; 765 else 766 fin = cinhand; 767 taloncnt = 0; 768 coldrow = ctoprow; 769 coldcol = cinitcol; 770 cnewrow = ctoprow; 771 cnewcol = cinitcol + cwidthcol; 772 clearstat(); 773 showstat(); 774 } else { 775 fin = 0; 776 done = TRUE; 777 printw("I believe you have lost"); 778 refresh(); 779 sleep(5); 780 } 781 } else { 782 errmsg = TRUE; 783 move(msgrow, msgcol); 784 printw("Talon and hand are empty"); 785 fin = 0; 786 } 787 for (i=0; i<fin; i++) { 788 transit(&hand, &talon); 789 INCRHAND(cnewrow, cnewcol); 790 INCRHAND(coldrow, coldcol); 791 removecard(cnewcol, cnewrow); 792 if (i == fin - 1) 793 talon->visible = TRUE; 794 if (Cflag) { 795 if (talon->paid == FALSE && talon->visible == TRUE) { 796 this.information += costofinformation; 797 total.information += costofinformation; 798 talon->paid = TRUE; 799 } 800 printcard(coldcol, coldrow, talon); 801 } 802 } 803 if (fin != 0) { 804 printcard(taloncol, talonrow, talon); 805 cinhand -= fin; 806 taloncnt += fin; 807 if (Cflag) { 808 move(handstatrow, handstatcol); 809 printw("%3d", cinhand); 810 move(talonstatrow, talonstatcol); 811 printw("%3d", taloncnt); 812 } 813 fndbase(&talon, taloncol, talonrow); 814 } 815 } 816 817 818 /* 819 * procedure to print card counting info on screen 820 */ 821 showstat() 822 { 823 int row, col; 824 register struct cardtype *ptr; 825 826 if (!Cflag) 827 return; 828 move(talonstatrow, talonstatcol - 7); 829 printw("Talon: %3d", taloncnt); 830 move(handstatrow, handstatcol - 7); 831 printw("Hand: %3d", cinhand); 832 move(stockstatrow, stockstatcol - 7); 833 printw("Stock: %3d", stockcnt); 834 for ( row = coldrow, col = coldcol, ptr = talon; 835 ptr != NIL; 836 ptr = ptr->next ) { 837 if (ptr->paid == FALSE && ptr->visible == TRUE) { 838 ptr->paid = TRUE; 839 this.information += costofinformation; 840 total.information += costofinformation; 841 } 842 printcard(col, row, ptr); 843 DECRHAND(row, col); 844 } 845 for ( row = cnewrow, col = cnewcol, ptr = hand; 846 ptr != NIL; 847 ptr = ptr->next ) { 848 if (ptr->paid == FALSE && ptr->visible == TRUE) { 849 ptr->paid = TRUE; 850 this.information += costofinformation; 851 total.information += costofinformation; 852 } 853 INCRHAND(row, col); 854 printcard(col, row, ptr); 855 } 856 } 857 858 /* 859 * procedure to clear card counting info from screen 860 */ 861 clearstat() 862 { 863 int row; 864 865 move(talonstatrow, talonstatcol - 7); 866 printw(" "); 867 move(handstatrow, handstatcol - 7); 868 printw(" "); 869 move(stockstatrow, stockstatcol - 7); 870 printw(" "); 871 for ( row = ctoprow ; row <= cbotrow ; row++ ) { 872 move(row, cinitcol); 873 printw("%56s", " "); 874 } 875 } 876 877 878 /* 879 * procedure to update card counting base 880 */ 881 usedtalon() 882 { 883 removecard(coldcol, coldrow); 884 DECRHAND(coldrow, coldcol); 885 if (talon != NIL && (talon->visible == FALSE)) { 886 talon->visible = TRUE; 887 if (Cflag) { 888 this.information += costofinformation; 889 total.information += costofinformation; 890 talon->paid = TRUE; 891 printcard(coldcol, coldrow, talon); 892 } 893 } 894 taloncnt--; 895 if (Cflag) { 896 move(talonstatrow, talonstatcol); 897 printw("%3d", taloncnt); 898 } 899 } 900 901 902 /* 903 * procedure to update stock card counting base 904 */ 905 usedstock() 906 { 907 stockcnt--; 908 if (Cflag) { 909 move(stockstatrow, stockstatcol); 910 printw("%3d", stockcnt); 911 } 912 } 913 914 /* 915 * let 'em know how they lost! 916 */ 917 showcards() 918 { 919 register struct cardtype *ptr; 920 int row; 921 922 if (!Cflag) 923 return; 924 for (ptr = talon; ptr != NIL; ptr = ptr->next) { 925 ptr->visible = TRUE; 926 ptr->paid = TRUE; 927 } 928 for (ptr = hand; ptr != NIL; ptr = ptr->next) { 929 ptr->visible = TRUE; 930 ptr->paid = TRUE; 931 } 932 showstat(); 933 move(stockrow + 1, sidecol); 934 printw(" "); 935 move(talonrow - 2, sidecol); 936 printw(" "); 937 move(talonrow - 1, sidecol); 938 printw(" "); 939 move(talonrow, sidecol); 940 printw(" "); 941 move(talonrow + 1, sidecol); 942 printw(" "); 943 for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) { 944 move(row, stockcol - 1); 945 printw("| |"); 946 printcard(stockcol, row, ptr); 947 } 948 if (stock == NIL) { 949 move(row, stockcol - 1); 950 printw("| |"); 951 row++; 952 } 953 move(handstatrow, handstatcol - 7); 954 printw(" "); 955 move(row, stockcol - 1); 956 printw("=---="); 957 getcmd(moverow, movecol, "Hit return to exit"); 958 } 959 960 /* 961 * procedure to update the betting values 962 */ 963 updatebettinginfo() 964 { 965 long thiscosts, totalcosts; 966 time_t now; 967 register long dollars; 968 969 thiscosts = this.hand + this.inspection + this.game + 970 this.runs + this.information + this.thinktime; 971 totalcosts = total.hand + total.inspection + total.game + 972 total.runs + total.information + total.thinktime; 973 this.worth = this.wins - thiscosts; 974 total.worth = total.wins - totalcosts; 975 time(&now); 976 dollars = (now - acctstart) / secondsperdollar; 977 if (dollars > 0) { 978 this.thinktime += dollars; 979 total.thinktime += dollars; 980 acctstart += dollars * secondsperdollar; 981 } 982 if (status != BETTINGBOX) 983 return; 984 move(tboxrow + 3, boxcol + 13); 985 printw("%4d%9d", this.hand, total.hand); 986 move(tboxrow + 4, boxcol + 13); 987 printw("%4d%9d", this.inspection, total.inspection); 988 move(tboxrow + 5, boxcol + 13); 989 printw("%4d%9d", this.game, total.game); 990 move(tboxrow + 6, boxcol + 13); 991 printw("%4d%9d", this.runs, total.runs); 992 move(tboxrow + 7, boxcol + 13); 993 printw("%4d%9d", this.information, total.information); 994 move(tboxrow + 8, boxcol + 13); 995 printw("%4d%9d", this.thinktime, total.thinktime); 996 move(tboxrow + 9, boxcol + 13); 997 printw("%4d%9d", thiscosts, totalcosts); 998 move(tboxrow + 10, boxcol + 13); 999 printw("%4d%9d", this.wins, total.wins); 1000 move(tboxrow + 11, boxcol + 13); 1001 printw("%4d%9d", this.worth, total.worth); 1002 } 1003 1004 /* 1005 * procedure to move a card from the stock or talon to the tableau 1006 */ 1007 simpletableau(cp, des) 1008 struct cardtype **cp; 1009 { 1010 int origin; 1011 1012 if (notempty(*cp)) { 1013 if (tabok(*cp, des)) { 1014 if (*cp == stock) 1015 origin = stk; 1016 else 1017 origin = tal; 1018 if (tableau[des] == NIL) 1019 bottom[des] = *cp; 1020 transit(cp, &tableau[des]); 1021 length[des]++; 1022 printcard(pilemap[des], length[des], tableau[des]); 1023 timesthru = 0; 1024 if (origin == stk) { 1025 usedstock(); 1026 printcard(stockcol, stockrow, stock); 1027 } else { 1028 usedtalon(); 1029 printcard(taloncol, talonrow, talon); 1030 } 1031 } else 1032 destinerror(); 1033 } 1034 } 1035 1036 1037 tabprint(sour, des) 1038 { 1039 int dlength, slength, i; 1040 struct cardtype *tempcard; 1041 1042 for (i=tabrow; i<=length[sour]; i++) 1043 removecard(pilemap[sour], i); 1044 dlength = length[des] + 1; 1045 slength = length[sour]; 1046 if (slength == tabrow) 1047 printcard(pilemap[des], dlength, tableau[sour]); 1048 else 1049 while (slength != tabrow - 1) { 1050 tempcard = tableau[sour]; 1051 for (i=1; i<=slength-tabrow; i++) 1052 tempcard = tempcard->next; 1053 printcard(pilemap[des], dlength, tempcard); 1054 slength--; 1055 dlength++; 1056 } 1057 } 1058 1059 /* procedure to move from the tableau to the tableau */ 1060 tabtotab(sour, des) 1061 { 1062 struct cardtype *temp; 1063 1064 if (notempty(tableau[sour])) { 1065 if (tabok(bottom[sour], des)) { 1066 tabprint(sour, des); 1067 temp = bottom[sour]; 1068 bottom[sour] = NIL; 1069 temp->next = tableau[des]; 1070 tableau[des] = tableau[sour]; 1071 tableau[sour] = NIL; 1072 length[des] = length[des] + (length[sour] - (tabrow - 1)); 1073 length[sour] = tabrow - 1; 1074 timesthru = 0; 1075 } else 1076 destinerror(); 1077 } 1078 } 1079 1080 1081 /* functions to see if the card can go onto the foundation */ 1082 bool 1083 rankhigher(cp, let) 1084 struct cardtype *cp; 1085 { 1086 if (found[let]->rank == King) 1087 if (cp->rank == Ace) 1088 return(TRUE); 1089 else 1090 return(FALSE); 1091 else if (cp->rank - 1 == found[let]->rank) 1092 return(TRUE); 1093 else 1094 return(FALSE); 1095 } 1096 1097 samesuit(cp, let) 1098 struct cardtype *cp; 1099 { 1100 if (cp->suit == found[let]->suit) 1101 return (TRUE); 1102 else 1103 return (FALSE); 1104 } 1105 1106 /* procedure to move a card to the correct foundation pile */ 1107 1108 movetofound(cp, source) 1109 struct cardtype **cp; 1110 { 1111 tempbase = 0; 1112 mtfdone = FALSE; 1113 if (notempty(*cp)) { 1114 do { 1115 if (found[tempbase] != NIL) 1116 if (rankhigher(*cp, tempbase) 1117 && samesuit(*cp, tempbase)) { 1118 if (*cp == stock) 1119 mtforigin = stk; 1120 else if (*cp == talon) 1121 mtforigin = tal; 1122 else 1123 mtforigin = tab; 1124 transit(cp, &found[tempbase]); 1125 printcard(pilemap[tempbase], 1126 foundrow, found[tempbase]); 1127 timesthru = 0; 1128 if (mtforigin == stk) { 1129 usedstock(); 1130 printcard(stockcol, stockrow, stock); 1131 } else if (mtforigin == tal) { 1132 usedtalon(); 1133 printcard(taloncol, talonrow, talon); 1134 } else { 1135 removecard(pilemap[source], length[source]); 1136 length[source]--; 1137 } 1138 cardsoff++; 1139 if (infullgame) { 1140 this.wins += valuepercardup; 1141 total.wins += valuepercardup; 1142 } 1143 mtfdone = TRUE; 1144 } else 1145 tempbase++; 1146 else 1147 tempbase++; 1148 } while ((tempbase != 4) && !mtfdone); 1149 if (!mtfdone) 1150 destinerror(); 1151 } 1152 } 1153 1154 1155 /* procedure to get a command */ 1156 1157 getcmd(row, col, cp) 1158 int row, col; 1159 char *cp; 1160 { 1161 char cmd[2], ch; 1162 int i; 1163 1164 i = 0; 1165 move(row, col); 1166 printw("%-24s", cp); 1167 col += 1 + strlen(cp); 1168 move(row, col); 1169 refresh(); 1170 do { 1171 ch = getch() & 0177; 1172 if (ch >= 'A' && ch <= 'Z') 1173 ch += ('a' - 'A'); 1174 if (ch == '\f') { 1175 wrefresh(curscr); 1176 refresh(); 1177 } else if (i >= 2 && ch != _tty.sg_erase && ch != _tty.sg_kill) { 1178 if (ch != '\n' && ch != '\r' && ch != ' ') 1179 write(1, "\007", 1); 1180 } else if (ch == _tty.sg_erase && i > 0) { 1181 printw("\b \b"); 1182 refresh(); 1183 i--; 1184 } else if (ch == _tty.sg_kill && i > 0) { 1185 while (i > 0) { 1186 printw("\b \b"); 1187 i--; 1188 } 1189 refresh(); 1190 } else if (ch == '\032') { /* Control-Z */ 1191 suspend(); 1192 move(row, col + i); 1193 refresh(); 1194 } else if (isprint(ch)) { 1195 cmd[i++] = ch; 1196 addch(ch); 1197 refresh(); 1198 } 1199 } while (ch != '\n' && ch != '\r' && ch != ' '); 1200 srcpile = cmd[0]; 1201 destpile = cmd[1]; 1202 } 1203 1204 /* Suspend the game (shell escape if no process control on system) */ 1205 1206 suspend() 1207 { 1208 #ifndef SIGTSTP 1209 char *sh; 1210 #endif 1211 1212 move(21, 0); 1213 refresh(); 1214 endwin(); 1215 fflush(stdout); 1216 #ifdef SIGTSTP 1217 kill(getpid(), SIGTSTP); 1218 #else 1219 sh = getenv("SHELL"); 1220 if (sh == NULL) 1221 sh = "/bin/sh"; 1222 system(sh); 1223 #endif 1224 raw(); 1225 noecho(); 1226 } 1227 1228 /* procedure to evaluate and make the specific moves */ 1229 1230 movecard() 1231 { 1232 int source, dest; 1233 char osrcpile, odestpile; 1234 1235 done = FALSE; 1236 errmsg = FALSE; 1237 do { 1238 if (talon == NIL && hand != NIL) 1239 movetotalon(); 1240 if (cardsoff == 52) { 1241 refresh(); 1242 srcpile = 'q'; 1243 } else 1244 getcmd(moverow, movecol, "Move:"); 1245 clearmsg(); 1246 if (srcpile >= '1' && srcpile <= '4') 1247 source = (int) (srcpile - '1'); 1248 if (destpile >= '1' && destpile <= '4') 1249 dest = (int) (destpile - '1'); 1250 if (!startedgame && 1251 (srcpile == 't' || srcpile == 's' || srcpile == 'h' || 1252 srcpile == '1' || srcpile == '2' || srcpile == '3' || 1253 srcpile == '4')) { 1254 startedgame = TRUE; 1255 osrcpile = srcpile; 1256 odestpile = destpile; 1257 if (status != BETTINGBOX) 1258 srcpile = 'y'; 1259 else do { 1260 getcmd(moverow, movecol, "Inspect game?"); 1261 } while (srcpile != 'y' && srcpile != 'n'); 1262 if (srcpile == 'n') { 1263 srcpile = 'q'; 1264 } else { 1265 this.inspection += costofinspection; 1266 total.inspection += costofinspection; 1267 srcpile = osrcpile; 1268 destpile = odestpile; 1269 } 1270 } 1271 switch (srcpile) { 1272 case 't': 1273 if (destpile == 'f' || destpile == 'F') 1274 movetofound(&talon, source); 1275 else if (destpile >= '1' && destpile <= '4') 1276 simpletableau(&talon, dest); 1277 else 1278 dumberror(); 1279 break; 1280 case 's': 1281 if (destpile == 'f' || destpile == 'F') 1282 movetofound(&stock, source); 1283 else if (destpile >= '1' && destpile <= '4') 1284 simpletableau(&stock, dest); 1285 else dumberror(); 1286 break; 1287 case 'h': 1288 if (destpile != 't' && destpile != 'T') { 1289 dumberror(); 1290 break; 1291 } 1292 if (infullgame) { 1293 movetotalon(); 1294 break; 1295 } 1296 if (status == BETTINGBOX) { 1297 do { 1298 getcmd(moverow, movecol, 1299 "Buy game?"); 1300 } while (srcpile != 'y' && 1301 srcpile != 'n'); 1302 if (srcpile == 'n') { 1303 showcards(); 1304 done = TRUE; 1305 break; 1306 } 1307 } 1308 infullgame = TRUE; 1309 this.wins += valuepercardup * cardsoff; 1310 total.wins += valuepercardup * cardsoff; 1311 this.game += costofgame; 1312 total.game += costofgame; 1313 movetotalon(); 1314 break; 1315 case 'q': 1316 showcards(); 1317 done = TRUE; 1318 break; 1319 case 'b': 1320 printtopbettingbox(); 1321 printbottombettingbox(); 1322 status = BETTINGBOX; 1323 break; 1324 case 'x': 1325 clearabovemovebox(); 1326 clearbelowmovebox(); 1327 status = NOBOX; 1328 break; 1329 case 'i': 1330 printtopinstructions(); 1331 printbottominstructions(); 1332 status = INSTRUCTIONBOX; 1333 break; 1334 case 'c': 1335 Cflag = !Cflag; 1336 if (Cflag) 1337 showstat(); 1338 else 1339 clearstat(); 1340 break; 1341 case '1': case '2': case '3': case '4': 1342 if (destpile == 'f' || destpile == 'F') 1343 movetofound(&tableau[source], source); 1344 else if (destpile >= '1' && destpile <= '4') 1345 tabtotab(source, dest); 1346 else dumberror(); 1347 break; 1348 default: 1349 dumberror(); 1350 } 1351 fndbase(&stock, stockcol, stockrow); 1352 fndbase(&talon, taloncol, talonrow); 1353 updatebettinginfo(); 1354 } while (!done); 1355 } 1356 1357 char *basicinstructions[] = { 1358 "Here are brief instuctions to the game of Canfield:\n\n", 1359 " If you have never played solitaire before, it is recom-\n", 1360 "mended that you consult a solitaire instruction book. In\n", 1361 "Canfield, tableau cards may be built on each other downward\n", 1362 "in alternate colors. An entire pile must be moved as a unit\n", 1363 "in building. Top cards of the piles are available to be able\n", 1364 "to be played on foundations, but never into empty spaces.\n\n", 1365 " Spaces must be filled from the stock. The top card of\n", 1366 "the stock also is available to be played on foundations or\n", 1367 "built on tableau piles. After the stock is exhausted, ta-\n", 1368 "bleau spaces may be filled from the talon and the player may\n", 1369 "keep them open until he wishes to use them.\n\n", 1370 " Cards are dealt from the hand to the talon by threes\n", 1371 "and this repeats until there are no more cards in the hand\n", 1372 "or the player quits. To have cards dealt onto the talon the\n", 1373 "player types 'ht' for his move. Foundation base cards are\n", 1374 "also automatically moved to the foundation when they become\n", 1375 "available.\n\n", 1376 "push any key when you are finished: ", 1377 0 }; 1378 1379 char *bettinginstructions[] = { 1380 " The rules for betting are somewhat less strict than\n", 1381 "those used in the official version of the game. The initial\n", 1382 "deal costs $13. You may quit at this point or inspect the\n", 1383 "game. Inspection costs $13 and allows you to make as many\n", 1384 "moves as is possible without moving any cards from your hand\n", 1385 "to the talon. (the initial deal places three cards on the\n", 1386 "talon; if all these cards are used, three more are made\n", 1387 "available) Finally, if the game seems interesting, you must\n", 1388 "pay the final installment of $26. At this point you are\n", 1389 "credited at the rate of $5 for each card on the foundation;\n", 1390 "as the game progresses you are credited with $5 for each\n", 1391 "card that is moved to the foundation. Each run through the\n", 1392 "hand after the first costs $5. The card counting feature\n", 1393 "costs $1 for each unknown card that is identified. If the\n", 1394 "information is toggled on, you are only charged for cards\n", 1395 "that became visible since it was last turned on. Thus the\n", 1396 "maximum cost of information is $34. Playing time is charged\n", 1397 "at a rate of $1 per minute.\n\n", 1398 "push any key when you are finished: ", 1399 0 }; 1400 1401 /* 1402 * procedure to printout instructions 1403 */ 1404 1405 instruct() 1406 { 1407 register char **cp; 1408 1409 move(originrow, origincol); 1410 printw("This is the game of solitaire called Canfield. Do\n"); 1411 printw("you want instructions for the game?"); 1412 do { 1413 getcmd(originrow + 3, origincol, "y or n?"); 1414 } while (srcpile != 'y' && srcpile != 'n'); 1415 if (srcpile == 'n') 1416 return; 1417 clear(); 1418 for (cp = basicinstructions; *cp != 0; cp++) 1419 printw(*cp); 1420 refresh(); 1421 getch(); 1422 clear(); 1423 move(originrow, origincol); 1424 printw("Do you want instructions for betting?"); 1425 do { 1426 getcmd(originrow + 2, origincol, "y or n?"); 1427 } while (srcpile != 'y' && srcpile != 'n'); 1428 if (srcpile == 'n') 1429 return; 1430 clear(); 1431 for (cp = bettinginstructions; *cp != 0; cp++) 1432 printw(*cp); 1433 refresh(); 1434 getch(); 1435 } 1436 1437 /* procedure to initialize the game */ 1438 initall() 1439 { 1440 int uid, i; 1441 1442 srandom(getpid()); 1443 time(&acctstart); 1444 initdeck(deck); 1445 uid = getuid(); 1446 if (uid < 0) 1447 return; 1448 dbfd = open("/usr/games/lib/cfscores", 2); 1449 if (dbfd < 0) 1450 return; 1451 i = lseek(dbfd, uid * sizeof(struct betinfo), 0); 1452 if (i < 0) { 1453 close(dbfd); 1454 dbfd = -1; 1455 return; 1456 } 1457 i = read(dbfd, (char *)&total, sizeof(total)); 1458 if (i < 0) { 1459 close(dbfd); 1460 dbfd = -1; 1461 return; 1462 } 1463 lseek(dbfd, uid * sizeof(struct betinfo), 0); 1464 } 1465 1466 /* 1467 * procedure to end the game 1468 */ 1469 bool 1470 finish() 1471 { 1472 int row, col; 1473 1474 if (cardsoff == 52) { 1475 clear(); 1476 refresh(); 1477 move(originrow, origincol); 1478 printw("CONGRATULATIONS!\n"); 1479 printw("You won the game. That is a feat to be proud of.\n"); 1480 move(originrow + 4, origincol); 1481 printw("Wish to play again? "); 1482 row = originrow + 5; 1483 col = origincol; 1484 } else { 1485 move(msgrow, msgcol); 1486 printw("You got %d card", cardsoff); 1487 if (cardsoff > 1) 1488 printw("s"); 1489 printw(" off "); 1490 getcmd(moverow, movecol, "Hit return to continue"); 1491 move(msgrow, msgcol); 1492 printw("Wish to play again? "); 1493 row = moverow; 1494 col = movecol; 1495 } 1496 do { 1497 getcmd(row, col, "y or n?"); 1498 } while (srcpile != 'y' && srcpile != 'n'); 1499 errmsg = TRUE; 1500 clearmsg(); 1501 if (srcpile == 'y') 1502 return (FALSE); 1503 else 1504 return (TRUE); 1505 } 1506 1507 /* 1508 * Field an interrupt. 1509 */ 1510 askquit() 1511 { 1512 move(msgrow, msgcol); 1513 printw("Really wish to quit? "); 1514 do { 1515 getcmd(moverow, movecol, "y or n?"); 1516 } while (srcpile != 'y' && srcpile != 'n'); 1517 clearmsg(); 1518 if (srcpile == 'y') 1519 cleanup(); 1520 signal(SIGINT, askquit); 1521 } 1522 1523 /* 1524 * procedure to clean up and exit 1525 */ 1526 cleanup() 1527 { 1528 1529 total.thinktime += 1; 1530 if (dbfd != -1) { 1531 write(dbfd, (char *)&total, sizeof(total)); 1532 close(dbfd); 1533 } 1534 clear(); 1535 move(22,0); 1536 refresh(); 1537 endwin(); 1538 exit(0); 1539 /* NOTREACHED */ 1540 } 1541 1542 main(argc, argv) 1543 int argc; 1544 char *argv[]; 1545 { 1546 #ifdef MAXLOAD 1547 double vec[3]; 1548 1549 loadav(vec); 1550 if (vec[2] >= MAXLOAD) { 1551 puts("The system load is too high. Try again later."); 1552 exit(0); 1553 } 1554 #endif 1555 signal(SIGINT, askquit); 1556 signal(SIGHUP, cleanup); 1557 signal(SIGTERM, cleanup); 1558 initscr(); 1559 raw(); 1560 noecho(); 1561 initall(); 1562 instruct(); 1563 makeboard(); 1564 for (;;) { 1565 startgame(); 1566 movecard(); 1567 if (finish()) 1568 break; 1569 if (cardsoff == 52) 1570 makeboard(); 1571 else 1572 cleanupboard(); 1573 } 1574 cleanup(); 1575 /* NOTREACHED */ 1576 } 1577