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