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. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)canfield.c 8.1 (Berkeley) 5/31/93 31 * $FreeBSD: src/games/canfield/canfield/canfield.c,v 1.11 1999/12/12 07:25:13 billf Exp $ 32 * $DragonFly: src/games/canfield/canfield/canfield.c,v 1.4 2005/11/19 09:50:30 swildner Exp $ 33 */ 34 35 /* 36 * The canfield program 37 * 38 * Authors: 39 * Originally written: Steve Levine 40 * Converted to use curses and debugged: Steve Feldman 41 * Card counting: Kirk McKusick and Mikey Olson 42 * User interface cleanups: Eric Allman and Kirk McKusick 43 * Betting by Kirk McKusick 44 */ 45 46 #include <sys/types.h> 47 48 #include <ctype.h> 49 #include <curses.h> 50 #include <fcntl.h> 51 #include <signal.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <time.h> 55 #include <unistd.h> 56 57 #include "pathnames.h" 58 59 #define decksize 52 60 #define originrow 0 61 #define origincol 0 62 #define basecol 1 63 #define boxcol 42 64 #define tboxrow 2 65 #define bboxrow 17 66 #define movecol 43 67 #define moverow 16 68 #define msgcol 43 69 #define msgrow 15 70 #define titlecol 30 71 #define titlerow 0 72 #define sidecol 1 73 #define ottlrow 6 74 #define foundcol 11 75 #define foundrow 3 76 #define stockcol 2 77 #define stockrow 8 78 #define fttlcol 10 79 #define fttlrow 1 80 #define taloncol 2 81 #define talonrow 13 82 #define tabrow 8 83 #define ctoprow 21 84 #define cbotrow 23 85 #define cinitcol 14 86 #define cheightcol 1 87 #define cwidthcol 4 88 #define handstatrow 21 89 #define handstatcol 7 90 #define talonstatrow 22 91 #define talonstatcol 7 92 #define stockstatrow 23 93 #define stockstatcol 7 94 #define Ace 1 95 #define Jack 11 96 #define Queen 12 97 #define King 13 98 #define atabcol 11 99 #define btabcol 18 100 #define ctabcol 25 101 #define dtabcol 32 102 103 #define spades 's' 104 #define clubs 'c' 105 #define hearts 'h' 106 #define diamonds 'd' 107 #define black 'b' 108 #define red 'r' 109 110 #define stk 1 111 #define tal 2 112 #define tab 3 113 #define INCRHAND(row, col) {\ 114 row -= cheightcol;\ 115 if (row < ctoprow) {\ 116 row = cbotrow;\ 117 col += cwidthcol;\ 118 }\ 119 } 120 #define DECRHAND(row, col) {\ 121 row += cheightcol;\ 122 if (row > cbotrow) {\ 123 row = ctoprow;\ 124 col -= cwidthcol;\ 125 }\ 126 } 127 128 129 struct cardtype { 130 char suit; 131 char color; 132 bool visible; 133 bool paid; 134 int rank; 135 struct cardtype *next; 136 }; 137 138 #define NIL ((struct cardtype *) -1) 139 140 struct cardtype *deck[decksize]; 141 struct cardtype cards[decksize]; 142 struct cardtype *bottom[4], *found[4], *tableau[4]; 143 struct cardtype *talon, *hand, *stock, *basecard; 144 int length[4]; 145 int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru; 146 char suitmap[4] = {spades, clubs, hearts, diamonds}; 147 char colormap[4] = {black, black, red, red}; 148 char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol}; 149 char srcpile, destpile; 150 int mtforigin, tempbase; 151 int coldcol, cnewcol, coldrow, cnewrow; 152 bool errmsg, done; 153 bool mtfdone, Cflag = FALSE; 154 #define INSTRUCTIONBOX 1 155 #define BETTINGBOX 2 156 #define NOBOX 3 157 int status = INSTRUCTIONBOX; 158 int uid; 159 160 /* 161 * Basic betting costs 162 */ 163 #define costofhand 13 164 #define costofinspection 13 165 #define costofgame 26 166 #define costofrunthroughhand 5 167 #define costofinformation 1 168 #define secondsperdollar 60 169 #define maxtimecharge 3 170 #define valuepercardup 5 171 /* 172 * Variables associated with betting 173 */ 174 struct betinfo { 175 long hand; /* cost of dealing hand */ 176 long inspection; /* cost of inspecting hand */ 177 long game; /* cost of buying game */ 178 long runs; /* cost of running through hands */ 179 long information; /* cost of information */ 180 long thinktime; /* cost of thinking time */ 181 long wins; /* total winnings */ 182 long worth; /* net worth after costs */ 183 }; 184 struct betinfo this, game, total; 185 bool startedgame = FALSE, infullgame = FALSE; 186 time_t acctstart; 187 int dbfd = -1; 188 189 static void askquit(int); 190 static void cleanup(int); 191 static void cleanupboard(void); 192 static void clearabovemovebox(void); 193 static void clearbelowmovebox(void); 194 static void clearmsg(void); 195 static void clearstat(void); 196 static void destinerror(void); 197 static bool diffcolor(struct cardtype *, struct cardtype *); 198 static void dumberror(void); 199 static bool finish(void); 200 static void fndbase(struct cardtype **, int, int); 201 static void getcmd(int, int, const char *); 202 static void initall(void); 203 static void initdeck(struct cardtype *[]); 204 static void initgame(void); 205 static void instruct(void); 206 static void makeboard(void); 207 static void movebox(void); 208 static void movecard(void); 209 static void movetofound(struct cardtype **, int); 210 static void movetotalon(void); 211 static bool notempty(struct cardtype *); 212 static void printbottombettingbox(void); 213 static void printbottominstructions(void); 214 static void printcard(int, int, struct cardtype *); 215 static void printrank(int, int, struct cardtype *, bool); 216 static void printtopbettingbox(void); 217 static void printtopinstructions(void); 218 static bool rankhigher(struct cardtype *, int); 219 static bool ranklower(struct cardtype *, struct cardtype *); 220 static void removecard(int, int); 221 static bool samesuit(struct cardtype *, int); 222 static void showcards(void); 223 static void showstat(void); 224 static void shuffle(struct cardtype *[]); 225 static void simpletableau(struct cardtype **, int); 226 static void startgame(void); 227 static void suspend(void); 228 static bool tabok(struct cardtype *, int); 229 static void tabprint(int, int); 230 static void tabtotab(int, int); 231 static void transit(struct cardtype **, struct cardtype **); 232 static void updatebettinginfo(void); 233 static void usedstock(void); 234 static void usedtalon(void); 235 236 /* 237 * The following procedures print the board onto the screen using the 238 * addressible cursor. The end of these procedures will also be 239 * separated from the rest of the program. 240 * 241 * procedure to set the move command box 242 */ 243 static void 244 movebox(void) 245 { 246 switch (status) { 247 case BETTINGBOX: 248 printtopbettingbox(); 249 break; 250 case NOBOX: 251 clearabovemovebox(); 252 break; 253 case INSTRUCTIONBOX: 254 printtopinstructions(); 255 break; 256 } 257 move(moverow, boxcol); 258 printw("| |"); 259 move(msgrow, boxcol); 260 printw("| |"); 261 switch (status) { 262 case BETTINGBOX: 263 printbottombettingbox(); 264 break; 265 case NOBOX: 266 clearbelowmovebox(); 267 break; 268 case INSTRUCTIONBOX: 269 printbottominstructions(); 270 break; 271 } 272 refresh(); 273 } 274 275 /* 276 * print directions above move box 277 */ 278 static void 279 printtopinstructions(void) 280 { 281 move(tboxrow, boxcol); 282 printw("*----------------------------------*"); 283 move(tboxrow + 1, boxcol); 284 printw("| MOVES |"); 285 move(tboxrow + 2, boxcol); 286 printw("|s# = stock to tableau |"); 287 move(tboxrow + 3, boxcol); 288 printw("|sf = stock to foundation |"); 289 move(tboxrow + 4, boxcol); 290 printw("|t# = talon to tableau |"); 291 move(tboxrow + 5, boxcol); 292 printw("|tf = talon to foundation |"); 293 move(tboxrow + 6, boxcol); 294 printw("|## = tableau to tableau |"); 295 move(tboxrow + 7, boxcol); 296 printw("|#f = tableau to foundation |"); 297 move(tboxrow + 8, boxcol); 298 printw("|ht = hand to talon |"); 299 move(tboxrow + 9, boxcol); 300 printw("|c = toggle card counting |"); 301 move(tboxrow + 10, boxcol); 302 printw("|b = present betting information |"); 303 move(tboxrow + 11, boxcol); 304 printw("|q = quit to end the game |"); 305 move(tboxrow + 12, boxcol); 306 printw("|==================================|"); 307 } 308 309 /* 310 * Print the betting box. 311 */ 312 static void 313 printtopbettingbox(void) 314 { 315 316 move(tboxrow, boxcol); 317 printw("*----------------------------------*"); 318 move(tboxrow + 1, boxcol); 319 printw("|Costs Hand Game Total |"); 320 move(tboxrow + 2, boxcol); 321 printw("| Hands |"); 322 move(tboxrow + 3, boxcol); 323 printw("| Inspections |"); 324 move(tboxrow + 4, boxcol); 325 printw("| Games |"); 326 move(tboxrow + 5, boxcol); 327 printw("| Runs |"); 328 move(tboxrow + 6, boxcol); 329 printw("| Information |"); 330 move(tboxrow + 7, boxcol); 331 printw("| Think time |"); 332 move(tboxrow + 8, boxcol); 333 printw("|Total Costs |"); 334 move(tboxrow + 9, boxcol); 335 printw("|Winnings |"); 336 move(tboxrow + 10, boxcol); 337 printw("|Net Worth |"); 338 move(tboxrow + 11, boxcol); 339 printw("|Return |"); 340 move(tboxrow + 12, boxcol); 341 printw("|==================================|"); 342 } 343 344 /* 345 * clear info above move box 346 */ 347 static void 348 clearabovemovebox(void) 349 { 350 int i; 351 352 for (i = 0; i <= 11; i++) { 353 move(tboxrow + i, boxcol); 354 printw(" "); 355 } 356 move(tboxrow + 12, boxcol); 357 printw("*----------------------------------*"); 358 } 359 360 /* 361 * print instructions below move box 362 */ 363 static void 364 printbottominstructions(void) 365 { 366 move(bboxrow, boxcol); 367 printw("|Replace # with the number of the |"); 368 move(bboxrow + 1, boxcol); 369 printw("|tableau you want. |"); 370 move(bboxrow + 2, boxcol); 371 printw("*----------------------------------*"); 372 } 373 374 /* 375 * print betting information below move box 376 */ 377 static void 378 printbottombettingbox(void) 379 { 380 move(bboxrow, boxcol); 381 printw("|x = toggle information box |"); 382 move(bboxrow + 1, boxcol); 383 printw("|i = list playing instructions |"); 384 move(bboxrow + 2, boxcol); 385 printw("*----------------------------------*"); 386 } 387 388 /* 389 * clear info below move box 390 */ 391 static void 392 clearbelowmovebox(void) 393 { 394 int i; 395 396 move(bboxrow, boxcol); 397 printw("*----------------------------------*"); 398 for (i = 1; i <= 2; i++) { 399 move(bboxrow + i, boxcol); 400 printw(" "); 401 } 402 } 403 404 /* 405 * procedure to put the board on the screen using addressable cursor 406 */ 407 static void 408 makeboard(void) 409 { 410 clear(); 411 refresh(); 412 move(titlerow, titlecol); 413 printw("=-> CANFIELD <-="); 414 move(fttlrow, fttlcol); 415 printw("foundation"); 416 move(foundrow - 1, fttlcol); 417 printw("=---= =---= =---= =---="); 418 move(foundrow, fttlcol); 419 printw("| | | | | | | |"); 420 move(foundrow + 1, fttlcol); 421 printw("=---= =---= =---= =---="); 422 move(ottlrow, sidecol); 423 printw("stock tableau"); 424 move(stockrow - 1, sidecol); 425 printw("=---="); 426 move(stockrow, sidecol); 427 printw("| |"); 428 move(stockrow + 1, sidecol); 429 printw("=---="); 430 move(talonrow - 2, sidecol); 431 printw("talon"); 432 move(talonrow - 1, sidecol); 433 printw("=---="); 434 move(talonrow, sidecol); 435 printw("| |"); 436 move(talonrow + 1, sidecol); 437 printw("=---="); 438 move(tabrow - 1, atabcol); 439 printw("-1- -2- -3- -4-"); 440 movebox(); 441 } 442 443 /* 444 * clean up the board for another game 445 */ 446 static void 447 cleanupboard(void) 448 { 449 int cnt, row, col; 450 struct cardtype *ptr; 451 452 col = 0; 453 if (Cflag) { 454 clearstat(); 455 for(ptr = stock, row = stockrow; 456 ptr != NIL; 457 ptr = ptr->next, row++) { 458 move(row, sidecol); 459 printw(" "); 460 } 461 move(row, sidecol); 462 printw(" "); 463 move(stockrow + 1, sidecol); 464 printw("=---="); 465 move(talonrow - 2, sidecol); 466 printw("talon"); 467 move(talonrow - 1, sidecol); 468 printw("=---="); 469 move(talonrow + 1, sidecol); 470 printw("=---="); 471 } 472 move(stockrow, sidecol); 473 printw("| |"); 474 move(talonrow, sidecol); 475 printw("| |"); 476 move(foundrow, fttlcol); 477 printw("| | | | | | | |"); 478 for (cnt = 0; cnt < 4; cnt++) { 479 switch(cnt) { 480 case 0: 481 col = atabcol; 482 break; 483 case 1: 484 col = btabcol; 485 break; 486 case 2: 487 col = ctabcol; 488 break; 489 case 3: 490 col = dtabcol; 491 break; 492 } 493 for(ptr = tableau[cnt], row = tabrow; 494 ptr != NIL; 495 ptr = ptr->next, row++) 496 removecard(col, row); 497 } 498 } 499 500 /* 501 * procedure to create a deck of cards 502 */ 503 static void 504 initdeck(struct cardtype *ideck[]) 505 { 506 int i; 507 int scnt; 508 char s; 509 int r; 510 511 i = 0; 512 for (scnt=0; scnt<4; scnt++) { 513 s = suitmap[scnt]; 514 for (r=Ace; r<=King; r++) { 515 ideck[i] = &cards[i]; 516 cards[i].rank = r; 517 cards[i].suit = s; 518 cards[i].color = colormap[scnt]; 519 cards[i].next = NIL; 520 i++; 521 } 522 } 523 } 524 525 /* 526 * procedure to shuffle the deck 527 */ 528 static void 529 shuffle(struct cardtype *ideck[]) 530 { 531 int i,j; 532 struct cardtype *temp; 533 534 for (i=0; i<decksize; i++) { 535 ideck[i]->visible = FALSE; 536 ideck[i]->paid = FALSE; 537 } 538 for (i = decksize-1; i>=0; i--) { 539 j = random() % decksize; 540 if (i != j) { 541 temp = ideck[i]; 542 ideck[i] = ideck[j]; 543 ideck[j] = temp; 544 } 545 } 546 } 547 548 /* 549 * procedure to remove the card from the board 550 */ 551 static void 552 removecard(int a, int b) 553 { 554 move(b, a); 555 printw(" "); 556 } 557 558 /* 559 * procedure to print the cards on the board 560 */ 561 static void 562 printrank(int a, int b, struct cardtype *cp, bool inverse) 563 { 564 move(b, a); 565 if (cp->rank != 10) 566 addch(' '); 567 if (inverse) 568 standout(); 569 switch (cp->rank) { 570 case 2: case 3: case 4: case 5: case 6: case 7: 571 case 8: case 9: case 10: 572 printw("%d", cp->rank); 573 break; 574 case Ace: 575 addch('A'); 576 break; 577 case Jack: 578 addch('J'); 579 break; 580 case Queen: 581 addch('Q'); 582 break; 583 case King: 584 addch('K'); 585 } 586 if (inverse) 587 standend(); 588 } 589 590 /* 591 * procedure to print out a card 592 */ 593 static void 594 printcard(int a, int b, struct cardtype *cp) 595 { 596 if (cp == NIL) 597 removecard(a, b); 598 else if (cp->visible == FALSE) { 599 move(b, a); 600 printw(" ? "); 601 } else { 602 bool inverse = (cp->suit == 'd' || cp->suit == 'h'); 603 604 printrank(a, b, cp, inverse); 605 if (inverse) 606 standout(); 607 addch(cp->suit); 608 if (inverse) 609 standend(); 610 } 611 } 612 613 /* 614 * procedure to move the top card from one location to the top 615 * of another location. The pointers always point to the top 616 * of the piles. 617 */ 618 static void 619 transit(struct cardtype **source, struct cardtype **dest) 620 { 621 struct cardtype *temp; 622 623 temp = *source; 624 *source = (*source)->next; 625 temp->next = *dest; 626 *dest = temp; 627 } 628 629 /* 630 * Procedure to set the cards on the foundation base when available. 631 * Note that it is only called on a foundation pile at the beginning of 632 * the game, so the pile will have exactly one card in it. 633 */ 634 static void 635 fndbase(struct cardtype **cp, int column, int row) 636 { 637 bool nomore; 638 639 if (*cp != NIL) 640 do { 641 if ((*cp)->rank == basecard->rank) { 642 base++; 643 printcard(pilemap[base], foundrow, *cp); 644 if (*cp == tableau[0]) 645 length[0] = length[0] - 1; 646 if (*cp == tableau[1]) 647 length[1] = length[1] - 1; 648 if (*cp == tableau[2]) 649 length[2] = length[2] - 1; 650 if (*cp == tableau[3]) 651 length[3] = length[3] - 1; 652 transit(cp, &found[base]); 653 if (cp == &talon) 654 usedtalon(); 655 if (cp == &stock) 656 usedstock(); 657 if (*cp != NIL) { 658 printcard(column, row, *cp); 659 nomore = FALSE; 660 } else { 661 removecard(column, row); 662 nomore = TRUE; 663 } 664 cardsoff++; 665 if (infullgame) { 666 this.wins += valuepercardup; 667 game.wins += valuepercardup; 668 total.wins += valuepercardup; 669 } 670 } else 671 nomore = TRUE; 672 } while (nomore == FALSE); 673 } 674 675 /* 676 * procedure to initialize the things necessary for the game 677 */ 678 static void 679 initgame(void) 680 { 681 int i; 682 683 for (i=0; i<18; i++) { 684 deck[i]->visible = TRUE; 685 deck[i]->paid = TRUE; 686 } 687 stockcnt = 13; 688 stock = deck[12]; 689 for (i=12; i>=1; i--) 690 deck[i]->next = deck[i - 1]; 691 deck[0]->next = NIL; 692 found[0] = deck[13]; 693 deck[13]->next = NIL; 694 for (i=1; i<4; i++) 695 found[i] = NIL; 696 basecard = found[0]; 697 for (i=14; i<18; i++) { 698 tableau[i - 14] = deck[i]; 699 deck[i]->next = NIL; 700 } 701 for (i=0; i<4; i++) { 702 bottom[i] = tableau[i]; 703 length[i] = tabrow; 704 } 705 hand = deck[18]; 706 for (i=18; i<decksize-1; i++) 707 deck[i]->next = deck[i + 1]; 708 deck[decksize-1]->next = NIL; 709 talon = NIL; 710 base = 0; 711 cinhand = 34; 712 taloncnt = 0; 713 timesthru = 0; 714 cardsoff = 1; 715 coldrow = ctoprow; 716 coldcol = cinitcol; 717 cnewrow = ctoprow; 718 cnewcol = cinitcol + cwidthcol; 719 } 720 721 /* 722 * procedure to print the beginning cards and to start each game 723 */ 724 static void 725 startgame(void) 726 { 727 int j; 728 729 shuffle(deck); 730 initgame(); 731 this.hand = costofhand; 732 game.hand += costofhand; 733 total.hand += costofhand; 734 this.inspection = 0; 735 this.game = 0; 736 this.runs = 0; 737 this.information = 0; 738 this.wins = 0; 739 this.thinktime = 0; 740 infullgame = FALSE; 741 startedgame = FALSE; 742 printcard(foundcol, foundrow, found[0]); 743 printcard(stockcol, stockrow, stock); 744 printcard(atabcol, tabrow, tableau[0]); 745 printcard(btabcol, tabrow, tableau[1]); 746 printcard(ctabcol, tabrow, tableau[2]); 747 printcard(dtabcol, tabrow, tableau[3]); 748 printcard(taloncol, talonrow, talon); 749 move(foundrow - 2, basecol); 750 printw("Base"); 751 move(foundrow - 1, basecol); 752 printw("Rank"); 753 printrank(basecol, foundrow, found[0], 0); 754 for (j=0; j<=3; j++) 755 fndbase(&tableau[j], pilemap[j], tabrow); 756 fndbase(&stock, stockcol, stockrow); 757 showstat(); /* show card counting info to cheaters */ 758 movetotalon(); 759 updatebettinginfo(); 760 } 761 762 /* 763 * procedure to clear the message printed from an error 764 */ 765 static void 766 clearmsg(void) 767 { 768 int i; 769 770 if (errmsg == TRUE) { 771 errmsg = FALSE; 772 move(msgrow, msgcol); 773 for (i=0; i<25; i++) 774 addch(' '); 775 refresh(); 776 } 777 } 778 779 /* 780 * procedure to print an error message if the move is not listed 781 */ 782 static void 783 dumberror(void) 784 { 785 errmsg = TRUE; 786 move(msgrow, msgcol); 787 printw("Not a proper move "); 788 } 789 790 /* 791 * procedure to print an error message if the move is not possible 792 */ 793 static void 794 destinerror(void) 795 { 796 errmsg = TRUE; 797 move(msgrow, msgcol); 798 printw("Error: Can't move there"); 799 } 800 801 /* 802 * function to see if the source has cards in it 803 */ 804 static bool 805 notempty(struct cardtype *cp) 806 { 807 if (cp == NIL) { 808 errmsg = TRUE; 809 move(msgrow, msgcol); 810 printw("Error: no cards to move"); 811 return (FALSE); 812 } else 813 return (TRUE); 814 } 815 816 /* 817 * function to see if the rank of one card is less than another 818 */ 819 static bool 820 ranklower(struct cardtype *cp1, struct cardtype *cp2) 821 { 822 if (cp2->rank == Ace) 823 if (cp1->rank == King) 824 return (TRUE); 825 else 826 return (FALSE); 827 else if (cp1->rank + 1 == cp2->rank) 828 return (TRUE); 829 else 830 return (FALSE); 831 } 832 833 /* 834 * function to check the cardcolor for moving to a tableau 835 */ 836 static bool 837 diffcolor(struct cardtype *cp1, struct cardtype *cp2) 838 { 839 if (cp1->color == cp2->color) 840 return (FALSE); 841 else 842 return (TRUE); 843 } 844 845 /* 846 * function to see if the card can move to the tableau 847 */ 848 static bool 849 tabok(struct cardtype *cp, int des) 850 { 851 if ((cp == stock) && (tableau[des] == NIL)) 852 return (TRUE); 853 else if (tableau[des] == NIL) 854 if (stock == NIL && 855 cp != bottom[0] && cp != bottom[1] && 856 cp != bottom[2] && cp != bottom[3]) 857 return (TRUE); 858 else 859 return (FALSE); 860 else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des])) 861 return (TRUE); 862 else 863 return (FALSE); 864 } 865 866 /* 867 * procedure to turn the cards onto the talon from the deck 868 */ 869 static void 870 movetotalon(void) 871 { 872 int i, fin; 873 874 if (cinhand <= 3 && cinhand > 0) { 875 move(msgrow, msgcol); 876 printw("Hand is now empty "); 877 } 878 if (cinhand >= 3) 879 fin = 3; 880 else if (cinhand > 0) 881 fin = cinhand; 882 else if (talon != NIL) { 883 timesthru++; 884 errmsg = TRUE; 885 move(msgrow, msgcol); 886 if (timesthru != 4) { 887 printw("Talon is now the new hand"); 888 this.runs += costofrunthroughhand; 889 game.runs += costofrunthroughhand; 890 total.runs += costofrunthroughhand; 891 while (talon != NIL) { 892 transit(&talon, &hand); 893 cinhand++; 894 } 895 if (cinhand >= 3) 896 fin = 3; 897 else 898 fin = cinhand; 899 taloncnt = 0; 900 coldrow = ctoprow; 901 coldcol = cinitcol; 902 cnewrow = ctoprow; 903 cnewcol = cinitcol + cwidthcol; 904 clearstat(); 905 showstat(); 906 } else { 907 fin = 0; 908 done = TRUE; 909 printw("I believe you have lost"); 910 refresh(); 911 sleep(5); 912 } 913 } else { 914 errmsg = TRUE; 915 move(msgrow, msgcol); 916 printw("Talon and hand are empty"); 917 fin = 0; 918 } 919 for (i=0; i<fin; i++) { 920 transit(&hand, &talon); 921 INCRHAND(cnewrow, cnewcol); 922 INCRHAND(coldrow, coldcol); 923 removecard(cnewcol, cnewrow); 924 if (i == fin - 1) 925 talon->visible = TRUE; 926 if (Cflag) { 927 if (talon->paid == FALSE && talon->visible == TRUE) { 928 this.information += costofinformation; 929 game.information += costofinformation; 930 total.information += costofinformation; 931 talon->paid = TRUE; 932 } 933 printcard(coldcol, coldrow, talon); 934 } 935 } 936 if (fin != 0) { 937 printcard(taloncol, talonrow, talon); 938 cinhand -= fin; 939 taloncnt += fin; 940 if (Cflag) { 941 move(handstatrow, handstatcol); 942 printw("%3d", cinhand); 943 move(talonstatrow, talonstatcol); 944 printw("%3d", taloncnt); 945 } 946 fndbase(&talon, taloncol, talonrow); 947 } 948 } 949 950 951 /* 952 * procedure to print card counting info on screen 953 */ 954 static void 955 showstat(void) 956 { 957 int row, col; 958 struct cardtype *ptr; 959 960 if (!Cflag) 961 return; 962 move(talonstatrow, talonstatcol - 7); 963 printw("Talon: %3d", taloncnt); 964 move(handstatrow, handstatcol - 7); 965 printw("Hand: %3d", cinhand); 966 move(stockstatrow, stockstatcol - 7); 967 printw("Stock: %3d", stockcnt); 968 for ( row = coldrow, col = coldcol, ptr = talon; 969 ptr != NIL; 970 ptr = ptr->next ) { 971 if (ptr->paid == FALSE && ptr->visible == TRUE) { 972 ptr->paid = TRUE; 973 this.information += costofinformation; 974 game.information += costofinformation; 975 total.information += costofinformation; 976 } 977 printcard(col, row, ptr); 978 DECRHAND(row, col); 979 } 980 for ( row = cnewrow, col = cnewcol, ptr = hand; 981 ptr != NIL; 982 ptr = ptr->next ) { 983 if (ptr->paid == FALSE && ptr->visible == TRUE) { 984 ptr->paid = TRUE; 985 this.information += costofinformation; 986 game.information += costofinformation; 987 total.information += costofinformation; 988 } 989 INCRHAND(row, col); 990 printcard(col, row, ptr); 991 } 992 } 993 994 /* 995 * procedure to clear card counting info from screen 996 */ 997 static void 998 clearstat(void) 999 { 1000 int row; 1001 1002 move(talonstatrow, talonstatcol - 7); 1003 printw(" "); 1004 move(handstatrow, handstatcol - 7); 1005 printw(" "); 1006 move(stockstatrow, stockstatcol - 7); 1007 printw(" "); 1008 for ( row = ctoprow ; row <= cbotrow ; row++ ) { 1009 move(row, cinitcol); 1010 printw("%56s", " "); 1011 } 1012 } 1013 1014 /* 1015 * procedure to update card counting base 1016 */ 1017 static void 1018 usedtalon(void) 1019 { 1020 removecard(coldcol, coldrow); 1021 DECRHAND(coldrow, coldcol); 1022 if (talon != NIL && (talon->visible == FALSE)) { 1023 talon->visible = TRUE; 1024 if (Cflag) { 1025 this.information += costofinformation; 1026 game.information += costofinformation; 1027 total.information += costofinformation; 1028 talon->paid = TRUE; 1029 printcard(coldcol, coldrow, talon); 1030 } 1031 } 1032 taloncnt--; 1033 if (Cflag) { 1034 move(talonstatrow, talonstatcol); 1035 printw("%3d", taloncnt); 1036 } 1037 } 1038 1039 /* 1040 * procedure to update stock card counting base 1041 */ 1042 static void 1043 usedstock(void) 1044 { 1045 stockcnt--; 1046 if (Cflag) { 1047 move(stockstatrow, stockstatcol); 1048 printw("%3d", stockcnt); 1049 } 1050 } 1051 1052 /* 1053 * let 'em know how they lost! 1054 */ 1055 static void 1056 showcards(void) 1057 { 1058 struct cardtype *ptr; 1059 int row; 1060 1061 if (!Cflag || cardsoff == 52) 1062 return; 1063 for (ptr = talon; ptr != NIL; ptr = ptr->next) { 1064 ptr->visible = TRUE; 1065 ptr->paid = TRUE; 1066 } 1067 for (ptr = hand; ptr != NIL; ptr = ptr->next) { 1068 ptr->visible = TRUE; 1069 ptr->paid = TRUE; 1070 } 1071 showstat(); 1072 move(stockrow + 1, sidecol); 1073 printw(" "); 1074 move(talonrow - 2, sidecol); 1075 printw(" "); 1076 move(talonrow - 1, sidecol); 1077 printw(" "); 1078 move(talonrow, sidecol); 1079 printw(" "); 1080 move(talonrow + 1, sidecol); 1081 printw(" "); 1082 for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) { 1083 move(row, stockcol - 1); 1084 printw("| |"); 1085 printcard(stockcol, row, ptr); 1086 } 1087 if (stock == NIL) { 1088 move(row, stockcol - 1); 1089 printw("| |"); 1090 row++; 1091 } 1092 move(handstatrow, handstatcol - 7); 1093 printw(" "); 1094 move(row, stockcol - 1); 1095 printw("=---="); 1096 if ( cardsoff == 52 ) 1097 getcmd(moverow, movecol, "Hit return to exit"); 1098 } 1099 1100 /* 1101 * procedure to update the betting values 1102 */ 1103 static void 1104 updatebettinginfo(void) 1105 { 1106 long thiscosts, gamecosts, totalcosts; 1107 double thisreturn, gamereturn, totalreturn; 1108 time_t now; 1109 long dollars; 1110 1111 time(&now); 1112 dollars = (now - acctstart) / secondsperdollar; 1113 if (dollars > 0) { 1114 acctstart += dollars * secondsperdollar; 1115 if (dollars > maxtimecharge) 1116 dollars = maxtimecharge; 1117 this.thinktime += dollars; 1118 game.thinktime += dollars; 1119 total.thinktime += dollars; 1120 } 1121 thiscosts = this.hand + this.inspection + this.game + 1122 this.runs + this.information + this.thinktime; 1123 gamecosts = game.hand + game.inspection + game.game + 1124 game.runs + game.information + game.thinktime; 1125 totalcosts = total.hand + total.inspection + total.game + 1126 total.runs + total.information + total.thinktime; 1127 this.worth = this.wins - thiscosts; 1128 game.worth = game.wins - gamecosts; 1129 total.worth = total.wins - totalcosts; 1130 thisreturn = ((double)this.wins / (double)thiscosts - 1.0) * 100.0; 1131 gamereturn = ((double)game.wins / (double)gamecosts - 1.0) * 100.0; 1132 totalreturn = ((double)total.wins / (double)totalcosts - 1.0) * 100.0; 1133 if (status != BETTINGBOX) 1134 return; 1135 move(tboxrow + 2, boxcol + 13); 1136 printw("%4d%8d%9d", this.hand, game.hand, total.hand); 1137 move(tboxrow + 3, boxcol + 13); 1138 printw("%4d%8d%9d", this.inspection, game.inspection, total.inspection); 1139 move(tboxrow + 4, boxcol + 13); 1140 printw("%4d%8d%9d", this.game, game.game, total.game); 1141 move(tboxrow + 5, boxcol + 13); 1142 printw("%4d%8d%9d", this.runs, game.runs, total.runs); 1143 move(tboxrow + 6, boxcol + 13); 1144 printw("%4d%8d%9d", this.information, game.information, 1145 total.information); 1146 move(tboxrow + 7, boxcol + 13); 1147 printw("%4d%8d%9d", this.thinktime, game.thinktime, total.thinktime); 1148 move(tboxrow + 8, boxcol + 13); 1149 printw("%4d%8d%9d", thiscosts, gamecosts, totalcosts); 1150 move(tboxrow + 9, boxcol + 13); 1151 printw("%4d%8d%9d", this.wins, game.wins, total.wins); 1152 move(tboxrow + 10, boxcol + 13); 1153 printw("%4d%8d%9d", this.worth, game.worth, total.worth); 1154 move(tboxrow + 11, boxcol + 13); 1155 printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn, gamereturn, totalreturn); 1156 } 1157 1158 /* 1159 * procedure to move a card from the stock or talon to the tableau 1160 */ 1161 static void 1162 simpletableau(struct cardtype **cp, int des) 1163 { 1164 int origin; 1165 1166 if (notempty(*cp)) { 1167 if (tabok(*cp, des)) { 1168 if (*cp == stock) 1169 origin = stk; 1170 else 1171 origin = tal; 1172 if (tableau[des] == NIL) 1173 bottom[des] = *cp; 1174 transit(cp, &tableau[des]); 1175 length[des]++; 1176 printcard(pilemap[des], length[des], tableau[des]); 1177 timesthru = 0; 1178 if (origin == stk) { 1179 usedstock(); 1180 printcard(stockcol, stockrow, stock); 1181 } else { 1182 usedtalon(); 1183 printcard(taloncol, talonrow, talon); 1184 } 1185 } else 1186 destinerror(); 1187 } 1188 } 1189 1190 /* 1191 * print the tableau 1192 */ 1193 static void 1194 tabprint(int sour, int des) 1195 { 1196 int dlength, slength, i; 1197 struct cardtype *tempcard; 1198 1199 for (i=tabrow; i<=length[sour]; i++) 1200 removecard(pilemap[sour], i); 1201 dlength = length[des] + 1; 1202 slength = length[sour]; 1203 if (slength == tabrow) 1204 printcard(pilemap[des], dlength, tableau[sour]); 1205 else 1206 while (slength != tabrow - 1) { 1207 tempcard = tableau[sour]; 1208 for (i=1; i<=slength-tabrow; i++) 1209 tempcard = tempcard->next; 1210 printcard(pilemap[des], dlength, tempcard); 1211 slength--; 1212 dlength++; 1213 } 1214 } 1215 1216 /* 1217 * procedure to move from the tableau to the tableau 1218 */ 1219 static void 1220 tabtotab(int sour, int des) 1221 { 1222 struct cardtype *temp; 1223 1224 if (notempty(tableau[sour])) { 1225 if (tabok(bottom[sour], des)) { 1226 tabprint(sour, des); 1227 temp = bottom[sour]; 1228 bottom[sour] = NIL; 1229 if (bottom[des] == NIL) 1230 bottom[des] = temp; 1231 temp->next = tableau[des]; 1232 tableau[des] = tableau[sour]; 1233 tableau[sour] = NIL; 1234 length[des] = length[des] + 1235 (length[sour] - (tabrow - 1)); 1236 length[sour] = tabrow - 1; 1237 timesthru = 0; 1238 } else 1239 destinerror(); 1240 } 1241 } 1242 1243 /* 1244 * functions to see if the card can go onto the foundation 1245 */ 1246 static bool 1247 rankhigher(struct cardtype *cp, int let) 1248 { 1249 if (found[let]->rank == King) 1250 if (cp->rank == Ace) 1251 return(TRUE); 1252 else 1253 return(FALSE); 1254 else if (cp->rank - 1 == found[let]->rank) 1255 return(TRUE); 1256 else 1257 return(FALSE); 1258 } 1259 1260 /* 1261 * function to determine if two cards are the same suit 1262 */ 1263 static bool 1264 samesuit(struct cardtype *cp, int let) 1265 { 1266 if (cp->suit == found[let]->suit) 1267 return (TRUE); 1268 else 1269 return (FALSE); 1270 } 1271 1272 /* 1273 * procedure to move a card to the correct foundation pile 1274 */ 1275 static void 1276 movetofound(struct cardtype **cp, int source) 1277 { 1278 tempbase = 0; 1279 mtfdone = FALSE; 1280 if (notempty(*cp)) { 1281 do { 1282 if (found[tempbase] != NIL) 1283 if (rankhigher(*cp, tempbase) 1284 && samesuit(*cp, tempbase)) { 1285 if (*cp == stock) 1286 mtforigin = stk; 1287 else if (*cp == talon) 1288 mtforigin = tal; 1289 else 1290 mtforigin = tab; 1291 transit(cp, &found[tempbase]); 1292 printcard(pilemap[tempbase], 1293 foundrow, found[tempbase]); 1294 timesthru = 0; 1295 if (mtforigin == stk) { 1296 usedstock(); 1297 printcard(stockcol, stockrow, 1298 stock); 1299 } else if (mtforigin == tal) { 1300 usedtalon(); 1301 printcard(taloncol, talonrow, 1302 talon); 1303 } else { 1304 removecard(pilemap[source], 1305 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(int sig __unused) 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(int sig __unused) 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 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