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