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