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