Fix some typos in messages/manpages.
[dragonfly.git] / games / canfield / canfield / canfield.c
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 }