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