1 /* $NetBSD: canfield.c,v 1.31 2021/05/02 12:50:44 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)canfield.c 8.1 (Berkeley) 5/31/93"; 41 #else 42 __RCSID("$NetBSD: canfield.c,v 1.31 2021/05/02 12:50:44 rillig Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 /* 47 * The canfield program 48 * 49 * Authors: 50 * Originally written: Steve Levine 51 * Converted to use curses and debugged: Steve Feldman 52 * Card counting: Kirk McKusick and Mikey Olson 53 * User interface cleanups: Eric Allman and Kirk McKusick 54 * Betting by Kirk McKusick 55 */ 56 57 #include <sys/types.h> 58 59 #include <ctype.h> 60 #include <curses.h> 61 #include <fcntl.h> 62 #include <signal.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <termios.h> 66 #include <time.h> 67 #include <unistd.h> 68 69 #include "betinfo.h" 70 #include "pathnames.h" 71 72 #define decksize 52 73 #define originrow 0 74 #define origincol 0 75 #define basecol 1 76 #define boxcol 42 77 #define tboxrow 2 78 #define bboxrow 17 79 #define movecol 43 80 #define moverow 16 81 #define msgcol 43 82 #define msgrow 15 83 #define titlecol 30 84 #define titlerow 0 85 #define sidecol 1 86 #define ottlrow 6 87 #define foundcol 11 88 #define foundrow 3 89 #define stockcol 2 90 #define stockrow 8 91 #define fttlcol 10 92 #define fttlrow 1 93 #define taloncol 2 94 #define talonrow 13 95 #define tabrow 8 96 #define ctoprow 21 97 #define cbotrow 23 98 #define cinitcol 14 99 #define cheightcol 1 100 #define cwidthcol 4 101 #define handstatrow 21 102 #define handstatcol 7 103 #define talonstatrow 22 104 #define talonstatcol 7 105 #define stockstatrow 23 106 #define stockstatcol 7 107 #define Ace 1 108 #define Jack 11 109 #define Queen 12 110 #define King 13 111 #define atabcol 11 112 #define btabcol 18 113 #define ctabcol 25 114 #define dtabcol 32 115 116 #define spades 's' 117 #define clubs 'c' 118 #define hearts 'h' 119 #define diamonds 'd' 120 #define black 'b' 121 #define red 'r' 122 123 #define stk 1 124 #define tal 2 125 #define tab 3 126 #define INCRHAND(row, col) {\ 127 row -= cheightcol;\ 128 if (row < ctoprow) {\ 129 row = cbotrow;\ 130 col += cwidthcol;\ 131 }\ 132 } 133 #define DECRHAND(row, col) {\ 134 row += cheightcol;\ 135 if (row > cbotrow) {\ 136 row = ctoprow;\ 137 col -= cwidthcol;\ 138 }\ 139 } 140 141 142 struct cardtype { 143 char suit; 144 char color; 145 bool visible; 146 bool paid; 147 int rank; 148 struct cardtype *next; 149 }; 150 151 #define NIL ((struct cardtype *) -1) 152 153 static struct cardtype *deck[decksize]; 154 static struct cardtype cards[decksize]; 155 static struct cardtype *bottom[4], *found[4], *tableau[4]; 156 static struct cardtype *talon, *hand, *stock, *basecard; 157 static int length[4]; 158 static int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru; 159 static char suitmap[4] = {spades, clubs, hearts, diamonds}; 160 static char colormap[4] = {black, black, red, red}; 161 static char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol}; 162 static char srcpile, destpile; 163 static int mtforigin, tempbase; 164 static int coldcol, cnewcol, coldrow, cnewrow; 165 static bool errmsg, done; 166 static bool mtfdone, Cflag = FALSE; 167 #define INSTRUCTIONBOX 1 168 #define BETTINGBOX 2 169 #define NOBOX 3 170 static int status = INSTRUCTIONBOX; 171 static uid_t uid; 172 173 /* 174 * Basic betting costs 175 */ 176 #define costofhand 13 177 #define costofinspection 13 178 #define costofgame 26 179 #define costofrunthroughhand 5 180 #define costofinformation 1 181 #define secondsperdollar 60 182 #define maxtimecharge 3 183 #define valuepercardup 5 184 /* 185 * Variables associated with betting 186 */ 187 static struct betinfo this, game, total; 188 static bool startedgame = FALSE, infullgame = FALSE; 189 static time_t acctstart; 190 static int dbfd = -1; 191 192 static void askquit(int); 193 static void cleanup(int) __dead; 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(const struct cardtype *, const 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(const struct cardtype *); 215 static void printbottombettingbox(void); 216 static void printbottominstructions(void); 217 static void printcard(int, int, const struct cardtype *); 218 static void printrank(int, int, const struct cardtype *, bool); 219 static void printtopbettingbox(void); 220 static void printtopinstructions(void); 221 static bool rankhigher(const struct cardtype *, int); 222 static bool ranklower(const struct cardtype *, const struct cardtype *); 223 static void removecard(int, int); 224 static int samesuit(const 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(const 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 *ideck[]) 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 ideck[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 *ideck[]) 533 { 534 int i,j; 535 struct cardtype *temp; 536 537 for (i=0; i<decksize; i++) { 538 ideck[i]->visible = FALSE; 539 ideck[i]->paid = FALSE; 540 } 541 for (i = decksize-1; i>=0; i--) { 542 j = random() % decksize; 543 if (i != j) { 544 temp = ideck[i]; 545 ideck[i] = ideck[j]; 546 ideck[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, const 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, const 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(const 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(const struct cardtype *cp1, const 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(const struct cardtype *cp1, const 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(const 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 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("%4ld%8ld%9ld", this.hand, game.hand, total.hand); 1141 move(tboxrow + 3, boxcol + 13); 1142 printw("%4ld%8ld%9ld", this.inspection, game.inspection, 1143 total.inspection); 1144 move(tboxrow + 4, boxcol + 13); 1145 printw("%4ld%8ld%9ld", this.game, game.game, total.game); 1146 move(tboxrow + 5, boxcol + 13); 1147 printw("%4ld%8ld%9ld", this.runs, game.runs, total.runs); 1148 move(tboxrow + 6, boxcol + 13); 1149 printw("%4ld%8ld%9ld", this.information, game.information, 1150 total.information); 1151 move(tboxrow + 7, boxcol + 13); 1152 printw("%4ld%8ld%9ld", this.thinktime, game.thinktime, total.thinktime); 1153 move(tboxrow + 8, boxcol + 13); 1154 printw("%4ld%8ld%9ld", thiscosts, gamecosts, totalcosts); 1155 move(tboxrow + 9, boxcol + 13); 1156 printw("%4ld%8ld%9ld", this.wins, game.wins, total.wins); 1157 move(tboxrow + 10, boxcol + 13); 1158 printw("%4ld%8ld%9ld", this.worth, game.worth, total.worth); 1159 move(tboxrow + 11, boxcol + 13); 1160 printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn, gamereturn, totalreturn); 1161 } 1162 1163 /* 1164 * procedure to move a card from the stock or talon to the tableau 1165 */ 1166 static void 1167 simpletableau(struct cardtype **cp, int des) 1168 { 1169 int origin; 1170 1171 if (notempty(*cp)) { 1172 if (tabok(*cp, des)) { 1173 if (*cp == stock) 1174 origin = stk; 1175 else 1176 origin = tal; 1177 if (tableau[des] == NIL) 1178 bottom[des] = *cp; 1179 transit(cp, &tableau[des]); 1180 length[des]++; 1181 printcard(pilemap[des], length[des], tableau[des]); 1182 timesthru = 0; 1183 if (origin == stk) { 1184 usedstock(); 1185 printcard(stockcol, stockrow, stock); 1186 } else { 1187 usedtalon(); 1188 printcard(taloncol, talonrow, talon); 1189 } 1190 } else 1191 destinerror(); 1192 } 1193 } 1194 1195 /* 1196 * print the tableau 1197 */ 1198 static void 1199 tabprint(int sour, int des) 1200 { 1201 int dlength, slength, i; 1202 struct cardtype *tempcard; 1203 1204 for (i=tabrow; i<=length[sour]; i++) 1205 removecard(pilemap[sour], i); 1206 dlength = length[des] + 1; 1207 slength = length[sour]; 1208 if (slength == tabrow) 1209 printcard(pilemap[des], dlength, tableau[sour]); 1210 else 1211 while (slength != tabrow - 1) { 1212 tempcard = tableau[sour]; 1213 for (i=1; i<=slength-tabrow; i++) 1214 tempcard = tempcard->next; 1215 printcard(pilemap[des], dlength, tempcard); 1216 slength--; 1217 dlength++; 1218 } 1219 } 1220 1221 /* 1222 * procedure to move from the tableau to the tableau 1223 */ 1224 static void 1225 tabtotab(int sour, int des) 1226 { 1227 struct cardtype *temp; 1228 1229 if (notempty(tableau[sour])) { 1230 if (tabok(bottom[sour], des)) { 1231 tabprint(sour, des); 1232 temp = bottom[sour]; 1233 bottom[sour] = NIL; 1234 if (bottom[des] == NIL) 1235 bottom[des] = temp; 1236 temp->next = tableau[des]; 1237 tableau[des] = tableau[sour]; 1238 tableau[sour] = NIL; 1239 length[des] = length[des] + 1240 (length[sour] - (tabrow - 1)); 1241 length[sour] = tabrow - 1; 1242 timesthru = 0; 1243 } else 1244 destinerror(); 1245 } 1246 } 1247 1248 /* 1249 * functions to see if the card can go onto the foundation 1250 */ 1251 static bool 1252 rankhigher(const struct cardtype *cp, int let) 1253 { 1254 if (found[let]->rank == King) 1255 if (cp->rank == Ace) 1256 return(TRUE); 1257 else 1258 return(FALSE); 1259 else if (cp->rank - 1 == found[let]->rank) 1260 return(TRUE); 1261 else 1262 return(FALSE); 1263 } 1264 1265 /* 1266 * function to determine if two cards are the same suit 1267 */ 1268 static int 1269 samesuit(const struct cardtype *cp, int let) 1270 { 1271 if (cp->suit == found[let]->suit) 1272 return (TRUE); 1273 else 1274 return (FALSE); 1275 } 1276 1277 /* 1278 * procedure to move a card to the correct foundation pile 1279 */ 1280 static void 1281 movetofound(struct cardtype **cp, int source) 1282 { 1283 tempbase = 0; 1284 mtfdone = FALSE; 1285 if (notempty(*cp)) { 1286 do { 1287 if (found[tempbase] != NIL) 1288 if (rankhigher(*cp, tempbase) 1289 && samesuit(*cp, tempbase)) { 1290 if (*cp == stock) 1291 mtforigin = stk; 1292 else if (*cp == talon) 1293 mtforigin = tal; 1294 else 1295 mtforigin = tab; 1296 transit(cp, &found[tempbase]); 1297 printcard(pilemap[tempbase], 1298 foundrow, found[tempbase]); 1299 timesthru = 0; 1300 if (mtforigin == stk) { 1301 usedstock(); 1302 printcard(stockcol, stockrow, 1303 stock); 1304 } else if (mtforigin == tal) { 1305 usedtalon(); 1306 printcard(taloncol, talonrow, 1307 talon); 1308 } else { 1309 removecard(pilemap[source], 1310 length[source]); 1311 length[source]--; 1312 } 1313 cardsoff++; 1314 if (infullgame) { 1315 this.wins += valuepercardup; 1316 game.wins += valuepercardup; 1317 total.wins += valuepercardup; 1318 } 1319 mtfdone = TRUE; 1320 } else 1321 tempbase++; 1322 else 1323 tempbase++; 1324 } while ((tempbase != 4) && !mtfdone); 1325 if (!mtfdone) 1326 destinerror(); 1327 } 1328 } 1329 1330 /* 1331 * procedure to get a command 1332 */ 1333 static void 1334 getcmd(int row, int col, const char *cp) 1335 { 1336 char cmd[2] = { '\0', '\0'}, ch; 1337 int i; 1338 1339 i = 0; 1340 move(row, col); 1341 printw("%-24s", cp); 1342 col += 1 + strlen(cp); 1343 move(row, col); 1344 refresh(); 1345 do { 1346 ch = getch() & 0177; 1347 if (ch >= 'A' && ch <= 'Z') 1348 ch += ('a' - 'A'); 1349 if (ch == '\f') { 1350 wrefresh(curscr); 1351 refresh(); 1352 } else if (i >= 2 && ch != erasechar() && ch != killchar()) { 1353 if (ch != '\n' && ch != '\r' && ch != ' ') 1354 write(1, "\007", 1); 1355 } else if (ch == erasechar() && i > 0) { 1356 printw("\b \b"); 1357 refresh(); 1358 cmd[--i] = '\0'; 1359 } else if (ch == killchar() && i > 0) { 1360 while (i > 0) { 1361 printw("\b \b"); 1362 cmd[--i] = '\0'; 1363 } 1364 refresh(); 1365 } else if (ch == '\032') { /* Control-Z */ 1366 suspend(); 1367 move(row, col + i); 1368 refresh(); 1369 } else if (isprint((unsigned char)ch)) { 1370 cmd[i++] = ch; 1371 addch(ch); 1372 refresh(); 1373 } 1374 } while (ch != '\n' && ch != '\r' && ch != ' '); 1375 srcpile = cmd[0]; 1376 destpile = cmd[1]; 1377 } 1378 1379 /* 1380 * Suspend the game (shell escape if no process control on system) 1381 */ 1382 static void 1383 suspend(void) 1384 { 1385 #ifndef SIGTSTP 1386 char *sh; 1387 #endif 1388 1389 updatebettinginfo(); 1390 move(21, 0); 1391 refresh(); 1392 if (dbfd != -1) { 1393 lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET); 1394 write(dbfd, (char *)&total, sizeof(total)); 1395 } 1396 kill(getpid(), SIGTSTP); 1397 raw(); 1398 noecho(); 1399 } 1400 1401 /* 1402 * procedure to evaluate and make the specific moves 1403 */ 1404 static void 1405 movecard(void) 1406 { 1407 int source, dest; 1408 char osrcpile, odestpile; 1409 1410 source = dest = 0; 1411 done = FALSE; 1412 errmsg = FALSE; 1413 do { 1414 if (talon == NIL && hand != NIL) 1415 movetotalon(); 1416 if (cardsoff == 52) { 1417 refresh(); 1418 srcpile = 'q'; 1419 } else if (!startedgame) { 1420 move(msgrow, msgcol); 1421 errmsg = TRUE; 1422 switch (34 - taloncnt - cinhand) { 1423 default: 1424 errmsg = FALSE; 1425 break; 1426 case 1: 1427 printw("One card used from talon "); 1428 break; 1429 case 2: 1430 printw("Two cards used from talon "); 1431 break; 1432 case 3: 1433 printw(">3< cards used from talon "); 1434 break; 1435 } 1436 getcmd(moverow, movecol, "Move:"); 1437 } else 1438 getcmd(moverow, movecol, "Move:"); 1439 clearmsg(); 1440 if (srcpile >= '1' && srcpile <= '4') 1441 source = (int) (srcpile - '1'); 1442 if (destpile >= '1' && destpile <= '4') 1443 dest = (int) (destpile - '1'); 1444 if (!startedgame && 1445 (srcpile == 't' || srcpile == 's' || srcpile == 'h' || 1446 srcpile == '1' || srcpile == '2' || srcpile == '3' || 1447 srcpile == '4')) { 1448 startedgame = TRUE; 1449 osrcpile = srcpile; 1450 odestpile = destpile; 1451 if (status != BETTINGBOX) 1452 srcpile = 'y'; 1453 else do { 1454 getcmd(moverow, movecol, "Inspect game?"); 1455 } while (srcpile != 'y' && srcpile != 'n'); 1456 if (srcpile == 'n') { 1457 srcpile = 'q'; 1458 } else { 1459 this.inspection += costofinspection; 1460 game.inspection += costofinspection; 1461 total.inspection += costofinspection; 1462 srcpile = osrcpile; 1463 destpile = odestpile; 1464 } 1465 } 1466 switch (srcpile) { 1467 case 't': 1468 if (destpile == 'f' || destpile == 'F') 1469 movetofound(&talon, source); 1470 else if (destpile >= '1' && destpile <= '4') 1471 simpletableau(&talon, dest); 1472 else 1473 dumberror(); 1474 break; 1475 case 's': 1476 if (destpile == 'f' || destpile == 'F') 1477 movetofound(&stock, source); 1478 else if (destpile >= '1' && destpile <= '4') 1479 simpletableau(&stock, dest); 1480 else dumberror(); 1481 break; 1482 case 'h': 1483 if (destpile != 't' && destpile != 'T') { 1484 dumberror(); 1485 break; 1486 } 1487 if (infullgame) { 1488 movetotalon(); 1489 break; 1490 } 1491 if (status == BETTINGBOX) { 1492 do { 1493 getcmd(moverow, movecol, 1494 "Buy game?"); 1495 } while (srcpile != 'y' && 1496 srcpile != 'n'); 1497 if (srcpile == 'n') { 1498 showcards(); 1499 done = TRUE; 1500 break; 1501 } 1502 } 1503 infullgame = TRUE; 1504 this.wins += valuepercardup * cardsoff; 1505 game.wins += valuepercardup * cardsoff; 1506 total.wins += valuepercardup * cardsoff; 1507 this.game += costofgame; 1508 game.game += costofgame; 1509 total.game += costofgame; 1510 movetotalon(); 1511 break; 1512 case 'q': 1513 showcards(); 1514 done = TRUE; 1515 break; 1516 case 'b': 1517 printtopbettingbox(); 1518 printbottombettingbox(); 1519 status = BETTINGBOX; 1520 break; 1521 case 'x': 1522 clearabovemovebox(); 1523 clearbelowmovebox(); 1524 status = NOBOX; 1525 break; 1526 case 'i': 1527 printtopinstructions(); 1528 printbottominstructions(); 1529 status = INSTRUCTIONBOX; 1530 break; 1531 case 'c': 1532 Cflag = !Cflag; 1533 if (Cflag) 1534 showstat(); 1535 else 1536 clearstat(); 1537 break; 1538 case '1': case '2': case '3': case '4': 1539 if (destpile == 'f' || destpile == 'F') 1540 movetofound(&tableau[source], source); 1541 else if (destpile >= '1' && destpile <= '4') 1542 tabtotab(source, dest); 1543 else dumberror(); 1544 break; 1545 default: 1546 dumberror(); 1547 } 1548 fndbase(&stock, stockcol, stockrow); 1549 fndbase(&talon, taloncol, talonrow); 1550 updatebettinginfo(); 1551 } while (!done); 1552 } 1553 1554 static const char *const basicinstructions[] = { 1555 "Here are brief instructions to the game of Canfield:\n\n", 1556 " If you have never played solitaire before, it is recom-\n", 1557 "mended that you consult a solitaire instruction book. In\n", 1558 "Canfield, tableau cards may be built on each other downward\n", 1559 "in alternate colors. An entire pile must be moved as a unit\n", 1560 "in building. Top cards of the piles are available to be able\n", 1561 "to be played on foundations, but never into empty spaces.\n\n", 1562 " Spaces must be filled from the stock. The top card of\n", 1563 "the stock also is available to be played on foundations or\n", 1564 "built on tableau piles. After the stock is exhausted, ta-\n", 1565 "bleau spaces may be filled from the talon and the player may\n", 1566 "keep them open until he wishes to use them.\n\n", 1567 " Cards are dealt from the hand to the talon by threes\n", 1568 "and this repeats until there are no more cards in the hand\n", 1569 "or the player quits. To have cards dealt onto the talon the\n", 1570 "player types 'ht' for his move. Foundation base cards are\n", 1571 "also automatically moved to the foundation when they become\n", 1572 "available.\n\n", 1573 "push any key when you are finished: ", 1574 0 }; 1575 1576 static const char *const bettinginstructions[] = { 1577 " The rules for betting are somewhat less strict than\n", 1578 "those used in the official version of the game. The initial\n", 1579 "deal costs $13. You may quit at this point or inspect the\n", 1580 "game. Inspection costs $13 and allows you to make as many\n", 1581 "moves as is possible without moving any cards from your hand\n", 1582 "to the talon. (the initial deal places three cards on the\n", 1583 "talon; if all these cards are used, three more are made\n", 1584 "available) Finally, if the game seems interesting, you must\n", 1585 "pay the final installment of $26. At this point you are\n", 1586 "credited at the rate of $5 for each card on the foundation;\n", 1587 "as the game progresses you are credited with $5 for each\n", 1588 "card that is moved to the foundation. Each run through the\n", 1589 "hand after the first costs $5. The card counting feature\n", 1590 "costs $1 for each unknown card that is identified. If the\n", 1591 "information is toggled on, you are only charged for cards\n", 1592 "that became visible since it was last turned on. Thus the\n", 1593 "maximum cost of information is $34. Playing time is charged\n", 1594 "at a rate of $1 per minute.\n\n", 1595 "push any key when you are finished: ", 1596 0 }; 1597 1598 /* 1599 * procedure to printout instructions 1600 */ 1601 static void 1602 instruct(void) 1603 { 1604 const char *const *cp; 1605 1606 move(originrow, origincol); 1607 printw("This is the game of solitaire called Canfield. Do\n"); 1608 printw("you want instructions for the game?"); 1609 do { 1610 getcmd(originrow + 3, origincol, "y or n?"); 1611 } while (srcpile != 'y' && srcpile != 'n'); 1612 if (srcpile == 'n') 1613 return; 1614 clear(); 1615 for (cp = basicinstructions; *cp != 0; cp++) 1616 printw("%s", *cp); 1617 refresh(); 1618 getch(); 1619 clear(); 1620 move(originrow, origincol); 1621 printw("Do you want instructions for betting?"); 1622 do { 1623 getcmd(originrow + 2, origincol, "y or n?"); 1624 } while (srcpile != 'y' && srcpile != 'n'); 1625 if (srcpile == 'n') 1626 return; 1627 clear(); 1628 for (cp = bettinginstructions; *cp != 0; cp++) 1629 printw("%s", *cp); 1630 refresh(); 1631 getch(); 1632 } 1633 1634 /* 1635 * procedure to initialize the game 1636 */ 1637 static void 1638 initall(void) 1639 { 1640 ssize_t i; 1641 1642 srandom(time(NULL)); 1643 time(&acctstart); 1644 initdeck(deck); 1645 uid = getuid(); 1646 dbfd = open(_PATH_SCORE, O_RDWR); 1647 1648 /* Revoke setgid privileges */ 1649 setgid(getgid()); 1650 1651 if (dbfd < 0) 1652 return; 1653 if (dbfd < 3) 1654 exit(1); 1655 if (lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET) < 0) { 1656 close(dbfd); 1657 dbfd = -1; 1658 return; 1659 } 1660 i = read(dbfd, (char *)&total, sizeof(total)); 1661 if (i < 0) { 1662 close(dbfd); 1663 dbfd = -1; 1664 return; 1665 } 1666 } 1667 1668 /* 1669 * procedure to end the game 1670 */ 1671 static bool 1672 finish(void) 1673 { 1674 int row, col; 1675 1676 if (cardsoff == 52) { 1677 getcmd(moverow, movecol, "Hit return to exit"); 1678 clear(); 1679 refresh(); 1680 move(originrow, origincol); 1681 printw("CONGRATULATIONS!\n"); 1682 printw("You won the game. That is a feat to be proud of.\n"); 1683 row = originrow + 5; 1684 col = origincol; 1685 } else { 1686 move(msgrow, msgcol); 1687 printw("You got %d card", cardsoff); 1688 if (cardsoff > 1) 1689 printw("s"); 1690 printw(" off "); 1691 move(msgrow, msgcol); 1692 row = moverow; 1693 col = movecol; 1694 } 1695 do { 1696 getcmd(row, col, "Play again (y or n)?"); 1697 } while (srcpile != 'y' && srcpile != 'n'); 1698 errmsg = TRUE; 1699 clearmsg(); 1700 if (srcpile == 'y') 1701 return (FALSE); 1702 else 1703 return (TRUE); 1704 } 1705 1706 /* 1707 * procedure to clean up and exit 1708 */ 1709 static void 1710 cleanup(int dummy __unused) 1711 { 1712 1713 total.thinktime += 1; 1714 status = NOBOX; 1715 updatebettinginfo(); 1716 if (dbfd != -1) { 1717 lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET); 1718 write(dbfd, (char *)&total, sizeof(total)); 1719 close(dbfd); 1720 } 1721 clear(); 1722 move(22,0); 1723 refresh(); 1724 endwin(); 1725 exit(0); 1726 /* NOTREACHED */ 1727 } 1728 1729 /* 1730 * Field an interrupt. 1731 */ 1732 static void 1733 askquit(int dummy __unused) 1734 { 1735 move(msgrow, msgcol); 1736 printw("Really wish to quit? "); 1737 do { 1738 getcmd(moverow, movecol, "y or n?"); 1739 } while (srcpile != 'y' && srcpile != 'n'); 1740 clearmsg(); 1741 if (srcpile == 'y') 1742 cleanup(0); 1743 signal(SIGINT, askquit); 1744 } 1745 1746 /* 1747 * Can you tell that this used to be a Pascal program? 1748 */ 1749 int 1750 main(void) 1751 { 1752 #ifdef MAXLOAD 1753 double vec[3]; 1754 1755 loadav(vec); 1756 if (vec[2] >= MAXLOAD) { 1757 puts("The system load is too high. Try again later."); 1758 exit(0); 1759 } 1760 #endif 1761 signal(SIGINT, askquit); 1762 signal(SIGHUP, cleanup); 1763 signal(SIGTERM, cleanup); 1764 if (!initscr()) { 1765 fprintf(stderr, "couldn't initialize screen\n"); 1766 exit (0); 1767 } 1768 raw(); 1769 noecho(); 1770 initall(); 1771 instruct(); 1772 makeboard(); 1773 for (;;) { 1774 startgame(); 1775 movecard(); 1776 if (finish()) 1777 break; 1778 if (cardsoff == 52) 1779 makeboard(); 1780 else 1781 cleanupboard(); 1782 } 1783 cleanup(0); 1784 /* NOTREACHED */ 1785 return(0); 1786 } 1787