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 #include <time.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 static void askquit(int); 194 static void cleanup(int); 195 static void cleanupboard(void); 196 static void clearabovemovebox(void); 197 static void clearbelowmovebox(void); 198 static void clearmsg(void); 199 static void clearstat(void); 200 static void destinerror(void); 201 static bool diffcolor(struct cardtype *, struct cardtype *); 202 static void dumberror(void); 203 static bool finish(void); 204 static void fndbase(struct cardtype **, int, int); 205 static void getcmd(int, int, const char *); 206 static void initall(void); 207 static void initdeck(struct cardtype **); 208 static void initgame(void); 209 static void instruct(void); 210 static void makeboard(void); 211 static void movebox(void); 212 static void movecard(void); 213 static void movetofound(struct cardtype **, int); 214 static void movetotalon(void); 215 static bool notempty(struct cardtype *); 216 static void printbottombettingbox(void); 217 static void printbottominstructions(void); 218 static void printcard(int, int, struct cardtype *); 219 static void printrank(int, int, struct cardtype *, bool); 220 static void printtopbettingbox(void); 221 static void printtopinstructions(void); 222 static bool rankhigher(struct cardtype *, int); 223 static bool ranklower(struct cardtype *, struct cardtype *); 224 static void removecard(int, int); 225 static bool samesuit(struct cardtype *, int); 226 static void showcards(void); 227 static void showstat(void); 228 static void shuffle(struct cardtype **); 229 static void simpletableau(struct cardtype **, int); 230 static void startgame(void); 231 static void suspend(void); 232 static bool tabok(struct cardtype *, int); 233 static void tabprint(int, int); 234 static void tabtotab(int, int); 235 static void transit(struct cardtype **, struct cardtype **); 236 static void updatebettinginfo(void); 237 static void usedstock(void); 238 static 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 static void 248 movebox(void) 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 static void 283 printtopinstructions(void) 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 static void 317 printtopbettingbox(void) 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 static void 352 clearabovemovebox(void) 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 static void 368 printbottominstructions(void) 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 static void 382 printbottombettingbox(void) 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 static void 396 clearbelowmovebox(void) 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 static void 412 makeboard(void) 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 static void 451 cleanupboard(void) 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 static void 508 initdeck(struct cardtype **ldeck) 509 { 510 int i; 511 int scnt; 512 char s; 513 int r; 514 515 i = 0; 516 for (scnt=0; scnt<4; scnt++) { 517 s = suitmap[scnt]; 518 for (r=Ace; r<=King; r++) { 519 ldeck[i] = &cards[i]; 520 cards[i].rank = r; 521 cards[i].suit = s; 522 cards[i].color = colormap[scnt]; 523 cards[i].next = NIL; 524 i++; 525 } 526 } 527 } 528 529 /* 530 * procedure to shuffle the deck 531 */ 532 static void 533 shuffle(struct cardtype **ldeck) 534 { 535 int i,j; 536 struct cardtype *temp; 537 538 for (i=0; i<decksize; i++) { 539 ldeck[i]->visible = FALSE; 540 ldeck[i]->paid = FALSE; 541 } 542 for (i = decksize-1; i>=0; i--) { 543 j = random() % decksize; 544 if (i != j) { 545 temp = ldeck[i]; 546 ldeck[i] = ldeck[j]; 547 ldeck[j] = temp; 548 } 549 } 550 } 551 552 /* 553 * procedure to remove the card from the board 554 */ 555 static void 556 removecard(int a, int b) 557 { 558 move(b, a); 559 printw(" "); 560 } 561 562 /* 563 * procedure to print the cards on the board 564 */ 565 static void 566 printrank(int a, int b, struct cardtype *cp, bool inverse) 567 { 568 move(b, a); 569 if (cp->rank != 10) 570 addch(' '); 571 if (inverse) 572 standout(); 573 switch (cp->rank) { 574 case 2: case 3: case 4: case 5: case 6: case 7: 575 case 8: case 9: case 10: 576 printw("%d", cp->rank); 577 break; 578 case Ace: 579 addch('A'); 580 break; 581 case Jack: 582 addch('J'); 583 break; 584 case Queen: 585 addch('Q'); 586 break; 587 case King: 588 addch('K'); 589 } 590 if (inverse) 591 standend(); 592 } 593 594 /* 595 * procedure to print out a card 596 */ 597 static void 598 printcard(int a, int b, struct cardtype *cp) 599 { 600 if (cp == NIL) 601 removecard(a, b); 602 else if (cp->visible == FALSE) { 603 move(b, a); 604 printw(" ? "); 605 } else { 606 bool inverse = (cp->suit == 'd' || cp->suit == 'h'); 607 608 printrank(a, b, cp, inverse); 609 if (inverse) 610 standout(); 611 addch(cp->suit); 612 if (inverse) 613 standend(); 614 } 615 } 616 617 /* 618 * procedure to move the top card from one location to the top 619 * of another location. The pointers always point to the top 620 * of the piles. 621 */ 622 static void 623 transit(struct cardtype **source, struct cardtype **dest) 624 { 625 struct cardtype *temp; 626 627 temp = *source; 628 *source = (*source)->next; 629 temp->next = *dest; 630 *dest = temp; 631 } 632 633 /* 634 * Procedure to set the cards on the foundation base when available. 635 * Note that it is only called on a foundation pile at the beginning of 636 * the game, so the pile will have exactly one card in it. 637 */ 638 static void 639 fndbase(struct cardtype **cp, int column, int row) 640 { 641 bool nomore; 642 643 if (*cp != NIL) 644 do { 645 if ((*cp)->rank == basecard->rank) { 646 base++; 647 printcard(pilemap[base], foundrow, *cp); 648 if (*cp == tableau[0]) 649 length[0] = length[0] - 1; 650 if (*cp == tableau[1]) 651 length[1] = length[1] - 1; 652 if (*cp == tableau[2]) 653 length[2] = length[2] - 1; 654 if (*cp == tableau[3]) 655 length[3] = length[3] - 1; 656 transit(cp, &found[base]); 657 if (cp == &talon) 658 usedtalon(); 659 if (cp == &stock) 660 usedstock(); 661 if (*cp != NIL) { 662 printcard(column, row, *cp); 663 nomore = FALSE; 664 } else { 665 removecard(column, row); 666 nomore = TRUE; 667 } 668 cardsoff++; 669 if (infullgame) { 670 this.wins += valuepercardup; 671 game.wins += valuepercardup; 672 total.wins += valuepercardup; 673 } 674 } else 675 nomore = TRUE; 676 } while (nomore == FALSE); 677 } 678 679 /* 680 * procedure to initialize the things necessary for the game 681 */ 682 static void 683 initgame(void) 684 { 685 int i; 686 687 for (i=0; i<18; i++) { 688 deck[i]->visible = TRUE; 689 deck[i]->paid = TRUE; 690 } 691 stockcnt = 13; 692 stock = deck[12]; 693 for (i=12; i>=1; i--) 694 deck[i]->next = deck[i - 1]; 695 deck[0]->next = NIL; 696 found[0] = deck[13]; 697 deck[13]->next = NIL; 698 for (i=1; i<4; i++) 699 found[i] = NIL; 700 basecard = found[0]; 701 for (i=14; i<18; i++) { 702 tableau[i - 14] = deck[i]; 703 deck[i]->next = NIL; 704 } 705 for (i=0; i<4; i++) { 706 bottom[i] = tableau[i]; 707 length[i] = tabrow; 708 } 709 hand = deck[18]; 710 for (i=18; i<decksize-1; i++) 711 deck[i]->next = deck[i + 1]; 712 deck[decksize-1]->next = NIL; 713 talon = NIL; 714 base = 0; 715 cinhand = 34; 716 taloncnt = 0; 717 timesthru = 0; 718 cardsoff = 1; 719 coldrow = ctoprow; 720 coldcol = cinitcol; 721 cnewrow = ctoprow; 722 cnewcol = cinitcol + cwidthcol; 723 } 724 725 /* 726 * procedure to print the beginning cards and to start each game 727 */ 728 static void 729 startgame(void) 730 { 731 int j; 732 733 shuffle(deck); 734 initgame(); 735 this.hand = costofhand; 736 game.hand += costofhand; 737 total.hand += costofhand; 738 this.inspection = 0; 739 this.game = 0; 740 this.runs = 0; 741 this.information = 0; 742 this.wins = 0; 743 this.thinktime = 0; 744 infullgame = FALSE; 745 startedgame = FALSE; 746 printcard(foundcol, foundrow, found[0]); 747 printcard(stockcol, stockrow, stock); 748 printcard(atabcol, tabrow, tableau[0]); 749 printcard(btabcol, tabrow, tableau[1]); 750 printcard(ctabcol, tabrow, tableau[2]); 751 printcard(dtabcol, tabrow, tableau[3]); 752 printcard(taloncol, talonrow, talon); 753 move(foundrow - 2, basecol); 754 printw("Base"); 755 move(foundrow - 1, basecol); 756 printw("Rank"); 757 printrank(basecol, foundrow, found[0], 0); 758 for (j=0; j<=3; j++) 759 fndbase(&tableau[j], pilemap[j], tabrow); 760 fndbase(&stock, stockcol, stockrow); 761 showstat(); /* show card counting info to cheaters */ 762 movetotalon(); 763 updatebettinginfo(); 764 } 765 766 /* 767 * procedure to clear the message printed from an error 768 */ 769 static void 770 clearmsg(void) 771 { 772 int i; 773 774 if (errmsg == TRUE) { 775 errmsg = FALSE; 776 move(msgrow, msgcol); 777 for (i=0; i<25; i++) 778 addch(' '); 779 refresh(); 780 } 781 } 782 783 /* 784 * procedure to print an error message if the move is not listed 785 */ 786 static void 787 dumberror(void) 788 { 789 errmsg = TRUE; 790 move(msgrow, msgcol); 791 printw("Not a proper move "); 792 } 793 794 /* 795 * procedure to print an error message if the move is not possible 796 */ 797 static void 798 destinerror(void) 799 { 800 errmsg = TRUE; 801 move(msgrow, msgcol); 802 printw("Error: Can't move there"); 803 } 804 805 /* 806 * function to see if the source has cards in it 807 */ 808 static bool 809 notempty(struct cardtype *cp) 810 { 811 if (cp == NIL) { 812 errmsg = TRUE; 813 move(msgrow, msgcol); 814 printw("Error: no cards to move"); 815 return (FALSE); 816 } else 817 return (TRUE); 818 } 819 820 /* 821 * function to see if the rank of one card is less than another 822 */ 823 static bool 824 ranklower(struct cardtype *cp1, struct cardtype *cp2) 825 { 826 if (cp2->rank == Ace) 827 if (cp1->rank == King) 828 return (TRUE); 829 else 830 return (FALSE); 831 else if (cp1->rank + 1 == cp2->rank) 832 return (TRUE); 833 else 834 return (FALSE); 835 } 836 837 /* 838 * function to check the cardcolor for moving to a tableau 839 */ 840 static bool 841 diffcolor(struct cardtype *cp1, struct cardtype *cp2) 842 { 843 if (cp1->color == cp2->color) 844 return (FALSE); 845 else 846 return (TRUE); 847 } 848 849 /* 850 * function to see if the card can move to the tableau 851 */ 852 static bool 853 tabok(struct cardtype *cp, int des) 854 { 855 if ((cp == stock) && (tableau[des] == NIL)) 856 return (TRUE); 857 else if (tableau[des] == NIL) 858 if (stock == NIL && 859 cp != bottom[0] && cp != bottom[1] && 860 cp != bottom[2] && cp != bottom[3]) 861 return (TRUE); 862 else 863 return (FALSE); 864 else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des])) 865 return (TRUE); 866 else 867 return (FALSE); 868 } 869 870 /* 871 * procedure to turn the cards onto the talon from the deck 872 */ 873 static void 874 movetotalon(void) 875 { 876 int i, fin; 877 878 if (cinhand <= 3 && cinhand > 0) { 879 move(msgrow, msgcol); 880 printw("Hand is now empty "); 881 } 882 if (cinhand >= 3) 883 fin = 3; 884 else if (cinhand > 0) 885 fin = cinhand; 886 else if (talon != NIL) { 887 timesthru++; 888 errmsg = TRUE; 889 move(msgrow, msgcol); 890 if (timesthru != 4) { 891 printw("Talon is now the new hand"); 892 this.runs += costofrunthroughhand; 893 game.runs += costofrunthroughhand; 894 total.runs += costofrunthroughhand; 895 while (talon != NIL) { 896 transit(&talon, &hand); 897 cinhand++; 898 } 899 if (cinhand >= 3) 900 fin = 3; 901 else 902 fin = cinhand; 903 taloncnt = 0; 904 coldrow = ctoprow; 905 coldcol = cinitcol; 906 cnewrow = ctoprow; 907 cnewcol = cinitcol + cwidthcol; 908 clearstat(); 909 showstat(); 910 } else { 911 fin = 0; 912 done = TRUE; 913 printw("I believe you have lost"); 914 refresh(); 915 sleep(5); 916 } 917 } else { 918 errmsg = TRUE; 919 move(msgrow, msgcol); 920 printw("Talon and hand are empty"); 921 fin = 0; 922 } 923 for (i=0; i<fin; i++) { 924 transit(&hand, &talon); 925 INCRHAND(cnewrow, cnewcol); 926 INCRHAND(coldrow, coldcol); 927 removecard(cnewcol, cnewrow); 928 if (i == fin - 1) 929 talon->visible = TRUE; 930 if (Cflag) { 931 if (talon->paid == FALSE && talon->visible == TRUE) { 932 this.information += costofinformation; 933 game.information += costofinformation; 934 total.information += costofinformation; 935 talon->paid = TRUE; 936 } 937 printcard(coldcol, coldrow, talon); 938 } 939 } 940 if (fin != 0) { 941 printcard(taloncol, talonrow, talon); 942 cinhand -= fin; 943 taloncnt += fin; 944 if (Cflag) { 945 move(handstatrow, handstatcol); 946 printw("%3d", cinhand); 947 move(talonstatrow, talonstatcol); 948 printw("%3d", taloncnt); 949 } 950 fndbase(&talon, taloncol, talonrow); 951 } 952 } 953 954 955 /* 956 * procedure to print card counting info on screen 957 */ 958 static void 959 showstat(void) 960 { 961 int row, col; 962 struct cardtype *ptr; 963 964 if (!Cflag) 965 return; 966 move(talonstatrow, talonstatcol - 7); 967 printw("Talon: %3d", taloncnt); 968 move(handstatrow, handstatcol - 7); 969 printw("Hand: %3d", cinhand); 970 move(stockstatrow, stockstatcol - 7); 971 printw("Stock: %3d", stockcnt); 972 for ( row = coldrow, col = coldcol, ptr = talon; 973 ptr != NIL; 974 ptr = ptr->next ) { 975 if (ptr->paid == FALSE && ptr->visible == TRUE) { 976 ptr->paid = TRUE; 977 this.information += costofinformation; 978 game.information += costofinformation; 979 total.information += costofinformation; 980 } 981 printcard(col, row, ptr); 982 DECRHAND(row, col); 983 } 984 for ( row = cnewrow, col = cnewcol, ptr = hand; 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 INCRHAND(row, col); 994 printcard(col, row, ptr); 995 } 996 } 997 998 /* 999 * procedure to clear card counting info from screen 1000 */ 1001 static void 1002 clearstat(void) 1003 { 1004 int row; 1005 1006 move(talonstatrow, talonstatcol - 7); 1007 printw(" "); 1008 move(handstatrow, handstatcol - 7); 1009 printw(" "); 1010 move(stockstatrow, stockstatcol - 7); 1011 printw(" "); 1012 for ( row = ctoprow ; row <= cbotrow ; row++ ) { 1013 move(row, cinitcol); 1014 printw("%56s", " "); 1015 } 1016 } 1017 1018 /* 1019 * procedure to update card counting base 1020 */ 1021 static void 1022 usedtalon(void) 1023 { 1024 removecard(coldcol, coldrow); 1025 DECRHAND(coldrow, coldcol); 1026 if (talon != NIL && (talon->visible == FALSE)) { 1027 talon->visible = TRUE; 1028 if (Cflag) { 1029 this.information += costofinformation; 1030 game.information += costofinformation; 1031 total.information += costofinformation; 1032 talon->paid = TRUE; 1033 printcard(coldcol, coldrow, talon); 1034 } 1035 } 1036 taloncnt--; 1037 if (Cflag) { 1038 move(talonstatrow, talonstatcol); 1039 printw("%3d", taloncnt); 1040 } 1041 } 1042 1043 /* 1044 * procedure to update stock card counting base 1045 */ 1046 static void 1047 usedstock(void) 1048 { 1049 stockcnt--; 1050 if (Cflag) { 1051 move(stockstatrow, stockstatcol); 1052 printw("%3d", stockcnt); 1053 } 1054 } 1055 1056 /* 1057 * let 'em know how they lost! 1058 */ 1059 static void 1060 showcards(void) 1061 { 1062 struct cardtype *ptr; 1063 int row; 1064 1065 if (!Cflag || cardsoff == 52) 1066 return; 1067 for (ptr = talon; ptr != NIL; ptr = ptr->next) { 1068 ptr->visible = TRUE; 1069 ptr->paid = TRUE; 1070 } 1071 for (ptr = hand; ptr != NIL; ptr = ptr->next) { 1072 ptr->visible = TRUE; 1073 ptr->paid = TRUE; 1074 } 1075 showstat(); 1076 move(stockrow + 1, sidecol); 1077 printw(" "); 1078 move(talonrow - 2, sidecol); 1079 printw(" "); 1080 move(talonrow - 1, sidecol); 1081 printw(" "); 1082 move(talonrow, sidecol); 1083 printw(" "); 1084 move(talonrow + 1, sidecol); 1085 printw(" "); 1086 for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) { 1087 move(row, stockcol - 1); 1088 printw("| |"); 1089 printcard(stockcol, row, ptr); 1090 } 1091 if (stock == NIL) { 1092 move(row, stockcol - 1); 1093 printw("| |"); 1094 row++; 1095 } 1096 move(handstatrow, handstatcol - 7); 1097 printw(" "); 1098 move(row, stockcol - 1); 1099 printw("=---="); 1100 if ( cardsoff == 52 ) 1101 getcmd(moverow, movecol, "Hit return to exit"); 1102 } 1103 1104 /* 1105 * procedure to update the betting values 1106 */ 1107 static void 1108 updatebettinginfo(void) 1109 { 1110 long thiscosts, gamecosts, totalcosts; 1111 double thisreturn, gamereturn, totalreturn; 1112 time_t now; 1113 long dollars; 1114 1115 time(&now); 1116 dollars = (now - acctstart) / secondsperdollar; 1117 if (dollars > 0) { 1118 acctstart += dollars * secondsperdollar; 1119 if (dollars > maxtimecharge) 1120 dollars = maxtimecharge; 1121 this.thinktime += dollars; 1122 game.thinktime += dollars; 1123 total.thinktime += dollars; 1124 } 1125 thiscosts = this.hand + this.inspection + this.game + 1126 this.runs + this.information + this.thinktime; 1127 gamecosts = game.hand + game.inspection + game.game + 1128 game.runs + game.information + game.thinktime; 1129 totalcosts = total.hand + total.inspection + total.game + 1130 total.runs + total.information + total.thinktime; 1131 this.worth = this.wins - thiscosts; 1132 game.worth = game.wins - gamecosts; 1133 total.worth = total.wins - totalcosts; 1134 thisreturn = ((double)this.wins / (double)thiscosts - 1.0) * 100.0; 1135 gamereturn = ((double)game.wins / (double)gamecosts - 1.0) * 100.0; 1136 totalreturn = ((double)total.wins / (double)totalcosts - 1.0) * 100.0; 1137 if (status != BETTINGBOX) 1138 return; 1139 move(tboxrow + 2, boxcol + 13); 1140 printw("%4d%8d%9d", this.hand, game.hand, total.hand); 1141 move(tboxrow + 3, boxcol + 13); 1142 printw("%4d%8d%9d", this.inspection, game.inspection, total.inspection); 1143 move(tboxrow + 4, boxcol + 13); 1144 printw("%4d%8d%9d", this.game, game.game, total.game); 1145 move(tboxrow + 5, boxcol + 13); 1146 printw("%4d%8d%9d", this.runs, game.runs, total.runs); 1147 move(tboxrow + 6, boxcol + 13); 1148 printw("%4d%8d%9d", this.information, game.information, 1149 total.information); 1150 move(tboxrow + 7, boxcol + 13); 1151 printw("%4d%8d%9d", this.thinktime, game.thinktime, total.thinktime); 1152 move(tboxrow + 8, boxcol + 13); 1153 printw("%4d%8d%9d", thiscosts, gamecosts, totalcosts); 1154 move(tboxrow + 9, boxcol + 13); 1155 printw("%4d%8d%9d", this.wins, game.wins, total.wins); 1156 move(tboxrow + 10, boxcol + 13); 1157 printw("%4d%8d%9d", this.worth, game.worth, total.worth); 1158 move(tboxrow + 11, boxcol + 13); 1159 printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn, gamereturn, totalreturn); 1160 } 1161 1162 /* 1163 * procedure to move a card from the stock or talon to the tableau 1164 */ 1165 static void 1166 simpletableau(struct cardtype **cp, int des) 1167 { 1168 int origin; 1169 1170 if (notempty(*cp)) { 1171 if (tabok(*cp, des)) { 1172 if (*cp == stock) 1173 origin = stk; 1174 else 1175 origin = tal; 1176 if (tableau[des] == NIL) 1177 bottom[des] = *cp; 1178 transit(cp, &tableau[des]); 1179 length[des]++; 1180 printcard(pilemap[des], length[des], tableau[des]); 1181 timesthru = 0; 1182 if (origin == stk) { 1183 usedstock(); 1184 printcard(stockcol, stockrow, stock); 1185 } else { 1186 usedtalon(); 1187 printcard(taloncol, talonrow, talon); 1188 } 1189 } else 1190 destinerror(); 1191 } 1192 } 1193 1194 /* 1195 * print the tableau 1196 */ 1197 static void 1198 tabprint(int sour, int des) 1199 { 1200 int dlength, slength, i; 1201 struct cardtype *tempcard; 1202 1203 for (i=tabrow; i<=length[sour]; i++) 1204 removecard(pilemap[sour], i); 1205 dlength = length[des] + 1; 1206 slength = length[sour]; 1207 if (slength == tabrow) 1208 printcard(pilemap[des], dlength, tableau[sour]); 1209 else 1210 while (slength != tabrow - 1) { 1211 tempcard = tableau[sour]; 1212 for (i=1; i<=slength-tabrow; i++) 1213 tempcard = tempcard->next; 1214 printcard(pilemap[des], dlength, tempcard); 1215 slength--; 1216 dlength++; 1217 } 1218 } 1219 1220 /* 1221 * procedure to move from the tableau to the tableau 1222 */ 1223 static void 1224 tabtotab(int sour, int des) 1225 { 1226 struct cardtype *temp; 1227 1228 if (notempty(tableau[sour])) { 1229 if (tabok(bottom[sour], des)) { 1230 tabprint(sour, des); 1231 temp = bottom[sour]; 1232 bottom[sour] = NIL; 1233 if (bottom[des] == NIL) 1234 bottom[des] = temp; 1235 temp->next = tableau[des]; 1236 tableau[des] = tableau[sour]; 1237 tableau[sour] = NIL; 1238 length[des] = length[des] + (length[sour] - (tabrow - 1)); 1239 length[sour] = tabrow - 1; 1240 timesthru = 0; 1241 } else 1242 destinerror(); 1243 } 1244 } 1245 1246 /* 1247 * functions to see if the card can go onto the foundation 1248 */ 1249 static bool 1250 rankhigher(struct cardtype *cp, int let) 1251 { 1252 if (found[let]->rank == King) 1253 if (cp->rank == Ace) 1254 return(TRUE); 1255 else 1256 return(FALSE); 1257 else if (cp->rank - 1 == found[let]->rank) 1258 return(TRUE); 1259 else 1260 return(FALSE); 1261 } 1262 1263 /* 1264 * function to determine if two cards are the same suit 1265 */ 1266 static bool 1267 samesuit(struct cardtype *cp, int let) 1268 { 1269 if (cp->suit == found[let]->suit) 1270 return (TRUE); 1271 else 1272 return (FALSE); 1273 } 1274 1275 /* 1276 * procedure to move a card to the correct foundation pile 1277 */ 1278 static void 1279 movetofound(struct cardtype **cp, int source) 1280 { 1281 tempbase = 0; 1282 mtfdone = FALSE; 1283 if (notempty(*cp)) { 1284 do { 1285 if (found[tempbase] != NIL) 1286 if (rankhigher(*cp, tempbase) 1287 && samesuit(*cp, tempbase)) { 1288 if (*cp == stock) 1289 mtforigin = stk; 1290 else if (*cp == talon) 1291 mtforigin = tal; 1292 else 1293 mtforigin = tab; 1294 transit(cp, &found[tempbase]); 1295 printcard(pilemap[tempbase], 1296 foundrow, found[tempbase]); 1297 timesthru = 0; 1298 if (mtforigin == stk) { 1299 usedstock(); 1300 printcard(stockcol, stockrow, stock); 1301 } else if (mtforigin == tal) { 1302 usedtalon(); 1303 printcard(taloncol, talonrow, talon); 1304 } else { 1305 removecard(pilemap[source], length[source]); 1306 length[source]--; 1307 } 1308 cardsoff++; 1309 if (infullgame) { 1310 this.wins += valuepercardup; 1311 game.wins += valuepercardup; 1312 total.wins += valuepercardup; 1313 } 1314 mtfdone = TRUE; 1315 } else 1316 tempbase++; 1317 else 1318 tempbase++; 1319 } while ((tempbase != 4) && !mtfdone); 1320 if (!mtfdone) 1321 destinerror(); 1322 } 1323 } 1324 1325 /* 1326 * procedure to get a command 1327 */ 1328 static void 1329 getcmd(int row, int col, const char *cp) 1330 { 1331 char cmd[2], ch; 1332 int i; 1333 1334 i = 0; 1335 move(row, col); 1336 printw("%-24s", cp); 1337 col += 1 + strlen(cp); 1338 move(row, col); 1339 refresh(); 1340 do { 1341 ch = getch() & 0177; 1342 if (ch >= 'A' && ch <= 'Z') 1343 ch += ('a' - 'A'); 1344 if (ch == '\f') { 1345 wrefresh(curscr); 1346 refresh(); 1347 } else if (i >= 2 && ch != erasechar() && ch != killchar()) { 1348 if (ch != '\n' && ch != '\r' && ch != ' ') 1349 write(1, "\007", 1); 1350 } else if (ch == erasechar() && i > 0) { 1351 printw("\b \b"); 1352 refresh(); 1353 i--; 1354 } else if (ch == killchar() && i > 0) { 1355 while (i > 0) { 1356 printw("\b \b"); 1357 i--; 1358 } 1359 refresh(); 1360 } else if (ch == '\032') { /* Control-Z */ 1361 suspend(); 1362 move(row, col + i); 1363 refresh(); 1364 } else if (isprint(ch)) { 1365 cmd[i++] = ch; 1366 addch(ch); 1367 refresh(); 1368 } 1369 } while (ch != '\n' && ch != '\r' && ch != ' '); 1370 srcpile = cmd[0]; 1371 destpile = cmd[1]; 1372 } 1373 1374 /* 1375 * Suspend the game (shell escape if no process control on system) 1376 */ 1377 static void 1378 suspend(void) 1379 { 1380 #ifndef SIGTSTP 1381 char *sh; 1382 #endif 1383 1384 updatebettinginfo(); 1385 move(21, 0); 1386 refresh(); 1387 if (dbfd != -1) { 1388 lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET); 1389 write(dbfd, (char *)&total, sizeof(total)); 1390 } 1391 kill(getpid(), SIGTSTP); 1392 raw(); 1393 noecho(); 1394 } 1395 1396 /* 1397 * procedure to evaluate and make the specific moves 1398 */ 1399 static void 1400 movecard(void) 1401 { 1402 int source, dest; 1403 char osrcpile, odestpile; 1404 1405 source = 0; 1406 dest = 0; 1407 done = FALSE; 1408 errmsg = FALSE; 1409 do { 1410 if (talon == NIL && hand != NIL) 1411 movetotalon(); 1412 if (cardsoff == 52) { 1413 refresh(); 1414 srcpile = 'q'; 1415 } else if (!startedgame) { 1416 move(msgrow, msgcol); 1417 errmsg = TRUE; 1418 switch (34 - taloncnt - cinhand) { 1419 default: 1420 errmsg = FALSE; 1421 break; 1422 case 1: 1423 printw("One card used from talon "); 1424 break; 1425 case 2: 1426 printw("Two cards used from talon "); 1427 break; 1428 case 3: 1429 printw(">3< cards used from talon "); 1430 break; 1431 } 1432 getcmd(moverow, movecol, "Move:"); 1433 } else 1434 getcmd(moverow, movecol, "Move:"); 1435 clearmsg(); 1436 if (srcpile >= '1' && srcpile <= '4') 1437 source = (int) (srcpile - '1'); 1438 if (destpile >= '1' && destpile <= '4') 1439 dest = (int) (destpile - '1'); 1440 if (!startedgame && 1441 (srcpile == 't' || srcpile == 's' || srcpile == 'h' || 1442 srcpile == '1' || srcpile == '2' || srcpile == '3' || 1443 srcpile == '4')) { 1444 startedgame = TRUE; 1445 osrcpile = srcpile; 1446 odestpile = destpile; 1447 if (status != BETTINGBOX) 1448 srcpile = 'y'; 1449 else do { 1450 getcmd(moverow, movecol, "Inspect game?"); 1451 } while (srcpile != 'y' && srcpile != 'n'); 1452 if (srcpile == 'n') { 1453 srcpile = 'q'; 1454 } else { 1455 this.inspection += costofinspection; 1456 game.inspection += costofinspection; 1457 total.inspection += costofinspection; 1458 srcpile = osrcpile; 1459 destpile = odestpile; 1460 } 1461 } 1462 switch (srcpile) { 1463 case 't': 1464 if (destpile == 'f' || destpile == 'F') 1465 movetofound(&talon, source); 1466 else if (destpile >= '1' && destpile <= '4') 1467 simpletableau(&talon, dest); 1468 else 1469 dumberror(); 1470 break; 1471 case 's': 1472 if (destpile == 'f' || destpile == 'F') 1473 movetofound(&stock, source); 1474 else if (destpile >= '1' && destpile <= '4') 1475 simpletableau(&stock, dest); 1476 else dumberror(); 1477 break; 1478 case 'h': 1479 if (destpile != 't' && destpile != 'T') { 1480 dumberror(); 1481 break; 1482 } 1483 if (infullgame) { 1484 movetotalon(); 1485 break; 1486 } 1487 if (status == BETTINGBOX) { 1488 do { 1489 getcmd(moverow, movecol, 1490 "Buy game?"); 1491 } while (srcpile != 'y' && 1492 srcpile != 'n'); 1493 if (srcpile == 'n') { 1494 showcards(); 1495 done = TRUE; 1496 break; 1497 } 1498 } 1499 infullgame = TRUE; 1500 this.wins += valuepercardup * cardsoff; 1501 game.wins += valuepercardup * cardsoff; 1502 total.wins += valuepercardup * cardsoff; 1503 this.game += costofgame; 1504 game.game += costofgame; 1505 total.game += costofgame; 1506 movetotalon(); 1507 break; 1508 case 'q': 1509 showcards(); 1510 done = TRUE; 1511 break; 1512 case 'b': 1513 printtopbettingbox(); 1514 printbottombettingbox(); 1515 status = BETTINGBOX; 1516 break; 1517 case 'x': 1518 clearabovemovebox(); 1519 clearbelowmovebox(); 1520 status = NOBOX; 1521 break; 1522 case 'i': 1523 printtopinstructions(); 1524 printbottominstructions(); 1525 status = INSTRUCTIONBOX; 1526 break; 1527 case 'c': 1528 Cflag = !Cflag; 1529 if (Cflag) 1530 showstat(); 1531 else 1532 clearstat(); 1533 break; 1534 case '1': case '2': case '3': case '4': 1535 if (destpile == 'f' || destpile == 'F') 1536 movetofound(&tableau[source], source); 1537 else if (destpile >= '1' && destpile <= '4') 1538 tabtotab(source, dest); 1539 else dumberror(); 1540 break; 1541 default: 1542 dumberror(); 1543 } 1544 fndbase(&stock, stockcol, stockrow); 1545 fndbase(&talon, taloncol, talonrow); 1546 updatebettinginfo(); 1547 } while (!done); 1548 } 1549 1550 const char *const basicinstructions[] = { 1551 "Here are brief instuctions to the game of Canfield:\n\n", 1552 " If you have never played solitaire before, it is recom-\n", 1553 "mended that you consult a solitaire instruction book. In\n", 1554 "Canfield, tableau cards may be built on each other downward\n", 1555 "in alternate colors. An entire pile must be moved as a unit\n", 1556 "in building. Top cards of the piles are available to be able\n", 1557 "to be played on foundations, but never into empty spaces.\n\n", 1558 " Spaces must be filled from the stock. The top card of\n", 1559 "the stock also is available to be played on foundations or\n", 1560 "built on tableau piles. After the stock is exhausted, ta-\n", 1561 "bleau spaces may be filled from the talon and the player may\n", 1562 "keep them open until he wishes to use them.\n\n", 1563 " Cards are dealt from the hand to the talon by threes\n", 1564 "and this repeats until there are no more cards in the hand\n", 1565 "or the player quits. To have cards dealt onto the talon the\n", 1566 "player types 'ht' for his move. Foundation base cards are\n", 1567 "also automatically moved to the foundation when they become\n", 1568 "available.\n\n", 1569 "push any key when you are finished: ", 1570 0 }; 1571 1572 const char *const bettinginstructions[] = { 1573 " The rules for betting are somewhat less strict than\n", 1574 "those used in the official version of the game. The initial\n", 1575 "deal costs $13. You may quit at this point or inspect the\n", 1576 "game. Inspection costs $13 and allows you to make as many\n", 1577 "moves as is possible without moving any cards from your hand\n", 1578 "to the talon. (the initial deal places three cards on the\n", 1579 "talon; if all these cards are used, three more are made\n", 1580 "available) Finally, if the game seems interesting, you must\n", 1581 "pay the final installment of $26. At this point you are\n", 1582 "credited at the rate of $5 for each card on the foundation;\n", 1583 "as the game progresses you are credited with $5 for each\n", 1584 "card that is moved to the foundation. Each run through the\n", 1585 "hand after the first costs $5. The card counting feature\n", 1586 "costs $1 for each unknown card that is identified. If the\n", 1587 "information is toggled on, you are only charged for cards\n", 1588 "that became visible since it was last turned on. Thus the\n", 1589 "maximum cost of information is $34. Playing time is charged\n", 1590 "at a rate of $1 per minute.\n\n", 1591 "push any key when you are finished: ", 1592 0 }; 1593 1594 /* 1595 * procedure to printout instructions 1596 */ 1597 static void 1598 instruct(void) 1599 { 1600 const char *const *cp; 1601 1602 move(originrow, origincol); 1603 printw("This is the game of solitaire called Canfield. Do\n"); 1604 printw("you want instructions for the game?"); 1605 do { 1606 getcmd(originrow + 3, origincol, "y or n?"); 1607 } while (srcpile != 'y' && srcpile != 'n'); 1608 if (srcpile == 'n') 1609 return; 1610 clear(); 1611 for (cp = basicinstructions; *cp != 0; cp++) 1612 printw(*cp); 1613 refresh(); 1614 getch(); 1615 clear(); 1616 move(originrow, origincol); 1617 printw("Do you want instructions for betting?"); 1618 do { 1619 getcmd(originrow + 2, origincol, "y or n?"); 1620 } while (srcpile != 'y' && srcpile != 'n'); 1621 if (srcpile == 'n') 1622 return; 1623 clear(); 1624 for (cp = bettinginstructions; *cp != 0; cp++) 1625 printw(*cp); 1626 refresh(); 1627 getch(); 1628 } 1629 1630 /* 1631 * procedure to initialize the game 1632 */ 1633 static void 1634 initall(void) 1635 { 1636 int i; 1637 1638 if (dbfd < 0) 1639 return; 1640 srandomdev(); 1641 time(&acctstart); 1642 initdeck(deck); 1643 uid = getuid(); 1644 1645 i = lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET); 1646 if (i < 0) { 1647 close(dbfd); 1648 dbfd = -1; 1649 return; 1650 } 1651 i = read(dbfd, (char *)&total, sizeof(total)); 1652 if (i < 0) { 1653 close(dbfd); 1654 dbfd = -1; 1655 return; 1656 } 1657 } 1658 1659 /* 1660 * procedure to end the game 1661 */ 1662 static bool 1663 finish(void) 1664 { 1665 int row, col; 1666 1667 if (cardsoff == 52) { 1668 getcmd(moverow, movecol, "Hit return to exit"); 1669 clear(); 1670 refresh(); 1671 move(originrow, origincol); 1672 printw("CONGRATULATIONS!\n"); 1673 printw("You won the game. That is a feat to be proud of.\n"); 1674 row = originrow + 5; 1675 col = origincol; 1676 } else { 1677 move(msgrow, msgcol); 1678 printw("You got %d card", cardsoff); 1679 if (cardsoff > 1) 1680 printw("s"); 1681 printw(" off "); 1682 move(msgrow, msgcol); 1683 row = moverow; 1684 col = movecol; 1685 } 1686 do { 1687 getcmd(row, col, "Play again (y or n)?"); 1688 } while (srcpile != 'y' && srcpile != 'n'); 1689 errmsg = TRUE; 1690 clearmsg(); 1691 if (srcpile == 'y') 1692 return (FALSE); 1693 else 1694 return (TRUE); 1695 } 1696 1697 /* 1698 * procedure to clean up and exit 1699 */ 1700 static void 1701 cleanup(__unused int sig) 1702 { 1703 1704 total.thinktime += 1; 1705 status = NOBOX; 1706 updatebettinginfo(); 1707 if (dbfd != -1) { 1708 lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET); 1709 write(dbfd, (char *)&total, sizeof(total)); 1710 close(dbfd); 1711 } 1712 clear(); 1713 move(22,0); 1714 refresh(); 1715 endwin(); 1716 exit(0); 1717 /* NOTREACHED */ 1718 } 1719 1720 /* 1721 * Field an interrupt. 1722 */ 1723 static void 1724 askquit(__unused int sig) 1725 { 1726 move(msgrow, msgcol); 1727 printw("Really wish to quit? "); 1728 do { 1729 getcmd(moverow, movecol, "y or n?"); 1730 } while (srcpile != 'y' && srcpile != 'n'); 1731 clearmsg(); 1732 if (srcpile == 'y') 1733 cleanup(0); 1734 signal(SIGINT, askquit); 1735 } 1736 1737 /* 1738 * Can you tell that this used to be a Pascal program? 1739 */ 1740 int 1741 main(void) 1742 { 1743 dbfd = open(_PATH_SCORE, O_RDWR); 1744 1745 /* revoke */ 1746 setgid(getgid()); 1747 1748 #ifdef MAXLOAD 1749 double vec[3]; 1750 1751 loadav(vec); 1752 if (vec[2] >= MAXLOAD) { 1753 puts("The system load is too high. Try again later."); 1754 exit(0); 1755 } 1756 #endif 1757 signal(SIGINT, askquit); 1758 signal(SIGHUP, cleanup); 1759 signal(SIGTERM, cleanup); 1760 initscr(); 1761 raw(); 1762 noecho(); 1763 initall(); 1764 1765 instruct(); 1766 makeboard(); 1767 for (;;) { 1768 startgame(); 1769 movecard(); 1770 if (finish()) 1771 break; 1772 if (cardsoff == 52) 1773 makeboard(); 1774 else 1775 cleanupboard(); 1776 } 1777 cleanup(0); 1778 /* NOTREACHED */ 1779 return (EXIT_FAILURE); 1780 } 1781