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