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) 1/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: }