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