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