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