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