2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved.
30 * @(#)canfield.c 8.1 (Berkeley) 5/31/93
31 * $FreeBSD: src/games/canfield/canfield/canfield.c,v 1.11 1999/12/12 07:25:13 billf Exp $
35 * The canfield program
38 * Originally written: Steve Levine
39 * Converted to use curses and debugged: Steve Feldman
40 * Card counting: Kirk McKusick and Mikey Olson
41 * User interface cleanups: Eric Allman and Kirk McKusick
42 * Betting by Kirk McKusick
45 #include <sys/types.h>
56 #include "pathnames.h"
87 #define handstatrow 21
89 #define talonstatrow 22
90 #define talonstatcol 7
91 #define stockstatrow 23
92 #define stockstatcol 7
112 #define INCRHAND(row, col) {\
114 if (row < ctoprow) {\
119 #define DECRHAND(row, col) {\
121 if (row > cbotrow) {\
134 struct cardtype
*next
;
137 #define NIL ((struct cardtype *) -1)
139 struct cardtype
*deck
[decksize
];
140 struct cardtype cards
[decksize
];
141 struct cardtype
*bottom
[4], *found
[4], *tableau
[4];
142 struct cardtype
*talon
, *hand
, *stock
, *basecard
;
144 int cardsoff
, base
, cinhand
, taloncnt
, stockcnt
, timesthru
;
145 char suitmap
[4] = {spades
, clubs
, hearts
, diamonds
};
146 char colormap
[4] = {black
, black
, red
, red
};
147 char pilemap
[4] = {atabcol
, btabcol
, ctabcol
, dtabcol
};
148 char srcpile
, destpile
;
149 int mtforigin
, tempbase
;
150 int coldcol
, cnewcol
, coldrow
, cnewrow
;
152 bool mtfdone
, Cflag
= FALSE
;
153 #define INSTRUCTIONBOX 1
156 int status
= INSTRUCTIONBOX
;
160 * Basic betting costs
162 #define costofhand 13
163 #define costofinspection 13
164 #define costofgame 26
165 #define costofrunthroughhand 5
166 #define costofinformation 1
167 #define secondsperdollar 60
168 #define maxtimecharge 3
169 #define valuepercardup 5
171 * Variables associated with betting
174 long hand
; /* cost of dealing hand */
175 long inspection
; /* cost of inspecting hand */
176 long game
; /* cost of buying game */
177 long runs
; /* cost of running through hands */
178 long information
; /* cost of information */
179 long thinktime
; /* cost of thinking time */
180 long wins
; /* total winnings */
181 long worth
; /* net worth after costs */
183 struct betinfo
this, game
, total
;
184 bool startedgame
= FALSE
, infullgame
= FALSE
;
188 static void askquit(int);
189 static void cleanup(int);
190 static void cleanupboard(void);
191 static void clearabovemovebox(void);
192 static void clearbelowmovebox(void);
193 static void clearmsg(void);
194 static void clearstat(void);
195 static void destinerror(void);
196 static bool diffcolor(struct cardtype
*, struct cardtype
*);
197 static void dumberror(void);
198 static bool finish(void);
199 static void fndbase(struct cardtype
**, int, int);
200 static void getcmd(int, int, const char *);
201 static void initall(void);
202 static void initdeck(struct cardtype
*[]);
203 static void initgame(void);
204 static void instruct(void);
205 static void makeboard(void);
206 static void movebox(void);
207 static void movecard(void);
208 static void movetofound(struct cardtype
**, int);
209 static void movetotalon(void);
210 static bool notempty(struct cardtype
*);
211 static void printbottombettingbox(void);
212 static void printbottominstructions(void);
213 static void printcard(int, int, struct cardtype
*);
214 static void printrank(int, int, struct cardtype
*, bool);
215 static void printtopbettingbox(void);
216 static void printtopinstructions(void);
217 static bool rankhigher(struct cardtype
*, int);
218 static bool ranklower(struct cardtype
*, struct cardtype
*);
219 static void removecard(int, int);
220 static bool samesuit(struct cardtype
*, int);
221 static void showcards(void);
222 static void showstat(void);
223 static void shuffle(struct cardtype
*[]);
224 static void simpletableau(struct cardtype
**, int);
225 static void startgame(void);
226 static void suspend(void);
227 static bool tabok(struct cardtype
*, int);
228 static void tabprint(int, int);
229 static void tabtotab(int, int);
230 static void transit(struct cardtype
**, struct cardtype
**);
231 static void updatebettinginfo(void);
232 static void usedstock(void);
233 static void usedtalon(void);
236 * The following procedures print the board onto the screen using the
237 * addressible cursor. The end of these procedures will also be
238 * separated from the rest of the program.
240 * procedure to set the move command box
247 printtopbettingbox();
253 printtopinstructions();
256 move(moverow
, boxcol
);
258 move(msgrow
, boxcol
);
262 printbottombettingbox();
268 printbottominstructions();
275 * print directions above move box
278 printtopinstructions(void)
280 move(tboxrow
, boxcol
);
281 printw("*----------------------------------*");
282 move(tboxrow
+ 1, boxcol
);
284 move(tboxrow
+ 2, boxcol
);
285 printw("|s# = stock to tableau |");
286 move(tboxrow
+ 3, boxcol
);
287 printw("|sf = stock to foundation |");
288 move(tboxrow
+ 4, boxcol
);
289 printw("|t# = talon to tableau |");
290 move(tboxrow
+ 5, boxcol
);
291 printw("|tf = talon to foundation |");
292 move(tboxrow
+ 6, boxcol
);
293 printw("|## = tableau to tableau |");
294 move(tboxrow
+ 7, boxcol
);
295 printw("|#f = tableau to foundation |");
296 move(tboxrow
+ 8, boxcol
);
297 printw("|ht = hand to talon |");
298 move(tboxrow
+ 9, boxcol
);
299 printw("|c = toggle card counting |");
300 move(tboxrow
+ 10, boxcol
);
301 printw("|b = present betting information |");
302 move(tboxrow
+ 11, boxcol
);
303 printw("|q = quit to end the game |");
304 move(tboxrow
+ 12, boxcol
);
305 printw("|==================================|");
309 * Print the betting box.
312 printtopbettingbox(void)
315 move(tboxrow
, boxcol
);
316 printw("*----------------------------------*");
317 move(tboxrow
+ 1, boxcol
);
318 printw("|Costs Hand Game Total |");
319 move(tboxrow
+ 2, boxcol
);
321 move(tboxrow
+ 3, boxcol
);
322 printw("| Inspections |");
323 move(tboxrow
+ 4, boxcol
);
325 move(tboxrow
+ 5, boxcol
);
327 move(tboxrow
+ 6, boxcol
);
328 printw("| Information |");
329 move(tboxrow
+ 7, boxcol
);
330 printw("| Think time |");
331 move(tboxrow
+ 8, boxcol
);
332 printw("|Total Costs |");
333 move(tboxrow
+ 9, boxcol
);
334 printw("|Winnings |");
335 move(tboxrow
+ 10, boxcol
);
336 printw("|Net Worth |");
337 move(tboxrow
+ 11, boxcol
);
339 move(tboxrow
+ 12, boxcol
);
340 printw("|==================================|");
344 * clear info above move box
347 clearabovemovebox(void)
351 for (i
= 0; i
<= 11; i
++) {
352 move(tboxrow
+ i
, boxcol
);
355 move(tboxrow
+ 12, boxcol
);
356 printw("*----------------------------------*");
360 * print instructions below move box
363 printbottominstructions(void)
365 move(bboxrow
, boxcol
);
366 printw("|Replace # with the number of the |");
367 move(bboxrow
+ 1, boxcol
);
368 printw("|tableau you want. |");
369 move(bboxrow
+ 2, boxcol
);
370 printw("*----------------------------------*");
374 * print betting information below move box
377 printbottombettingbox(void)
379 move(bboxrow
, boxcol
);
380 printw("|x = toggle information box |");
381 move(bboxrow
+ 1, boxcol
);
382 printw("|i = list playing instructions |");
383 move(bboxrow
+ 2, boxcol
);
384 printw("*----------------------------------*");
388 * clear info below move box
391 clearbelowmovebox(void)
395 move(bboxrow
, boxcol
);
396 printw("*----------------------------------*");
397 for (i
= 1; i
<= 2; i
++) {
398 move(bboxrow
+ i
, boxcol
);
404 * procedure to put the board on the screen using addressable cursor
411 move(titlerow
, titlecol
);
412 printw("=-> CANFIELD <-=");
413 move(fttlrow
, fttlcol
);
414 printw("foundation");
415 move(foundrow
- 1, fttlcol
);
416 printw("=---= =---= =---= =---=");
417 move(foundrow
, fttlcol
);
418 printw("| | | | | | | |");
419 move(foundrow
+ 1, fttlcol
);
420 printw("=---= =---= =---= =---=");
421 move(ottlrow
, sidecol
);
422 printw("stock tableau");
423 move(stockrow
- 1, sidecol
);
425 move(stockrow
, sidecol
);
427 move(stockrow
+ 1, sidecol
);
429 move(talonrow
- 2, sidecol
);
431 move(talonrow
- 1, sidecol
);
433 move(talonrow
, sidecol
);
435 move(talonrow
+ 1, sidecol
);
437 move(tabrow
- 1, atabcol
);
438 printw("-1- -2- -3- -4-");
443 * clean up the board for another game
449 struct cardtype
*ptr
;
454 for(ptr
= stock
, row
= stockrow
;
456 ptr
= ptr
->next
, row
++) {
462 move(stockrow
+ 1, sidecol
);
464 move(talonrow
- 2, sidecol
);
466 move(talonrow
- 1, sidecol
);
468 move(talonrow
+ 1, sidecol
);
471 move(stockrow
, sidecol
);
473 move(talonrow
, sidecol
);
475 move(foundrow
, fttlcol
);
476 printw("| | | | | | | |");
477 for (cnt
= 0; cnt
< 4; cnt
++) {
492 for(ptr
= tableau
[cnt
], row
= tabrow
;
494 ptr
= ptr
->next
, row
++)
495 removecard(col
, row
);
500 * procedure to create a deck of cards
503 initdeck(struct cardtype
*ideck
[])
511 for (scnt
=0; scnt
<4; scnt
++) {
513 for (r
=Ace
; r
<=King
; r
++) {
514 ideck
[i
] = &cards
[i
];
517 cards
[i
].color
= colormap
[scnt
];
525 * procedure to shuffle the deck
528 shuffle(struct cardtype
*ideck
[])
531 struct cardtype
*temp
;
533 for (i
=0; i
<decksize
; i
++) {
534 ideck
[i
]->visible
= FALSE
;
535 ideck
[i
]->paid
= FALSE
;
537 for (i
= decksize
-1; i
>=0; i
--) {
538 j
= random() % decksize
;
548 * procedure to remove the card from the board
551 removecard(int a
, int b
)
558 * procedure to print the cards on the board
561 printrank(int a
, int b
, struct cardtype
*cp
, bool inverse
)
569 case 2: case 3: case 4: case 5: case 6: case 7:
570 case 8: case 9: case 10:
571 printw("%d", cp
->rank
);
590 * procedure to print out a card
593 printcard(int a
, int b
, struct cardtype
*cp
)
597 else if (cp
->visible
== FALSE
) {
601 bool inverse
= (cp
->suit
== 'd' || cp
->suit
== 'h');
603 printrank(a
, b
, cp
, inverse
);
613 * procedure to move the top card from one location to the top
614 * of another location. The pointers always point to the top
618 transit(struct cardtype
**source
, struct cardtype
**dest
)
620 struct cardtype
*temp
;
623 *source
= (*source
)->next
;
629 * Procedure to set the cards on the foundation base when available.
630 * Note that it is only called on a foundation pile at the beginning of
631 * the game, so the pile will have exactly one card in it.
634 fndbase(struct cardtype
**cp
, int column
, int row
)
640 if ((*cp
)->rank
== basecard
->rank
) {
642 printcard(pilemap
[base
], foundrow
, *cp
);
643 if (*cp
== tableau
[0])
644 length
[0] = length
[0] - 1;
645 if (*cp
== tableau
[1])
646 length
[1] = length
[1] - 1;
647 if (*cp
== tableau
[2])
648 length
[2] = length
[2] - 1;
649 if (*cp
== tableau
[3])
650 length
[3] = length
[3] - 1;
651 transit(cp
, &found
[base
]);
657 printcard(column
, row
, *cp
);
660 removecard(column
, row
);
665 this.wins
+= valuepercardup
;
666 game
.wins
+= valuepercardup
;
667 total
.wins
+= valuepercardup
;
671 } while (nomore
== FALSE
);
675 * procedure to initialize the things necessary for the game
682 for (i
=0; i
<18; i
++) {
683 deck
[i
]->visible
= TRUE
;
684 deck
[i
]->paid
= TRUE
;
688 for (i
=12; i
>=1; i
--)
689 deck
[i
]->next
= deck
[i
- 1];
692 deck
[13]->next
= NIL
;
696 for (i
=14; i
<18; i
++) {
697 tableau
[i
- 14] = deck
[i
];
700 for (i
=0; i
<4; i
++) {
701 bottom
[i
] = tableau
[i
];
705 for (i
=18; i
<decksize
-1; i
++)
706 deck
[i
]->next
= deck
[i
+ 1];
707 deck
[decksize
-1]->next
= NIL
;
717 cnewcol
= cinitcol
+ cwidthcol
;
721 * procedure to print the beginning cards and to start each game
730 this.hand
= costofhand
;
731 game
.hand
+= costofhand
;
732 total
.hand
+= costofhand
;
736 this.information
= 0;
741 printcard(foundcol
, foundrow
, found
[0]);
742 printcard(stockcol
, stockrow
, stock
);
743 printcard(atabcol
, tabrow
, tableau
[0]);
744 printcard(btabcol
, tabrow
, tableau
[1]);
745 printcard(ctabcol
, tabrow
, tableau
[2]);
746 printcard(dtabcol
, tabrow
, tableau
[3]);
747 printcard(taloncol
, talonrow
, talon
);
748 move(foundrow
- 2, basecol
);
750 move(foundrow
- 1, basecol
);
752 printrank(basecol
, foundrow
, found
[0], 0);
754 fndbase(&tableau
[j
], pilemap
[j
], tabrow
);
755 fndbase(&stock
, stockcol
, stockrow
);
756 showstat(); /* show card counting info to cheaters */
762 * procedure to clear the message printed from an error
769 if (errmsg
== TRUE
) {
771 move(msgrow
, msgcol
);
779 * procedure to print an error message if the move is not listed
785 move(msgrow
, msgcol
);
786 printw("Not a proper move ");
790 * procedure to print an error message if the move is not possible
796 move(msgrow
, msgcol
);
797 printw("Error: Can't move there");
801 * function to see if the source has cards in it
804 notempty(struct cardtype
*cp
)
808 move(msgrow
, msgcol
);
809 printw("Error: no cards to move");
816 * function to see if the rank of one card is less than another
819 ranklower(struct cardtype
*cp1
, struct cardtype
*cp2
)
821 if (cp2
->rank
== Ace
)
822 if (cp1
->rank
== King
)
826 else if (cp1
->rank
+ 1 == cp2
->rank
)
833 * function to check the cardcolor for moving to a tableau
836 diffcolor(struct cardtype
*cp1
, struct cardtype
*cp2
)
838 if (cp1
->color
== cp2
->color
)
845 * function to see if the card can move to the tableau
848 tabok(struct cardtype
*cp
, int des
)
850 if ((cp
== stock
) && (tableau
[des
] == NIL
))
852 else if (tableau
[des
] == NIL
)
854 cp
!= bottom
[0] && cp
!= bottom
[1] &&
855 cp
!= bottom
[2] && cp
!= bottom
[3])
859 else if (ranklower(cp
, tableau
[des
]) && diffcolor(cp
, tableau
[des
]))
866 * procedure to turn the cards onto the talon from the deck
873 if (cinhand
<= 3 && cinhand
> 0) {
874 move(msgrow
, msgcol
);
875 printw("Hand is now empty ");
879 else if (cinhand
> 0)
881 else if (talon
!= NIL
) {
884 move(msgrow
, msgcol
);
885 if (timesthru
!= 4) {
886 printw("Talon is now the new hand");
887 this.runs
+= costofrunthroughhand
;
888 game
.runs
+= costofrunthroughhand
;
889 total
.runs
+= costofrunthroughhand
;
890 while (talon
!= NIL
) {
891 transit(&talon
, &hand
);
902 cnewcol
= cinitcol
+ cwidthcol
;
908 printw("I believe you have lost");
914 move(msgrow
, msgcol
);
915 printw("Talon and hand are empty");
918 for (i
=0; i
<fin
; i
++) {
919 transit(&hand
, &talon
);
920 INCRHAND(cnewrow
, cnewcol
);
921 INCRHAND(coldrow
, coldcol
);
922 removecard(cnewcol
, cnewrow
);
924 talon
->visible
= TRUE
;
926 if (talon
->paid
== FALSE
&& talon
->visible
== TRUE
) {
927 this.information
+= costofinformation
;
928 game
.information
+= costofinformation
;
929 total
.information
+= costofinformation
;
932 printcard(coldcol
, coldrow
, talon
);
936 printcard(taloncol
, talonrow
, talon
);
940 move(handstatrow
, handstatcol
);
941 printw("%3d", cinhand
);
942 move(talonstatrow
, talonstatcol
);
943 printw("%3d", taloncnt
);
945 fndbase(&talon
, taloncol
, talonrow
);
951 * procedure to print card counting info on screen
957 struct cardtype
*ptr
;
961 move(talonstatrow
, talonstatcol
- 7);
962 printw("Talon: %3d", taloncnt
);
963 move(handstatrow
, handstatcol
- 7);
964 printw("Hand: %3d", cinhand
);
965 move(stockstatrow
, stockstatcol
- 7);
966 printw("Stock: %3d", stockcnt
);
967 for ( row
= coldrow
, col
= coldcol
, ptr
= talon
;
970 if (ptr
->paid
== FALSE
&& ptr
->visible
== TRUE
) {
972 this.information
+= costofinformation
;
973 game
.information
+= costofinformation
;
974 total
.information
+= costofinformation
;
976 printcard(col
, row
, ptr
);
979 for ( row
= cnewrow
, col
= cnewcol
, ptr
= hand
;
982 if (ptr
->paid
== FALSE
&& ptr
->visible
== TRUE
) {
984 this.information
+= costofinformation
;
985 game
.information
+= costofinformation
;
986 total
.information
+= costofinformation
;
989 printcard(col
, row
, ptr
);
994 * procedure to clear card counting info from screen
1001 move(talonstatrow
, talonstatcol
- 7);
1003 move(handstatrow
, handstatcol
- 7);
1005 move(stockstatrow
, stockstatcol
- 7);
1007 for ( row
= ctoprow
; row
<= cbotrow
; row
++ ) {
1008 move(row
, cinitcol
);
1009 printw("%56s", " ");
1014 * procedure to update card counting base
1019 removecard(coldcol
, coldrow
);
1020 DECRHAND(coldrow
, coldcol
);
1021 if (talon
!= NIL
&& (talon
->visible
== FALSE
)) {
1022 talon
->visible
= TRUE
;
1024 this.information
+= costofinformation
;
1025 game
.information
+= costofinformation
;
1026 total
.information
+= costofinformation
;
1028 printcard(coldcol
, coldrow
, talon
);
1033 move(talonstatrow
, talonstatcol
);
1034 printw("%3d", taloncnt
);
1039 * procedure to update stock card counting base
1046 move(stockstatrow
, stockstatcol
);
1047 printw("%3d", stockcnt
);
1052 * let 'em know how they lost!
1057 struct cardtype
*ptr
;
1060 if (!Cflag
|| cardsoff
== 52)
1062 for (ptr
= talon
; ptr
!= NIL
; ptr
= ptr
->next
) {
1063 ptr
->visible
= TRUE
;
1066 for (ptr
= hand
; ptr
!= NIL
; ptr
= ptr
->next
) {
1067 ptr
->visible
= TRUE
;
1071 move(stockrow
+ 1, sidecol
);
1073 move(talonrow
- 2, sidecol
);
1075 move(talonrow
- 1, sidecol
);
1077 move(talonrow
, sidecol
);
1079 move(talonrow
+ 1, sidecol
);
1081 for (ptr
= stock
, row
= stockrow
; ptr
!= NIL
; ptr
= ptr
->next
, row
++) {
1082 move(row
, stockcol
- 1);
1084 printcard(stockcol
, row
, ptr
);
1087 move(row
, stockcol
- 1);
1091 move(handstatrow
, handstatcol
- 7);
1093 move(row
, stockcol
- 1);
1095 if ( cardsoff
== 52 )
1096 getcmd(moverow
, movecol
, "Hit return to exit");
1100 * procedure to update the betting values
1103 updatebettinginfo(void)
1105 long thiscosts
, gamecosts
, totalcosts
;
1106 double thisreturn
, gamereturn
, totalreturn
;
1111 dollars
= (now
- acctstart
) / secondsperdollar
;
1113 acctstart
+= dollars
* secondsperdollar
;
1114 if (dollars
> maxtimecharge
)
1115 dollars
= maxtimecharge
;
1116 this.thinktime
+= dollars
;
1117 game
.thinktime
+= dollars
;
1118 total
.thinktime
+= dollars
;
1120 thiscosts
= this.hand
+ this.inspection
+ this.game
+
1121 this.runs
+ this.information
+ this.thinktime
;
1122 gamecosts
= game
.hand
+ game
.inspection
+ game
.game
+
1123 game
.runs
+ game
.information
+ game
.thinktime
;
1124 totalcosts
= total
.hand
+ total
.inspection
+ total
.game
+
1125 total
.runs
+ total
.information
+ total
.thinktime
;
1126 this.worth
= this.wins
- thiscosts
;
1127 game
.worth
= game
.wins
- gamecosts
;
1128 total
.worth
= total
.wins
- totalcosts
;
1129 thisreturn
= ((double)this.wins
/ (double)thiscosts
- 1.0) * 100.0;
1130 gamereturn
= ((double)game
.wins
/ (double)gamecosts
- 1.0) * 100.0;
1131 totalreturn
= ((double)total
.wins
/ (double)totalcosts
- 1.0) * 100.0;
1132 if (status
!= BETTINGBOX
)
1134 move(tboxrow
+ 2, boxcol
+ 13);
1135 printw("%4d%8d%9d", this.hand
, game
.hand
, total
.hand
);
1136 move(tboxrow
+ 3, boxcol
+ 13);
1137 printw("%4d%8d%9d", this.inspection
, game
.inspection
, total
.inspection
);
1138 move(tboxrow
+ 4, boxcol
+ 13);
1139 printw("%4d%8d%9d", this.game
, game
.game
, total
.game
);
1140 move(tboxrow
+ 5, boxcol
+ 13);
1141 printw("%4d%8d%9d", this.runs
, game
.runs
, total
.runs
);
1142 move(tboxrow
+ 6, boxcol
+ 13);
1143 printw("%4d%8d%9d", this.information
, game
.information
,
1145 move(tboxrow
+ 7, boxcol
+ 13);
1146 printw("%4d%8d%9d", this.thinktime
, game
.thinktime
, total
.thinktime
);
1147 move(tboxrow
+ 8, boxcol
+ 13);
1148 printw("%4d%8d%9d", thiscosts
, gamecosts
, totalcosts
);
1149 move(tboxrow
+ 9, boxcol
+ 13);
1150 printw("%4d%8d%9d", this.wins
, game
.wins
, total
.wins
);
1151 move(tboxrow
+ 10, boxcol
+ 13);
1152 printw("%4d%8d%9d", this.worth
, game
.worth
, total
.worth
);
1153 move(tboxrow
+ 11, boxcol
+ 13);
1154 printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn
, gamereturn
, totalreturn
);
1158 * procedure to move a card from the stock or talon to the tableau
1161 simpletableau(struct cardtype
**cp
, int des
)
1165 if (notempty(*cp
)) {
1166 if (tabok(*cp
, des
)) {
1171 if (tableau
[des
] == NIL
)
1173 transit(cp
, &tableau
[des
]);
1175 printcard(pilemap
[des
], length
[des
], tableau
[des
]);
1177 if (origin
== stk
) {
1179 printcard(stockcol
, stockrow
, stock
);
1182 printcard(taloncol
, talonrow
, talon
);
1193 tabprint(int sour
, int des
)
1195 int dlength
, slength
, i
;
1196 struct cardtype
*tempcard
;
1198 for (i
=tabrow
; i
<=length
[sour
]; i
++)
1199 removecard(pilemap
[sour
], i
);
1200 dlength
= length
[des
] + 1;
1201 slength
= length
[sour
];
1202 if (slength
== tabrow
)
1203 printcard(pilemap
[des
], dlength
, tableau
[sour
]);
1205 while (slength
!= tabrow
- 1) {
1206 tempcard
= tableau
[sour
];
1207 for (i
=1; i
<=slength
-tabrow
; i
++)
1208 tempcard
= tempcard
->next
;
1209 printcard(pilemap
[des
], dlength
, tempcard
);
1216 * procedure to move from the tableau to the tableau
1219 tabtotab(int sour
, int des
)
1221 struct cardtype
*temp
;
1223 if (notempty(tableau
[sour
])) {
1224 if (tabok(bottom
[sour
], des
)) {
1225 tabprint(sour
, des
);
1226 temp
= bottom
[sour
];
1228 if (bottom
[des
] == NIL
)
1230 temp
->next
= tableau
[des
];
1231 tableau
[des
] = tableau
[sour
];
1232 tableau
[sour
] = NIL
;
1233 length
[des
] = length
[des
] +
1234 (length
[sour
] - (tabrow
- 1));
1235 length
[sour
] = tabrow
- 1;
1243 * functions to see if the card can go onto the foundation
1246 rankhigher(struct cardtype
*cp
, int let
)
1248 if (found
[let
]->rank
== King
)
1249 if (cp
->rank
== Ace
)
1253 else if (cp
->rank
- 1 == found
[let
]->rank
)
1260 * function to determine if two cards are the same suit
1263 samesuit(struct cardtype
*cp
, int let
)
1265 if (cp
->suit
== found
[let
]->suit
)
1272 * procedure to move a card to the correct foundation pile
1275 movetofound(struct cardtype
**cp
, int source
)
1279 if (notempty(*cp
)) {
1281 if (found
[tempbase
] != NIL
)
1282 if (rankhigher(*cp
, tempbase
)
1283 && samesuit(*cp
, tempbase
)) {
1286 else if (*cp
== talon
)
1290 transit(cp
, &found
[tempbase
]);
1291 printcard(pilemap
[tempbase
],
1292 foundrow
, found
[tempbase
]);
1294 if (mtforigin
== stk
) {
1296 printcard(stockcol
, stockrow
,
1298 } else if (mtforigin
== tal
) {
1300 printcard(taloncol
, talonrow
,
1303 removecard(pilemap
[source
],
1309 this.wins
+= valuepercardup
;
1310 game
.wins
+= valuepercardup
;
1311 total
.wins
+= valuepercardup
;
1318 } while ((tempbase
!= 4) && !mtfdone
);
1325 * procedure to get a command
1328 getcmd(int row
, int col
, const char *cp
)
1335 printw("%-24s", cp
);
1336 col
+= 1 + strlen(cp
);
1340 ch
= getch() & 0177;
1341 if (ch
>= 'A' && ch
<= 'Z')
1346 } else if (i
>= 2 && ch
!= erasechar() && ch
!= killchar()) {
1347 if (ch
!= '\n' && ch
!= '\r' && ch
!= ' ')
1348 write(1, "\007", 1);
1349 } else if (ch
== erasechar() && i
> 0) {
1353 } else if (ch
== killchar() && i
> 0) {
1359 } else if (ch
== '\032') { /* Control-Z */
1363 } else if (isprint(ch
)) {
1368 } while (ch
!= '\n' && ch
!= '\r' && ch
!= ' ');
1374 * Suspend the game (shell escape if no process control on system)
1379 updatebettinginfo();
1383 lseek(dbfd
, uid
* sizeof(struct betinfo
), SEEK_SET
);
1384 write(dbfd
, (char *)&total
, sizeof(total
));
1386 kill(getpid(), SIGTSTP
);
1392 * procedure to evaluate and make the specific moves
1398 char osrcpile
, odestpile
;
1405 if (talon
== NIL
&& hand
!= NIL
)
1407 if (cardsoff
== 52) {
1410 } else if (!startedgame
) {
1411 move(msgrow
, msgcol
);
1413 switch (34 - taloncnt
- cinhand
) {
1418 printw("One card used from talon ");
1421 printw("Two cards used from talon ");
1424 printw(">3< cards used from talon ");
1427 getcmd(moverow
, movecol
, "Move:");
1429 getcmd(moverow
, movecol
, "Move:");
1431 if (srcpile
>= '1' && srcpile
<= '4')
1432 source
= (int) (srcpile
- '1');
1433 if (destpile
>= '1' && destpile
<= '4')
1434 dest
= (int) (destpile
- '1');
1436 (srcpile
== 't' || srcpile
== 's' || srcpile
== 'h' ||
1437 srcpile
== '1' || srcpile
== '2' || srcpile
== '3' ||
1441 odestpile
= destpile
;
1442 if (status
!= BETTINGBOX
)
1445 getcmd(moverow
, movecol
, "Inspect game?");
1446 } while (srcpile
!= 'y' && srcpile
!= 'n');
1447 if (srcpile
== 'n') {
1450 this.inspection
+= costofinspection
;
1451 game
.inspection
+= costofinspection
;
1452 total
.inspection
+= costofinspection
;
1454 destpile
= odestpile
;
1459 if (destpile
== 'f' || destpile
== 'F')
1460 movetofound(&talon
, source
);
1461 else if (destpile
>= '1' && destpile
<= '4')
1462 simpletableau(&talon
, dest
);
1467 if (destpile
== 'f' || destpile
== 'F')
1468 movetofound(&stock
, source
);
1469 else if (destpile
>= '1' && destpile
<= '4')
1470 simpletableau(&stock
, dest
);
1474 if (destpile
!= 't' && destpile
!= 'T') {
1482 if (status
== BETTINGBOX
) {
1484 getcmd(moverow
, movecol
,
1486 } while (srcpile
!= 'y' &&
1488 if (srcpile
== 'n') {
1495 this.wins
+= valuepercardup
* cardsoff
;
1496 game
.wins
+= valuepercardup
* cardsoff
;
1497 total
.wins
+= valuepercardup
* cardsoff
;
1498 this.game
+= costofgame
;
1499 game
.game
+= costofgame
;
1500 total
.game
+= costofgame
;
1508 printtopbettingbox();
1509 printbottombettingbox();
1510 status
= BETTINGBOX
;
1513 clearabovemovebox();
1514 clearbelowmovebox();
1518 printtopinstructions();
1519 printbottominstructions();
1520 status
= INSTRUCTIONBOX
;
1529 case '1': case '2': case '3': case '4':
1530 if (destpile
== 'f' || destpile
== 'F')
1531 movetofound(&tableau
[source
], source
);
1532 else if (destpile
>= '1' && destpile
<= '4')
1533 tabtotab(source
, dest
);
1539 fndbase(&stock
, stockcol
, stockrow
);
1540 fndbase(&talon
, taloncol
, talonrow
);
1541 updatebettinginfo();
1545 const char *const basicinstructions
[] = {
1546 "Here are brief instructions to the game of Canfield:\n\n",
1547 " If you have never played solitaire before, it is recom-\n",
1548 "mended that you consult a solitaire instruction book. In\n",
1549 "Canfield, tableau cards may be built on each other downward\n",
1550 "in alternate colors. An entire pile must be moved as a unit\n",
1551 "in building. Top cards of the piles are available to be able\n",
1552 "to be played on foundations, but never into empty spaces.\n\n",
1553 " Spaces must be filled from the stock. The top card of\n",
1554 "the stock also is available to be played on foundations or\n",
1555 "built on tableau piles. After the stock is exhausted, ta-\n",
1556 "bleau spaces may be filled from the talon and the player may\n",
1557 "keep them open until he wishes to use them.\n\n",
1558 " Cards are dealt from the hand to the talon by threes\n",
1559 "and this repeats until there are no more cards in the hand\n",
1560 "or the player quits. To have cards dealt onto the talon the\n",
1561 "player types 'ht' for his move. Foundation base cards are\n",
1562 "also automatically moved to the foundation when they become\n",
1564 "push any key when you are finished: ",
1567 const char *const bettinginstructions
[] = {
1568 " The rules for betting are somewhat less strict than\n",
1569 "those used in the official version of the game. The initial\n",
1570 "deal costs $13. You may quit at this point or inspect the\n",
1571 "game. Inspection costs $13 and allows you to make as many\n",
1572 "moves as is possible without moving any cards from your hand\n",
1573 "to the talon. (the initial deal places three cards on the\n",
1574 "talon; if all these cards are used, three more are made\n",
1575 "available) Finally, if the game seems interesting, you must\n",
1576 "pay the final installment of $26. At this point you are\n",
1577 "credited at the rate of $5 for each card on the foundation;\n",
1578 "as the game progresses you are credited with $5 for each\n",
1579 "card that is moved to the foundation. Each run through the\n",
1580 "hand after the first costs $5. The card counting feature\n",
1581 "costs $1 for each unknown card that is identified. If the\n",
1582 "information is toggled on, you are only charged for cards\n",
1583 "that became visible since it was last turned on. Thus the\n",
1584 "maximum cost of information is $34. Playing time is charged\n",
1585 "at a rate of $1 per minute.\n\n",
1586 "push any key when you are finished: ",
1590 * procedure to printout instructions
1595 const char *const *cp
;
1597 move(originrow
, origincol
);
1598 printw("This is the game of solitaire called Canfield. Do\n");
1599 printw("you want instructions for the game?");
1601 getcmd(originrow
+ 3, origincol
, "y or n?");
1602 } while (srcpile
!= 'y' && srcpile
!= 'n');
1606 for (cp
= basicinstructions
; *cp
!= NULL
; cp
++)
1611 move(originrow
, origincol
);
1612 printw("Do you want instructions for betting?");
1614 getcmd(originrow
+ 2, origincol
, "y or n?");
1615 } while (srcpile
!= 'y' && srcpile
!= 'n');
1619 for (cp
= bettinginstructions
; *cp
!= NULL
; cp
++)
1626 * procedure to initialize the game
1640 i
= lseek(dbfd
, uid
* sizeof(struct betinfo
), SEEK_SET
);
1646 i
= read(dbfd
, (char *)&total
, sizeof(total
));
1655 * procedure to end the game
1662 if (cardsoff
== 52) {
1663 getcmd(moverow
, movecol
, "Hit return to exit");
1666 move(originrow
, origincol
);
1667 printw("CONGRATULATIONS!\n");
1668 printw("You won the game. That is a feat to be proud of.\n");
1669 row
= originrow
+ 5;
1672 move(msgrow
, msgcol
);
1673 printw("You got %d card", cardsoff
);
1677 move(msgrow
, msgcol
);
1682 getcmd(row
, col
, "Play again (y or n)?");
1683 } while (srcpile
!= 'y' && srcpile
!= 'n');
1693 * procedure to clean up and exit
1696 cleanup(int sig __unused
)
1699 total
.thinktime
+= 1;
1701 updatebettinginfo();
1703 lseek(dbfd
, uid
* sizeof(struct betinfo
), SEEK_SET
);
1704 write(dbfd
, (char *)&total
, sizeof(total
));
1716 * Field an interrupt.
1719 askquit(int sig __unused
)
1721 move(msgrow
, msgcol
);
1722 printw("Really wish to quit? ");
1724 getcmd(moverow
, movecol
, "y or n?");
1725 } while (srcpile
!= 'y' && srcpile
!= 'n');
1729 signal(SIGINT
, askquit
);
1733 * Can you tell that this used to be a Pascal program?
1738 dbfd
= open(_PATH_SCORE
, O_RDWR
);
1747 if (vec
[2] >= MAXLOAD
) {
1748 puts("The system load is too high. Try again later.");
1752 signal(SIGINT
, askquit
);
1753 signal(SIGHUP
, cleanup
);
1754 signal(SIGTERM
, cleanup
);
1773 return (EXIT_FAILURE
);