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