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