1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)canfield.c 4.10 08/30/84"; 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 */ 351 cleanupboard() 352 { 353 int cnt, row, col; 354 struct cardtype *ptr; 355 356 if (Cflag) { 357 clearstat(); 358 for(ptr = stock, row = stockrow; 359 ptr != NIL; 360 ptr = ptr->next, row++) { 361 move(row, sidecol); 362 printw(" "); 363 } 364 move(row, sidecol); 365 printw(" "); 366 move(stockrow + 1, sidecol); 367 printw("=---="); 368 move(talonrow - 2, sidecol); 369 printw("talon"); 370 move(talonrow - 1, sidecol); 371 printw("=---="); 372 move(talonrow + 1, sidecol); 373 printw("=---="); 374 } 375 move(stockrow, sidecol); 376 printw("| |"); 377 move(talonrow, sidecol); 378 printw("| |"); 379 move(foundrow, fttlcol); 380 printw("| | | | | | | |"); 381 for (cnt = 0; cnt < 4; cnt++) { 382 switch(cnt) { 383 case 0: 384 col = atabcol; 385 break; 386 case 1: 387 col = btabcol; 388 break; 389 case 2: 390 col = ctabcol; 391 break; 392 case 3: 393 col = dtabcol; 394 break; 395 } 396 for(ptr = tableau[cnt], row = tabrow; 397 ptr != NIL; 398 ptr = ptr->next, row++) 399 removecard(col, row); 400 } 401 } 402 403 /* 404 * procedure to create a deck of cards 405 */ 406 initdeck(deck) 407 struct cardtype *deck[]; 408 { 409 int i; 410 int scnt; 411 char s; 412 int r; 413 414 i = 0; 415 for (scnt=0; scnt<4; scnt++) { 416 s = suitmap[scnt]; 417 for (r=Ace; r<=King; r++) { 418 deck[i] = &cards[i]; 419 cards[i].rank = r; 420 cards[i].suit = s; 421 cards[i].color = colormap[scnt]; 422 cards[i].next = NIL; 423 i++; 424 } 425 } 426 } 427 428 /* 429 * procedure to shuffle the deck 430 */ 431 shuffle(deck) 432 struct cardtype *deck[]; 433 { 434 int i,j; 435 struct cardtype *temp; 436 437 for (i=0; i<decksize; i++) { 438 deck[i]->visible = FALSE; 439 deck[i]->paid = FALSE; 440 } 441 for (i = decksize-1; i>=0; i--) { 442 j = random() % decksize; 443 if (i != j) { 444 temp = deck[i]; 445 deck[i] = deck[j]; 446 deck[j] = temp; 447 } 448 } 449 } 450 451 /* 452 * procedure to remove the card from the board 453 */ 454 removecard(a, b) 455 { 456 move(b, a); 457 printw(" "); 458 } 459 460 /* 461 * procedure to print the cards on the board 462 */ 463 printrank(a, b, cp, inverse) 464 struct cardtype *cp; 465 bool inverse; 466 { 467 move(b, a); 468 if (cp->rank != 10) 469 addch(' '); 470 if (inverse) 471 standout(); 472 switch (cp->rank) { 473 case 2: case 3: case 4: case 5: case 6: case 7: 474 case 8: case 9: case 10: 475 printw("%d", cp->rank); 476 break; 477 case Ace: 478 addch('A'); 479 break; 480 case Jack: 481 addch('J'); 482 break; 483 case Queen: 484 addch('Q'); 485 break; 486 case King: 487 addch('K'); 488 } 489 if (inverse) 490 standend(); 491 } 492 493 /* 494 * procedure to print out a card 495 */ 496 printcard(a, b, cp) 497 int a,b; 498 struct cardtype *cp; 499 { 500 if (cp == NIL) 501 removecard(a, b); 502 else if (cp->visible == FALSE) { 503 move(b, a); 504 printw(" ? "); 505 } else { 506 bool inverse = (cp->suit == 'd' || cp->suit == 'h'); 507 508 printrank(a, b, cp, inverse); 509 if (inverse) 510 standout(); 511 addch(cp->suit); 512 if (inverse) 513 standend(); 514 } 515 } 516 517 /* 518 * procedure to move the top card from one location to the top 519 * of another location. The pointers always point to the top 520 * of the piles. 521 */ 522 transit(source, dest) 523 struct cardtype **source, **dest; 524 { 525 struct cardtype *temp; 526 527 temp = *source; 528 *source = (*source)->next; 529 temp->next = *dest; 530 *dest = temp; 531 } 532 533 /* 534 * Procedure to set the cards on the foundation base when available. 535 * Note that it is only called on a foundation pile at the beginning of 536 * the game, so the pile will have exactly one card in it. 537 */ 538 fndbase(cp, column, row) 539 struct cardtype **cp; 540 { 541 bool nomore; 542 543 if (*cp != NIL) 544 do { 545 if ((*cp)->rank == basecard->rank) { 546 base++; 547 printcard(pilemap[base], foundrow, *cp); 548 if (*cp == tableau[0]) 549 length[0] = length[0] - 1; 550 if (*cp == tableau[1]) 551 length[1] = length[1] - 1; 552 if (*cp == tableau[2]) 553 length[2] = length[2] - 1; 554 if (*cp == tableau[3]) 555 length[3] = length[3] - 1; 556 transit(cp, &found[base]); 557 if (cp == &talon) 558 usedtalon(); 559 if (cp == &stock) 560 usedstock(); 561 if (*cp != NIL) { 562 printcard(column, row, *cp); 563 nomore = FALSE; 564 } else { 565 removecard(column, row); 566 nomore = TRUE; 567 } 568 cardsoff++; 569 if (infullgame) { 570 this.wins += valuepercardup; 571 total.wins += valuepercardup; 572 } 573 } else 574 nomore = TRUE; 575 } while (nomore == FALSE); 576 } 577 578 /* 579 * procedure to initialize the things necessary for the game 580 */ 581 initgame() 582 { 583 register i; 584 585 for (i=0; i<18; i++) { 586 deck[i]->visible = TRUE; 587 deck[i]->paid = TRUE; 588 } 589 stockcnt = 13; 590 stock = deck[12]; 591 for (i=12; i>=1; i--) 592 deck[i]->next = deck[i - 1]; 593 deck[0]->next = NIL; 594 found[0] = deck[13]; 595 deck[13]->next = NIL; 596 for (i=1; i<4; i++) 597 found[i] = NIL; 598 basecard = found[0]; 599 for (i=14; i<18; i++) { 600 tableau[i - 14] = deck[i]; 601 deck[i]->next = NIL; 602 } 603 for (i=0; i<4; i++) { 604 bottom[i] = tableau[i]; 605 length[i] = tabrow; 606 } 607 hand = deck[18]; 608 for (i=18; i<decksize-1; i++) 609 deck[i]->next = deck[i + 1]; 610 deck[decksize-1]->next = NIL; 611 talon = NIL; 612 base = 0; 613 cinhand = 34; 614 taloncnt = 0; 615 timesthru = 0; 616 cardsoff = 1; 617 coldrow = ctoprow; 618 coldcol = cinitcol; 619 cnewrow = ctoprow; 620 cnewcol = cinitcol + cwidthcol; 621 } 622 623 /* 624 * procedure to print the beginning cards and to start each game 625 */ 626 startgame() 627 { 628 register int j; 629 630 shuffle(deck); 631 initgame(); 632 this.hand = costofhand; 633 total.hand += costofhand; 634 this.inspection = 0; 635 this.game = 0; 636 this.runs = 0; 637 this.information = 0; 638 this.wins = 0; 639 this.thinktime = 0; 640 infullgame = FALSE; 641 startedgame = FALSE; 642 printcard(foundcol, foundrow, found[0]); 643 printcard(stockcol, stockrow, stock); 644 printcard(atabcol, tabrow, tableau[0]); 645 printcard(btabcol, tabrow, tableau[1]); 646 printcard(ctabcol, tabrow, tableau[2]); 647 printcard(dtabcol, tabrow, tableau[3]); 648 printcard(taloncol, talonrow, talon); 649 move(foundrow - 2, basecol); 650 printw("Base"); 651 move(foundrow - 1, basecol); 652 printw("Rank"); 653 printrank(basecol, foundrow, found[0], 0); 654 for (j=0; j<=3; j++) 655 fndbase(&tableau[j], pilemap[j], tabrow); 656 fndbase(&stock, stockcol, stockrow); 657 showstat(); /* show card counting info to cheaters */ 658 movetotalon(); 659 updatebettinginfo(); 660 } 661 662 /* 663 * procedure to clear the message printed from an error 664 */ 665 clearmsg() 666 { 667 int i; 668 669 if (errmsg == TRUE) { 670 errmsg = FALSE; 671 move(msgrow, msgcol); 672 for (i=0; i<25; i++) 673 addch(' '); 674 refresh(); 675 } 676 } 677 678 /* 679 * procedure to print an error message if the move is not listed 680 */ 681 dumberror() 682 { 683 errmsg = TRUE; 684 move(msgrow, msgcol); 685 printw("Not a proper move "); 686 } 687 688 /* 689 * procedure to print an error message if the move is not possible 690 */ 691 destinerror() 692 { 693 errmsg = TRUE; 694 move(msgrow, msgcol); 695 printw("Error: Can't move there"); 696 } 697 698 /* 699 * function to see if the source has cards in it 700 */ 701 bool 702 notempty(cp) 703 struct cardtype *cp; 704 { 705 if (cp == NIL) { 706 errmsg = TRUE; 707 move(msgrow, msgcol); 708 printw("Error: no cards to move"); 709 return (FALSE); 710 } else 711 return (TRUE); 712 } 713 714 /* 715 * function to see if the rank of one card is less than another 716 */ 717 bool 718 ranklower(cp1, cp2) 719 struct cardtype *cp1, *cp2; 720 { 721 if (cp2->rank == Ace) 722 if (cp1->rank == King) 723 return (TRUE); 724 else 725 return (FALSE); 726 else if (cp1->rank + 1 == cp2->rank) 727 return (TRUE); 728 else 729 return (FALSE); 730 } 731 732 /* 733 * function to check the cardcolor for moving to a tableau 734 */ 735 bool 736 diffcolor(cp1, cp2) 737 struct cardtype *cp1, *cp2; 738 { 739 if (cp1->color == cp2->color) 740 return (FALSE); 741 else 742 return (TRUE); 743 } 744 745 /* 746 * function to see if the card can move to the tableau 747 */ 748 bool 749 tabok(cp, des) 750 struct cardtype *cp; 751 { 752 if ((cp == stock) && (tableau[des] == NIL)) 753 return (TRUE); 754 else if (tableau[des] == NIL) 755 if (stock == NIL) 756 return (TRUE); 757 else 758 return (FALSE); 759 else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des])) 760 return (TRUE); 761 else 762 return (FALSE); 763 } 764 765 /* 766 * procedure to turn the cards onto the talon from the deck 767 */ 768 movetotalon() 769 { 770 int i, fin; 771 772 if (cinhand >= 3) 773 fin = 3; 774 else if (cinhand > 0) 775 fin = cinhand; 776 else if (talon != NIL) { 777 timesthru++; 778 errmsg = TRUE; 779 move(msgrow, msgcol); 780 if (timesthru != 4) { 781 printw("Talon is now the new hand"); 782 this.runs += costofrunthroughhand; 783 total.runs += costofrunthroughhand; 784 while (talon != NIL) { 785 transit(&talon, &hand); 786 cinhand++; 787 } 788 if (cinhand >= 3) 789 fin = 3; 790 else 791 fin = cinhand; 792 taloncnt = 0; 793 coldrow = ctoprow; 794 coldcol = cinitcol; 795 cnewrow = ctoprow; 796 cnewcol = cinitcol + cwidthcol; 797 clearstat(); 798 showstat(); 799 } else { 800 fin = 0; 801 done = TRUE; 802 printw("I believe you have lost"); 803 refresh(); 804 sleep(5); 805 } 806 } else { 807 errmsg = TRUE; 808 move(msgrow, msgcol); 809 printw("Talon and hand are empty"); 810 fin = 0; 811 } 812 for (i=0; i<fin; i++) { 813 transit(&hand, &talon); 814 INCRHAND(cnewrow, cnewcol); 815 INCRHAND(coldrow, coldcol); 816 removecard(cnewcol, cnewrow); 817 if (i == fin - 1) 818 talon->visible = TRUE; 819 if (Cflag) { 820 if (talon->paid == FALSE && talon->visible == TRUE) { 821 this.information += costofinformation; 822 total.information += costofinformation; 823 talon->paid = TRUE; 824 } 825 printcard(coldcol, coldrow, talon); 826 } 827 } 828 if (fin != 0) { 829 printcard(taloncol, talonrow, talon); 830 cinhand -= fin; 831 taloncnt += fin; 832 if (Cflag) { 833 move(handstatrow, handstatcol); 834 printw("%3d", cinhand); 835 move(talonstatrow, talonstatcol); 836 printw("%3d", taloncnt); 837 } 838 fndbase(&talon, taloncol, talonrow); 839 } 840 } 841 842 843 /* 844 * procedure to print card counting info on screen 845 */ 846 showstat() 847 { 848 int row, col; 849 register struct cardtype *ptr; 850 851 if (!Cflag) 852 return; 853 move(talonstatrow, talonstatcol - 7); 854 printw("Talon: %3d", taloncnt); 855 move(handstatrow, handstatcol - 7); 856 printw("Hand: %3d", cinhand); 857 move(stockstatrow, stockstatcol - 7); 858 printw("Stock: %3d", stockcnt); 859 for ( row = coldrow, col = coldcol, ptr = talon; 860 ptr != NIL; 861 ptr = ptr->next ) { 862 if (ptr->paid == FALSE && ptr->visible == TRUE) { 863 ptr->paid = TRUE; 864 this.information += costofinformation; 865 total.information += costofinformation; 866 } 867 printcard(col, row, ptr); 868 DECRHAND(row, col); 869 } 870 for ( row = cnewrow, col = cnewcol, ptr = hand; 871 ptr != NIL; 872 ptr = ptr->next ) { 873 if (ptr->paid == FALSE && ptr->visible == TRUE) { 874 ptr->paid = TRUE; 875 this.information += costofinformation; 876 total.information += costofinformation; 877 } 878 INCRHAND(row, col); 879 printcard(col, row, ptr); 880 } 881 } 882 883 /* 884 * procedure to clear card counting info from screen 885 */ 886 clearstat() 887 { 888 int row; 889 890 move(talonstatrow, talonstatcol - 7); 891 printw(" "); 892 move(handstatrow, handstatcol - 7); 893 printw(" "); 894 move(stockstatrow, stockstatcol - 7); 895 printw(" "); 896 for ( row = ctoprow ; row <= cbotrow ; row++ ) { 897 move(row, cinitcol); 898 printw("%56s", " "); 899 } 900 } 901 902 /* 903 * procedure to update card counting base 904 */ 905 usedtalon() 906 { 907 removecard(coldcol, coldrow); 908 DECRHAND(coldrow, coldcol); 909 if (talon != NIL && (talon->visible == FALSE)) { 910 talon->visible = TRUE; 911 if (Cflag) { 912 this.information += costofinformation; 913 total.information += costofinformation; 914 talon->paid = TRUE; 915 printcard(coldcol, coldrow, talon); 916 } 917 } 918 taloncnt--; 919 if (Cflag) { 920 move(talonstatrow, talonstatcol); 921 printw("%3d", taloncnt); 922 } 923 } 924 925 /* 926 * procedure to update stock card counting base 927 */ 928 usedstock() 929 { 930 stockcnt--; 931 if (Cflag) { 932 move(stockstatrow, stockstatcol); 933 printw("%3d", stockcnt); 934 } 935 } 936 937 /* 938 * let 'em know how they lost! 939 */ 940 showcards() 941 { 942 register struct cardtype *ptr; 943 int row; 944 945 if (!Cflag) 946 return; 947 for (ptr = talon; ptr != NIL; ptr = ptr->next) { 948 ptr->visible = TRUE; 949 ptr->paid = TRUE; 950 } 951 for (ptr = hand; ptr != NIL; ptr = ptr->next) { 952 ptr->visible = TRUE; 953 ptr->paid = TRUE; 954 } 955 showstat(); 956 move(stockrow + 1, sidecol); 957 printw(" "); 958 move(talonrow - 2, sidecol); 959 printw(" "); 960 move(talonrow - 1, sidecol); 961 printw(" "); 962 move(talonrow, sidecol); 963 printw(" "); 964 move(talonrow + 1, sidecol); 965 printw(" "); 966 for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) { 967 move(row, stockcol - 1); 968 printw("| |"); 969 printcard(stockcol, row, ptr); 970 } 971 if (stock == NIL) { 972 move(row, stockcol - 1); 973 printw("| |"); 974 row++; 975 } 976 move(handstatrow, handstatcol - 7); 977 printw(" "); 978 move(row, stockcol - 1); 979 printw("=---="); 980 getcmd(moverow, movecol, "Hit return to exit"); 981 } 982 983 /* 984 * procedure to update the betting values 985 */ 986 updatebettinginfo() 987 { 988 long thiscosts, totalcosts; 989 time_t now; 990 register long dollars; 991 992 time(&now); 993 dollars = (now - acctstart) / secondsperdollar; 994 if (dollars > 0) { 995 this.thinktime += dollars; 996 total.thinktime += dollars; 997 acctstart += dollars * secondsperdollar; 998 } 999 thiscosts = this.hand + this.inspection + this.game + 1000 this.runs + this.information + this.thinktime; 1001 totalcosts = total.hand + total.inspection + total.game + 1002 total.runs + total.information + total.thinktime; 1003 this.worth = this.wins - thiscosts; 1004 total.worth = total.wins - totalcosts; 1005 if (status != BETTINGBOX) 1006 return; 1007 move(tboxrow + 3, boxcol + 13); 1008 printw("%4d%9d", this.hand, total.hand); 1009 move(tboxrow + 4, boxcol + 13); 1010 printw("%4d%9d", this.inspection, total.inspection); 1011 move(tboxrow + 5, boxcol + 13); 1012 printw("%4d%9d", this.game, total.game); 1013 move(tboxrow + 6, boxcol + 13); 1014 printw("%4d%9d", this.runs, total.runs); 1015 move(tboxrow + 7, boxcol + 13); 1016 printw("%4d%9d", this.information, total.information); 1017 move(tboxrow + 8, boxcol + 13); 1018 printw("%4d%9d", this.thinktime, total.thinktime); 1019 move(tboxrow + 9, boxcol + 13); 1020 printw("%4d%9d", thiscosts, totalcosts); 1021 move(tboxrow + 10, boxcol + 13); 1022 printw("%4d%9d", this.wins, total.wins); 1023 move(tboxrow + 11, boxcol + 13); 1024 printw("%4d%9d", this.worth, total.worth); 1025 } 1026 1027 /* 1028 * procedure to move a card from the stock or talon to the tableau 1029 */ 1030 simpletableau(cp, des) 1031 struct cardtype **cp; 1032 { 1033 int origin; 1034 1035 if (notempty(*cp)) { 1036 if (tabok(*cp, des)) { 1037 if (*cp == stock) 1038 origin = stk; 1039 else 1040 origin = tal; 1041 if (tableau[des] == NIL) 1042 bottom[des] = *cp; 1043 transit(cp, &tableau[des]); 1044 length[des]++; 1045 printcard(pilemap[des], length[des], tableau[des]); 1046 timesthru = 0; 1047 if (origin == stk) { 1048 usedstock(); 1049 printcard(stockcol, stockrow, stock); 1050 } else { 1051 usedtalon(); 1052 printcard(taloncol, talonrow, talon); 1053 } 1054 } else 1055 destinerror(); 1056 } 1057 } 1058 1059 /* 1060 * print the tableau 1061 */ 1062 tabprint(sour, des) 1063 { 1064 int dlength, slength, i; 1065 struct cardtype *tempcard; 1066 1067 for (i=tabrow; i<=length[sour]; i++) 1068 removecard(pilemap[sour], i); 1069 dlength = length[des] + 1; 1070 slength = length[sour]; 1071 if (slength == tabrow) 1072 printcard(pilemap[des], dlength, tableau[sour]); 1073 else 1074 while (slength != tabrow - 1) { 1075 tempcard = tableau[sour]; 1076 for (i=1; i<=slength-tabrow; i++) 1077 tempcard = tempcard->next; 1078 printcard(pilemap[des], dlength, tempcard); 1079 slength--; 1080 dlength++; 1081 } 1082 } 1083 1084 /* 1085 * procedure to move from the tableau to the tableau 1086 */ 1087 tabtotab(sour, des) 1088 register int sour, des; 1089 { 1090 struct cardtype *temp; 1091 1092 if (notempty(tableau[sour])) { 1093 if (tabok(bottom[sour], des)) { 1094 tabprint(sour, des); 1095 temp = bottom[sour]; 1096 bottom[sour] = NIL; 1097 if (bottom[des] == NIL) 1098 bottom[des] = temp; 1099 temp->next = tableau[des]; 1100 tableau[des] = tableau[sour]; 1101 tableau[sour] = NIL; 1102 length[des] = length[des] + (length[sour] - (tabrow - 1)); 1103 length[sour] = tabrow - 1; 1104 timesthru = 0; 1105 } else 1106 destinerror(); 1107 } 1108 } 1109 1110 /* 1111 * functions to see if the card can go onto the foundation 1112 */ 1113 bool 1114 rankhigher(cp, let) 1115 struct cardtype *cp; 1116 { 1117 if (found[let]->rank == King) 1118 if (cp->rank == Ace) 1119 return(TRUE); 1120 else 1121 return(FALSE); 1122 else if (cp->rank - 1 == found[let]->rank) 1123 return(TRUE); 1124 else 1125 return(FALSE); 1126 } 1127 1128 /* 1129 * function to determine if two cards are the same suit 1130 */ 1131 samesuit(cp, let) 1132 struct cardtype *cp; 1133 { 1134 if (cp->suit == found[let]->suit) 1135 return (TRUE); 1136 else 1137 return (FALSE); 1138 } 1139 1140 /* 1141 * procedure to move a card to the correct foundation pile 1142 */ 1143 movetofound(cp, source) 1144 struct cardtype **cp; 1145 { 1146 tempbase = 0; 1147 mtfdone = FALSE; 1148 if (notempty(*cp)) { 1149 do { 1150 if (found[tempbase] != NIL) 1151 if (rankhigher(*cp, tempbase) 1152 && samesuit(*cp, tempbase)) { 1153 if (*cp == stock) 1154 mtforigin = stk; 1155 else if (*cp == talon) 1156 mtforigin = tal; 1157 else 1158 mtforigin = tab; 1159 transit(cp, &found[tempbase]); 1160 printcard(pilemap[tempbase], 1161 foundrow, found[tempbase]); 1162 timesthru = 0; 1163 if (mtforigin == stk) { 1164 usedstock(); 1165 printcard(stockcol, stockrow, stock); 1166 } else if (mtforigin == tal) { 1167 usedtalon(); 1168 printcard(taloncol, talonrow, talon); 1169 } else { 1170 removecard(pilemap[source], length[source]); 1171 length[source]--; 1172 } 1173 cardsoff++; 1174 if (infullgame) { 1175 this.wins += valuepercardup; 1176 total.wins += valuepercardup; 1177 } 1178 mtfdone = TRUE; 1179 } else 1180 tempbase++; 1181 else 1182 tempbase++; 1183 } while ((tempbase != 4) && !mtfdone); 1184 if (!mtfdone) 1185 destinerror(); 1186 } 1187 } 1188 1189 /* 1190 * procedure to get a command 1191 */ 1192 getcmd(row, col, cp) 1193 int row, col; 1194 char *cp; 1195 { 1196 char cmd[2], ch; 1197 int i; 1198 1199 i = 0; 1200 move(row, col); 1201 printw("%-24s", cp); 1202 col += 1 + strlen(cp); 1203 move(row, col); 1204 refresh(); 1205 do { 1206 ch = getch() & 0177; 1207 if (ch >= 'A' && ch <= 'Z') 1208 ch += ('a' - 'A'); 1209 if (ch == '\f') { 1210 wrefresh(curscr); 1211 refresh(); 1212 } else if (i >= 2 && ch != _tty.sg_erase && ch != _tty.sg_kill) { 1213 if (ch != '\n' && ch != '\r' && ch != ' ') 1214 write(1, "\007", 1); 1215 } else if (ch == _tty.sg_erase && i > 0) { 1216 printw("\b \b"); 1217 refresh(); 1218 i--; 1219 } else if (ch == _tty.sg_kill && i > 0) { 1220 while (i > 0) { 1221 printw("\b \b"); 1222 i--; 1223 } 1224 refresh(); 1225 } else if (ch == '\032') { /* Control-Z */ 1226 suspend(); 1227 move(row, col + i); 1228 refresh(); 1229 } else if (isprint(ch)) { 1230 cmd[i++] = ch; 1231 addch(ch); 1232 refresh(); 1233 } 1234 } while (ch != '\n' && ch != '\r' && ch != ' '); 1235 srcpile = cmd[0]; 1236 destpile = cmd[1]; 1237 } 1238 1239 /* 1240 * Suspend the game (shell escape if no process control on system) 1241 */ 1242 suspend() 1243 { 1244 #ifndef SIGTSTP 1245 char *sh; 1246 #endif 1247 1248 move(21, 0); 1249 refresh(); 1250 #ifdef SIGTSTP 1251 kill(getpid(), SIGTSTP); 1252 #else 1253 sh = getenv("SHELL"); 1254 if (sh == NULL) 1255 sh = "/bin/sh"; 1256 system(sh); 1257 #endif 1258 raw(); 1259 noecho(); 1260 } 1261 1262 /* 1263 * procedure to evaluate and make the specific moves 1264 */ 1265 movecard() 1266 { 1267 int source, dest; 1268 char osrcpile, odestpile; 1269 1270 done = FALSE; 1271 errmsg = FALSE; 1272 do { 1273 if (talon == NIL && hand != NIL) 1274 movetotalon(); 1275 if (cardsoff == 52) { 1276 refresh(); 1277 srcpile = 'q'; 1278 } else 1279 getcmd(moverow, movecol, "Move:"); 1280 clearmsg(); 1281 if (srcpile >= '1' && srcpile <= '4') 1282 source = (int) (srcpile - '1'); 1283 if (destpile >= '1' && destpile <= '4') 1284 dest = (int) (destpile - '1'); 1285 if (!startedgame && 1286 (srcpile == 't' || srcpile == 's' || srcpile == 'h' || 1287 srcpile == '1' || srcpile == '2' || srcpile == '3' || 1288 srcpile == '4')) { 1289 startedgame = TRUE; 1290 osrcpile = srcpile; 1291 odestpile = destpile; 1292 if (status != BETTINGBOX) 1293 srcpile = 'y'; 1294 else do { 1295 getcmd(moverow, movecol, "Inspect game?"); 1296 } while (srcpile != 'y' && srcpile != 'n'); 1297 if (srcpile == 'n') { 1298 srcpile = 'q'; 1299 } else { 1300 this.inspection += costofinspection; 1301 total.inspection += costofinspection; 1302 srcpile = osrcpile; 1303 destpile = odestpile; 1304 } 1305 } 1306 switch (srcpile) { 1307 case 't': 1308 if (destpile == 'f' || destpile == 'F') 1309 movetofound(&talon, source); 1310 else if (destpile >= '1' && destpile <= '4') 1311 simpletableau(&talon, dest); 1312 else 1313 dumberror(); 1314 break; 1315 case 's': 1316 if (destpile == 'f' || destpile == 'F') 1317 movetofound(&stock, source); 1318 else if (destpile >= '1' && destpile <= '4') 1319 simpletableau(&stock, dest); 1320 else dumberror(); 1321 break; 1322 case 'h': 1323 if (destpile != 't' && destpile != 'T') { 1324 dumberror(); 1325 break; 1326 } 1327 if (infullgame) { 1328 movetotalon(); 1329 break; 1330 } 1331 if (status == BETTINGBOX) { 1332 do { 1333 getcmd(moverow, movecol, 1334 "Buy game?"); 1335 } while (srcpile != 'y' && 1336 srcpile != 'n'); 1337 if (srcpile == 'n') { 1338 showcards(); 1339 done = TRUE; 1340 break; 1341 } 1342 } 1343 infullgame = TRUE; 1344 this.wins += valuepercardup * cardsoff; 1345 total.wins += valuepercardup * cardsoff; 1346 this.game += costofgame; 1347 total.game += costofgame; 1348 movetotalon(); 1349 break; 1350 case 'q': 1351 showcards(); 1352 done = TRUE; 1353 break; 1354 case 'b': 1355 printtopbettingbox(); 1356 printbottombettingbox(); 1357 status = BETTINGBOX; 1358 break; 1359 case 'x': 1360 clearabovemovebox(); 1361 clearbelowmovebox(); 1362 status = NOBOX; 1363 break; 1364 case 'i': 1365 printtopinstructions(); 1366 printbottominstructions(); 1367 status = INSTRUCTIONBOX; 1368 break; 1369 case 'c': 1370 Cflag = !Cflag; 1371 if (Cflag) 1372 showstat(); 1373 else 1374 clearstat(); 1375 break; 1376 case '1': case '2': case '3': case '4': 1377 if (destpile == 'f' || destpile == 'F') 1378 movetofound(&tableau[source], source); 1379 else if (destpile >= '1' && destpile <= '4') 1380 tabtotab(source, dest); 1381 else dumberror(); 1382 break; 1383 default: 1384 dumberror(); 1385 } 1386 fndbase(&stock, stockcol, stockrow); 1387 fndbase(&talon, taloncol, talonrow); 1388 updatebettinginfo(); 1389 } while (!done); 1390 } 1391 1392 char *basicinstructions[] = { 1393 "Here are brief instuctions to the game of Canfield:\n\n", 1394 " If you have never played solitaire before, it is recom-\n", 1395 "mended that you consult a solitaire instruction book. In\n", 1396 "Canfield, tableau cards may be built on each other downward\n", 1397 "in alternate colors. An entire pile must be moved as a unit\n", 1398 "in building. Top cards of the piles are available to be able\n", 1399 "to be played on foundations, but never into empty spaces.\n\n", 1400 " Spaces must be filled from the stock. The top card of\n", 1401 "the stock also is available to be played on foundations or\n", 1402 "built on tableau piles. After the stock is exhausted, ta-\n", 1403 "bleau spaces may be filled from the talon and the player may\n", 1404 "keep them open until he wishes to use them.\n\n", 1405 " Cards are dealt from the hand to the talon by threes\n", 1406 "and this repeats until there are no more cards in the hand\n", 1407 "or the player quits. To have cards dealt onto the talon the\n", 1408 "player types 'ht' for his move. Foundation base cards are\n", 1409 "also automatically moved to the foundation when they become\n", 1410 "available.\n\n", 1411 "push any key when you are finished: ", 1412 0 }; 1413 1414 char *bettinginstructions[] = { 1415 " The rules for betting are somewhat less strict than\n", 1416 "those used in the official version of the game. The initial\n", 1417 "deal costs $13. You may quit at this point or inspect the\n", 1418 "game. Inspection costs $13 and allows you to make as many\n", 1419 "moves as is possible without moving any cards from your hand\n", 1420 "to the talon. (the initial deal places three cards on the\n", 1421 "talon; if all these cards are used, three more are made\n", 1422 "available) Finally, if the game seems interesting, you must\n", 1423 "pay the final installment of $26. At this point you are\n", 1424 "credited at the rate of $5 for each card on the foundation;\n", 1425 "as the game progresses you are credited with $5 for each\n", 1426 "card that is moved to the foundation. Each run through the\n", 1427 "hand after the first costs $5. The card counting feature\n", 1428 "costs $1 for each unknown card that is identified. If the\n", 1429 "information is toggled on, you are only charged for cards\n", 1430 "that became visible since it was last turned on. Thus the\n", 1431 "maximum cost of information is $34. Playing time is charged\n", 1432 "at a rate of $1 per minute.\n\n", 1433 "push any key when you are finished: ", 1434 0 }; 1435 1436 /* 1437 * procedure to printout instructions 1438 */ 1439 instruct() 1440 { 1441 register char **cp; 1442 1443 move(originrow, origincol); 1444 printw("This is the game of solitaire called Canfield. Do\n"); 1445 printw("you want instructions for the game?"); 1446 do { 1447 getcmd(originrow + 3, origincol, "y or n?"); 1448 } while (srcpile != 'y' && srcpile != 'n'); 1449 if (srcpile == 'n') 1450 return; 1451 clear(); 1452 for (cp = basicinstructions; *cp != 0; cp++) 1453 printw(*cp); 1454 refresh(); 1455 getch(); 1456 clear(); 1457 move(originrow, origincol); 1458 printw("Do you want instructions for betting?"); 1459 do { 1460 getcmd(originrow + 2, origincol, "y or n?"); 1461 } while (srcpile != 'y' && srcpile != 'n'); 1462 if (srcpile == 'n') 1463 return; 1464 clear(); 1465 for (cp = bettinginstructions; *cp != 0; cp++) 1466 printw(*cp); 1467 refresh(); 1468 getch(); 1469 } 1470 1471 /* 1472 * procedure to initialize the game 1473 */ 1474 initall() 1475 { 1476 int uid, i; 1477 1478 srandom(getpid()); 1479 time(&acctstart); 1480 initdeck(deck); 1481 uid = getuid(); 1482 if (uid < 0) 1483 return; 1484 dbfd = open("/usr/games/lib/cfscores", 2); 1485 if (dbfd < 0) 1486 return; 1487 i = lseek(dbfd, uid * sizeof(struct betinfo), 0); 1488 if (i < 0) { 1489 close(dbfd); 1490 dbfd = -1; 1491 return; 1492 } 1493 i = read(dbfd, (char *)&total, sizeof(total)); 1494 if (i < 0) { 1495 close(dbfd); 1496 dbfd = -1; 1497 return; 1498 } 1499 lseek(dbfd, uid * sizeof(struct betinfo), 0); 1500 } 1501 1502 /* 1503 * procedure to end the game 1504 */ 1505 bool 1506 finish() 1507 { 1508 int row, col; 1509 1510 if (cardsoff == 52) { 1511 clear(); 1512 refresh(); 1513 move(originrow, origincol); 1514 printw("CONGRATULATIONS!\n"); 1515 printw("You won the game. That is a feat to be proud of.\n"); 1516 move(originrow + 4, origincol); 1517 printw("Wish to play again? "); 1518 row = originrow + 5; 1519 col = origincol; 1520 } else { 1521 move(msgrow, msgcol); 1522 printw("You got %d card", cardsoff); 1523 if (cardsoff > 1) 1524 printw("s"); 1525 printw(" off "); 1526 getcmd(moverow, movecol, "Hit return to continue"); 1527 move(msgrow, msgcol); 1528 printw("Wish to play again? "); 1529 row = moverow; 1530 col = movecol; 1531 } 1532 do { 1533 getcmd(row, col, "y or n?"); 1534 } while (srcpile != 'y' && srcpile != 'n'); 1535 errmsg = TRUE; 1536 clearmsg(); 1537 if (srcpile == 'y') 1538 return (FALSE); 1539 else 1540 return (TRUE); 1541 } 1542 1543 /* 1544 * Field an interrupt. 1545 */ 1546 askquit() 1547 { 1548 move(msgrow, msgcol); 1549 printw("Really wish to quit? "); 1550 do { 1551 getcmd(moverow, movecol, "y or n?"); 1552 } while (srcpile != 'y' && srcpile != 'n'); 1553 clearmsg(); 1554 if (srcpile == 'y') 1555 cleanup(); 1556 signal(SIGINT, askquit); 1557 } 1558 1559 /* 1560 * procedure to clean up and exit 1561 */ 1562 cleanup() 1563 { 1564 1565 total.thinktime += 1; 1566 status = NOBOX; 1567 updatebettinginfo(); 1568 if (dbfd != -1) { 1569 write(dbfd, (char *)&total, sizeof(total)); 1570 close(dbfd); 1571 } 1572 clear(); 1573 move(22,0); 1574 refresh(); 1575 endwin(); 1576 exit(0); 1577 /* NOTREACHED */ 1578 } 1579 1580 /* 1581 * Can you tell that this used to be a Pascal program? 1582 */ 1583 main(argc, argv) 1584 int argc; 1585 char *argv[]; 1586 { 1587 #ifdef MAXLOAD 1588 double vec[3]; 1589 1590 loadav(vec); 1591 if (vec[2] >= MAXLOAD) { 1592 puts("The system load is too high. Try again later."); 1593 exit(0); 1594 } 1595 #endif 1596 signal(SIGINT, askquit); 1597 signal(SIGHUP, cleanup); 1598 signal(SIGTERM, cleanup); 1599 initscr(); 1600 raw(); 1601 noecho(); 1602 initall(); 1603 instruct(); 1604 makeboard(); 1605 for (;;) { 1606 startgame(); 1607 movecard(); 1608 if (finish()) 1609 break; 1610 if (cardsoff == 52) 1611 makeboard(); 1612 else 1613 cleanupboard(); 1614 } 1615 cleanup(); 1616 /* NOTREACHED */ 1617 } 1618