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