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