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 $
32 * $DragonFly: src/games/canfield/canfield/canfield.c,v 1.4 2005/11/19 09:50:30 swildner Exp $
36 * The canfield program
39 * Originally written: Steve Levine
40 * Converted to use curses and debugged: Steve Feldman
41 * Card counting: Kirk McKusick and Mikey Olson
42 * User interface cleanups: Eric Allman and Kirk McKusick
43 * Betting by Kirk McKusick
46 #include <sys/types.h>
57 #include "pathnames.h"
88 #define handstatrow 21
90 #define talonstatrow 22
91 #define talonstatcol 7
92 #define stockstatrow 23
93 #define stockstatcol 7
113 #define INCRHAND(row, col) {\
115 if (row < ctoprow) {\
120 #define DECRHAND(row, col) {\
122 if (row > cbotrow) {\
135 struct cardtype *next;
138 #define NIL ((struct cardtype *) -1)
140 struct cardtype *deck[decksize];
141 struct cardtype cards[decksize];
142 struct cardtype *bottom[4], *found[4], *tableau[4];
143 struct cardtype *talon, *hand, *stock, *basecard;
145 int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru;
146 char suitmap[4] = {spades, clubs, hearts, diamonds};
147 char colormap[4] = {black, black, red, red};
148 char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol};
149 char srcpile, destpile;
150 int mtforigin, tempbase;
151 int coldcol, cnewcol, coldrow, cnewrow;
153 bool mtfdone, Cflag = FALSE;
154 #define INSTRUCTIONBOX 1
157 int status = INSTRUCTIONBOX;
161 * Basic betting costs
163 #define costofhand 13
164 #define costofinspection 13
165 #define costofgame 26
166 #define costofrunthroughhand 5
167 #define costofinformation 1
168 #define secondsperdollar 60
169 #define maxtimecharge 3
170 #define valuepercardup 5
172 * Variables associated with betting
175 long hand; /* cost of dealing hand */
176 long inspection; /* cost of inspecting hand */
177 long game; /* cost of buying game */
178 long runs; /* cost of running through hands */
179 long information; /* cost of information */
180 long thinktime; /* cost of thinking time */
181 long wins; /* total winnings */
182 long worth; /* net worth after costs */
184 struct betinfo this, game, total;
185 bool startedgame = FALSE, infullgame = FALSE;
189 static void askquit(int);
190 static void cleanup(int);
191 static void cleanupboard(void);
192 static void clearabovemovebox(void);
193 static void clearbelowmovebox(void);
194 static void clearmsg(void);
195 static void clearstat(void);
196 static void destinerror(void);
197 static bool diffcolor(struct cardtype *, struct cardtype *);
198 static void dumberror(void);
199 static bool finish(void);
200 static void fndbase(struct cardtype **, int, int);
201 static void getcmd(int, int, const char *);
202 static void initall(void);
203 static void initdeck(struct cardtype *[]);
204 static void initgame(void);
205 static void instruct(void);
206 static void makeboard(void);
207 static void movebox(void);
208 static void movecard(void);
209 static void movetofound(struct cardtype **, int);
210 static void movetotalon(void);
211 static bool notempty(struct cardtype *);
212 static void printbottombettingbox(void);
213 static void printbottominstructions(void);
214 static void printcard(int, int, struct cardtype *);
215 static void printrank(int, int, struct cardtype *, bool);
216 static void printtopbettingbox(void);
217 static void printtopinstructions(void);
218 static bool rankhigher(struct cardtype *, int);
219 static bool ranklower(struct cardtype *, struct cardtype *);
220 static void removecard(int, int);
221 static bool samesuit(struct cardtype *, int);
222 static void showcards(void);
223 static void showstat(void);
224 static void shuffle(struct cardtype *[]);
225 static void simpletableau(struct cardtype **, int);
226 static void startgame(void);
227 static void suspend(void);
228 static bool tabok(struct cardtype *, int);
229 static void tabprint(int, int);
230 static void tabtotab(int, int);
231 static void transit(struct cardtype **, struct cardtype **);
232 static void updatebettinginfo(void);
233 static void usedstock(void);
234 static void usedtalon(void);
237 * The following procedures print the board onto the screen using the
238 * addressible cursor. The end of these procedures will also be
239 * separated from the rest of the program.
241 * procedure to set the move command box
248 printtopbettingbox();
254 printtopinstructions();
257 move(moverow, boxcol);
259 move(msgrow, boxcol);
263 printbottombettingbox();
269 printbottominstructions();
276 * print directions above move box
279 printtopinstructions(void)
281 move(tboxrow, boxcol);
282 printw("*----------------------------------*");
283 move(tboxrow + 1, boxcol);
285 move(tboxrow + 2, boxcol);
286 printw("|s# = stock to tableau |");
287 move(tboxrow + 3, boxcol);
288 printw("|sf = stock to foundation |");
289 move(tboxrow + 4, boxcol);
290 printw("|t# = talon to tableau |");
291 move(tboxrow + 5, boxcol);
292 printw("|tf = talon to foundation |");
293 move(tboxrow + 6, boxcol);
294 printw("|## = tableau to tableau |");
295 move(tboxrow + 7, boxcol);
296 printw("|#f = tableau to foundation |");
297 move(tboxrow + 8, boxcol);
298 printw("|ht = hand to talon |");
299 move(tboxrow + 9, boxcol);
300 printw("|c = toggle card counting |");
301 move(tboxrow + 10, boxcol);
302 printw("|b = present betting information |");
303 move(tboxrow + 11, boxcol);
304 printw("|q = quit to end the game |");
305 move(tboxrow + 12, boxcol);
306 printw("|==================================|");
310 * Print the betting box.
313 printtopbettingbox(void)
316 move(tboxrow, boxcol);
317 printw("*----------------------------------*");
318 move(tboxrow + 1, boxcol);
319 printw("|Costs Hand Game Total |");
320 move(tboxrow + 2, boxcol);
322 move(tboxrow + 3, boxcol);
323 printw("| Inspections |");
324 move(tboxrow + 4, boxcol);
326 move(tboxrow + 5, boxcol);
328 move(tboxrow + 6, boxcol);
329 printw("| Information |");
330 move(tboxrow + 7, boxcol);
331 printw("| Think time |");
332 move(tboxrow + 8, boxcol);
333 printw("|Total Costs |");
334 move(tboxrow + 9, boxcol);
335 printw("|Winnings |");
336 move(tboxrow + 10, boxcol);
337 printw("|Net Worth |");
338 move(tboxrow + 11, boxcol);
340 move(tboxrow + 12, boxcol);
341 printw("|==================================|");
345 * clear info above move box
348 clearabovemovebox(void)
352 for (i = 0; i <= 11; i++) {
353 move(tboxrow + i, boxcol);
356 move(tboxrow + 12, boxcol);
357 printw("*----------------------------------*");
361 * print instructions below move box
364 printbottominstructions(void)
366 move(bboxrow, boxcol);
367 printw("|Replace # with the number of the |");
368 move(bboxrow + 1, boxcol);
369 printw("|tableau you want. |");
370 move(bboxrow + 2, boxcol);
371 printw("*----------------------------------*");
375 * print betting information below move box
378 printbottombettingbox(void)
380 move(bboxrow, boxcol);
381 printw("|x = toggle information box |");
382 move(bboxrow + 1, boxcol);
383 printw("|i = list playing instructions |");
384 move(bboxrow + 2, boxcol);
385 printw("*----------------------------------*");
389 * clear info below move box
392 clearbelowmovebox(void)
396 move(bboxrow, boxcol);
397 printw("*----------------------------------*");
398 for (i = 1; i <= 2; i++) {
399 move(bboxrow + i, boxcol);
405 * procedure to put the board on the screen using addressable cursor
412 move(titlerow, titlecol);
413 printw("=-> CANFIELD <-=");
414 move(fttlrow, fttlcol);
415 printw("foundation");
416 move(foundrow - 1, fttlcol);
417 printw("=---= =---= =---= =---=");
418 move(foundrow, fttlcol);
419 printw("| | | | | | | |");
420 move(foundrow + 1, fttlcol);
421 printw("=---= =---= =---= =---=");
422 move(ottlrow, sidecol);
423 printw("stock tableau");
424 move(stockrow - 1, sidecol);
426 move(stockrow, sidecol);
428 move(stockrow + 1, sidecol);
430 move(talonrow - 2, sidecol);
432 move(talonrow - 1, sidecol);
434 move(talonrow, sidecol);
436 move(talonrow + 1, sidecol);
438 move(tabrow - 1, atabcol);
439 printw("-1- -2- -3- -4-");
444 * clean up the board for another game
450 struct cardtype *ptr;
455 for(ptr = stock, row = stockrow;
457 ptr = ptr->next, row++) {
463 move(stockrow + 1, sidecol);
465 move(talonrow - 2, sidecol);
467 move(talonrow - 1, sidecol);
469 move(talonrow + 1, sidecol);
472 move(stockrow, sidecol);
474 move(talonrow, sidecol);
476 move(foundrow, fttlcol);
477 printw("| | | | | | | |");
478 for (cnt = 0; cnt < 4; cnt++) {
493 for(ptr = tableau[cnt], row = tabrow;
495 ptr = ptr->next, row++)
496 removecard(col, row);
501 * procedure to create a deck of cards
504 initdeck(struct cardtype *ideck[])
512 for (scnt=0; scnt<4; scnt++) {
514 for (r=Ace; r<=King; r++) {
515 ideck[i] = &cards[i];
518 cards[i].color = colormap[scnt];
526 * procedure to shuffle the deck
529 shuffle(struct cardtype *ideck[])
532 struct cardtype *temp;
534 for (i=0; i<decksize; i++) {
535 ideck[i]->visible = FALSE;
536 ideck[i]->paid = FALSE;
538 for (i = decksize-1; i>=0; i--) {
539 j = random() % decksize;
549 * procedure to remove the card from the board
552 removecard(int a, int b)
559 * procedure to print the cards on the board
562 printrank(int a, int b, struct cardtype *cp, bool inverse)
570 case 2: case 3: case 4: case 5: case 6: case 7:
571 case 8: case 9: case 10:
572 printw("%d", cp->rank);
591 * procedure to print out a card
594 printcard(int a, int b, struct cardtype *cp)
598 else if (cp->visible == FALSE) {
602 bool inverse = (cp->suit == 'd' || cp->suit == 'h');
604 printrank(a, b, cp, inverse);
614 * procedure to move the top card from one location to the top
615 * of another location. The pointers always point to the top
619 transit(struct cardtype **source, struct cardtype **dest)
621 struct cardtype *temp;
624 *source = (*source)->next;
630 * Procedure to set the cards on the foundation base when available.
631 * Note that it is only called on a foundation pile at the beginning of
632 * the game, so the pile will have exactly one card in it.
635 fndbase(struct cardtype **cp, int column, int row)
641 if ((*cp)->rank == basecard->rank) {
643 printcard(pilemap[base], foundrow, *cp);
644 if (*cp == tableau[0])
645 length[0] = length[0] - 1;
646 if (*cp == tableau[1])
647 length[1] = length[1] - 1;
648 if (*cp == tableau[2])
649 length[2] = length[2] - 1;
650 if (*cp == tableau[3])
651 length[3] = length[3] - 1;
652 transit(cp, &found[base]);
658 printcard(column, row, *cp);
661 removecard(column, row);
666 this.wins += valuepercardup;
667 game.wins += valuepercardup;
668 total.wins += valuepercardup;
672 } while (nomore == FALSE);
676 * procedure to initialize the things necessary for the game
683 for (i=0; i<18; i++) {
684 deck[i]->visible = TRUE;
685 deck[i]->paid = TRUE;
689 for (i=12; i>=1; i--)
690 deck[i]->next = deck[i - 1];
693 deck[13]->next = NIL;
697 for (i=14; i<18; i++) {
698 tableau[i - 14] = deck[i];
701 for (i=0; i<4; i++) {
702 bottom[i] = tableau[i];
706 for (i=18; i<decksize-1; i++)
707 deck[i]->next = deck[i + 1];
708 deck[decksize-1]->next = NIL;
718 cnewcol = cinitcol + cwidthcol;
722 * procedure to print the beginning cards and to start each game
731 this.hand = costofhand;
732 game.hand += costofhand;
733 total.hand += costofhand;
737 this.information = 0;
742 printcard(foundcol, foundrow, found[0]);
743 printcard(stockcol, stockrow, stock);
744 printcard(atabcol, tabrow, tableau[0]);
745 printcard(btabcol, tabrow, tableau[1]);
746 printcard(ctabcol, tabrow, tableau[2]);
747 printcard(dtabcol, tabrow, tableau[3]);
748 printcard(taloncol, talonrow, talon);
749 move(foundrow - 2, basecol);
751 move(foundrow - 1, basecol);
753 printrank(basecol, foundrow, found[0], 0);
755 fndbase(&tableau[j], pilemap[j], tabrow);
756 fndbase(&stock, stockcol, stockrow);
757 showstat(); /* show card counting info to cheaters */
763 * procedure to clear the message printed from an error
770 if (errmsg == TRUE) {
772 move(msgrow, msgcol);
780 * procedure to print an error message if the move is not listed
786 move(msgrow, msgcol);
787 printw("Not a proper move ");
791 * procedure to print an error message if the move is not possible
797 move(msgrow, msgcol);
798 printw("Error: Can't move there");
802 * function to see if the source has cards in it
805 notempty(struct cardtype *cp)
809 move(msgrow, msgcol);
810 printw("Error: no cards to move");
817 * function to see if the rank of one card is less than another
820 ranklower(struct cardtype *cp1, struct cardtype *cp2)
822 if (cp2->rank == Ace)
823 if (cp1->rank == King)
827 else if (cp1->rank + 1 == cp2->rank)
834 * function to check the cardcolor for moving to a tableau
837 diffcolor(struct cardtype *cp1, struct cardtype *cp2)
839 if (cp1->color == cp2->color)
846 * function to see if the card can move to the tableau
849 tabok(struct cardtype *cp, int des)
851 if ((cp == stock) && (tableau[des] == NIL))
853 else if (tableau[des] == NIL)
855 cp != bottom[0] && cp != bottom[1] &&
856 cp != bottom[2] && cp != bottom[3])
860 else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des]))
867 * procedure to turn the cards onto the talon from the deck
874 if (cinhand <= 3 && cinhand > 0) {
875 move(msgrow, msgcol);
876 printw("Hand is now empty ");
880 else if (cinhand > 0)
882 else if (talon != NIL) {
885 move(msgrow, msgcol);
886 if (timesthru != 4) {
887 printw("Talon is now the new hand");
888 this.runs += costofrunthroughhand;
889 game.runs += costofrunthroughhand;
890 total.runs += costofrunthroughhand;
891 while (talon != NIL) {
892 transit(&talon, &hand);
903 cnewcol = cinitcol + cwidthcol;
909 printw("I believe you have lost");
915 move(msgrow, msgcol);
916 printw("Talon and hand are empty");
919 for (i=0; i<fin; i++) {
920 transit(&hand, &talon);
921 INCRHAND(cnewrow, cnewcol);
922 INCRHAND(coldrow, coldcol);
923 removecard(cnewcol, cnewrow);
925 talon->visible = TRUE;
927 if (talon->paid == FALSE && talon->visible == TRUE) {
928 this.information += costofinformation;
929 game.information += costofinformation;
930 total.information += costofinformation;
933 printcard(coldcol, coldrow, talon);
937 printcard(taloncol, talonrow, talon);
941 move(handstatrow, handstatcol);
942 printw("%3d", cinhand);
943 move(talonstatrow, talonstatcol);
944 printw("%3d", taloncnt);
946 fndbase(&talon, taloncol, talonrow);
952 * procedure to print card counting info on screen
958 struct cardtype *ptr;
962 move(talonstatrow, talonstatcol - 7);
963 printw("Talon: %3d", taloncnt);
964 move(handstatrow, handstatcol - 7);
965 printw("Hand: %3d", cinhand);
966 move(stockstatrow, stockstatcol - 7);
967 printw("Stock: %3d", stockcnt);
968 for ( row = coldrow, col = coldcol, ptr = talon;
971 if (ptr->paid == FALSE && ptr->visible == TRUE) {
973 this.information += costofinformation;
974 game.information += costofinformation;
975 total.information += costofinformation;
977 printcard(col, row, ptr);
980 for ( row = cnewrow, col = cnewcol, ptr = hand;
983 if (ptr->paid == FALSE && ptr->visible == TRUE) {
985 this.information += costofinformation;
986 game.information += costofinformation;
987 total.information += costofinformation;
990 printcard(col, row, ptr);
995 * procedure to clear card counting info from screen
1002 move(talonstatrow, talonstatcol - 7);
1004 move(handstatrow, handstatcol - 7);
1006 move(stockstatrow, stockstatcol - 7);
1008 for ( row = ctoprow ; row <= cbotrow ; row++ ) {
1009 move(row, cinitcol);
1010 printw("%56s", " ");
1015 * procedure to update card counting base
1020 removecard(coldcol, coldrow);
1021 DECRHAND(coldrow, coldcol);
1022 if (talon != NIL && (talon->visible == FALSE)) {
1023 talon->visible = TRUE;
1025 this.information += costofinformation;
1026 game.information += costofinformation;
1027 total.information += costofinformation;
1029 printcard(coldcol, coldrow, talon);
1034 move(talonstatrow, talonstatcol);
1035 printw("%3d", taloncnt);
1040 * procedure to update stock card counting base
1047 move(stockstatrow, stockstatcol);
1048 printw("%3d", stockcnt);
1053 * let 'em know how they lost!
1058 struct cardtype *ptr;
1061 if (!Cflag || cardsoff == 52)
1063 for (ptr = talon; ptr != NIL; ptr = ptr->next) {
1064 ptr->visible = TRUE;
1067 for (ptr = hand; ptr != NIL; ptr = ptr->next) {
1068 ptr->visible = TRUE;
1072 move(stockrow + 1, sidecol);
1074 move(talonrow - 2, sidecol);
1076 move(talonrow - 1, sidecol);
1078 move(talonrow, sidecol);
1080 move(talonrow + 1, sidecol);
1082 for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) {
1083 move(row, stockcol - 1);
1085 printcard(stockcol, row, ptr);
1088 move(row, stockcol - 1);
1092 move(handstatrow, handstatcol - 7);
1094 move(row, stockcol - 1);
1096 if ( cardsoff == 52 )
1097 getcmd(moverow, movecol, "Hit return to exit");
1101 * procedure to update the betting values
1104 updatebettinginfo(void)
1106 long thiscosts, gamecosts, totalcosts;
1107 double thisreturn, gamereturn, totalreturn;
1112 dollars = (now - acctstart) / secondsperdollar;
1114 acctstart += dollars * secondsperdollar;
1115 if (dollars > maxtimecharge)
1116 dollars = maxtimecharge;
1117 this.thinktime += dollars;
1118 game.thinktime += dollars;
1119 total.thinktime += dollars;
1121 thiscosts = this.hand + this.inspection + this.game +
1122 this.runs + this.information + this.thinktime;
1123 gamecosts = game.hand + game.inspection + game.game +
1124 game.runs + game.information + game.thinktime;
1125 totalcosts = total.hand + total.inspection + total.game +
1126 total.runs + total.information + total.thinktime;
1127 this.worth = this.wins - thiscosts;
1128 game.worth = game.wins - gamecosts;
1129 total.worth = total.wins - totalcosts;
1130 thisreturn = ((double)this.wins / (double)thiscosts - 1.0) * 100.0;
1131 gamereturn = ((double)game.wins / (double)gamecosts - 1.0) * 100.0;
1132 totalreturn = ((double)total.wins / (double)totalcosts - 1.0) * 100.0;
1133 if (status != BETTINGBOX)
1135 move(tboxrow + 2, boxcol + 13);
1136 printw("%4d%8d%9d", this.hand, game.hand, total.hand);
1137 move(tboxrow + 3, boxcol + 13);
1138 printw("%4d%8d%9d", this.inspection, game.inspection, total.inspection);
1139 move(tboxrow + 4, boxcol + 13);
1140 printw("%4d%8d%9d", this.game, game.game, total.game);
1141 move(tboxrow + 5, boxcol + 13);
1142 printw("%4d%8d%9d", this.runs, game.runs, total.runs);
1143 move(tboxrow + 6, boxcol + 13);
1144 printw("%4d%8d%9d", this.information, game.information,
1146 move(tboxrow + 7, boxcol + 13);
1147 printw("%4d%8d%9d", this.thinktime, game.thinktime, total.thinktime);
1148 move(tboxrow + 8, boxcol + 13);
1149 printw("%4d%8d%9d", thiscosts, gamecosts, totalcosts);
1150 move(tboxrow + 9, boxcol + 13);
1151 printw("%4d%8d%9d", this.wins, game.wins, total.wins);
1152 move(tboxrow + 10, boxcol + 13);
1153 printw("%4d%8d%9d", this.worth, game.worth, total.worth);
1154 move(tboxrow + 11, boxcol + 13);
1155 printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn, gamereturn, totalreturn);
1159 * procedure to move a card from the stock or talon to the tableau
1162 simpletableau(struct cardtype **cp, int des)
1166 if (notempty(*cp)) {
1167 if (tabok(*cp, des)) {
1172 if (tableau[des] == NIL)
1174 transit(cp, &tableau[des]);
1176 printcard(pilemap[des], length[des], tableau[des]);
1178 if (origin == stk) {
1180 printcard(stockcol, stockrow, stock);
1183 printcard(taloncol, talonrow, talon);
1194 tabprint(int sour, int des)
1196 int dlength, slength, i;
1197 struct cardtype *tempcard;
1199 for (i=tabrow; i<=length[sour]; i++)
1200 removecard(pilemap[sour], i);
1201 dlength = length[des] + 1;
1202 slength = length[sour];
1203 if (slength == tabrow)
1204 printcard(pilemap[des], dlength, tableau[sour]);
1206 while (slength != tabrow - 1) {
1207 tempcard = tableau[sour];
1208 for (i=1; i<=slength-tabrow; i++)
1209 tempcard = tempcard->next;
1210 printcard(pilemap[des], dlength, tempcard);
1217 * procedure to move from the tableau to the tableau
1220 tabtotab(int sour, int des)
1222 struct cardtype *temp;
1224 if (notempty(tableau[sour])) {
1225 if (tabok(bottom[sour], des)) {
1226 tabprint(sour, des);
1227 temp = bottom[sour];
1229 if (bottom[des] == NIL)
1231 temp->next = tableau[des];
1232 tableau[des] = tableau[sour];
1233 tableau[sour] = NIL;
1234 length[des] = length[des] +
1235 (length[sour] - (tabrow - 1));
1236 length[sour] = tabrow - 1;
1244 * functions to see if the card can go onto the foundation
1247 rankhigher(struct cardtype *cp, int let)
1249 if (found[let]->rank == King)
1250 if (cp->rank == Ace)
1254 else if (cp->rank - 1 == found[let]->rank)
1261 * function to determine if two cards are the same suit
1264 samesuit(struct cardtype *cp, int let)
1266 if (cp->suit == found[let]->suit)
1273 * procedure to move a card to the correct foundation pile
1276 movetofound(struct cardtype **cp, int source)
1280 if (notempty(*cp)) {
1282 if (found[tempbase] != NIL)
1283 if (rankhigher(*cp, tempbase)
1284 && samesuit(*cp, tempbase)) {
1287 else if (*cp == talon)
1291 transit(cp, &found[tempbase]);
1292 printcard(pilemap[tempbase],
1293 foundrow, found[tempbase]);
1295 if (mtforigin == stk) {
1297 printcard(stockcol, stockrow,
1299 } else if (mtforigin == tal) {
1301 printcard(taloncol, talonrow,
1304 removecard(pilemap[source],
1310 this.wins += valuepercardup;
1311 game.wins += valuepercardup;
1312 total.wins += valuepercardup;
1319 } while ((tempbase != 4) && !mtfdone);
1326 * procedure to get a command
1329 getcmd(int row, int col, const char *cp)
1336 printw("%-24s", cp);
1337 col += 1 + strlen(cp);
1341 ch = getch() & 0177;
1342 if (ch >= 'A' && ch <= 'Z')
1347 } else if (i >= 2 && ch != erasechar() && ch != killchar()) {
1348 if (ch != '\n' && ch != '\r' && ch != ' ')
1349 write(1, "\007", 1);
1350 } else if (ch == erasechar() && i > 0) {
1354 } else if (ch == killchar() && i > 0) {
1360 } else if (ch == '\032') { /* Control-Z */
1364 } else if (isprint(ch)) {
1369 } while (ch != '\n' && ch != '\r' && ch != ' ');
1375 * Suspend the game (shell escape if no process control on system)
1384 updatebettinginfo();
1388 lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET);
1389 write(dbfd, (char *)&total, sizeof(total));
1391 kill(getpid(), SIGTSTP);
1397 * procedure to evaluate and make the specific moves
1403 char osrcpile, odestpile;
1410 if (talon == NIL && hand != NIL)
1412 if (cardsoff == 52) {
1415 } else if (!startedgame) {
1416 move(msgrow, msgcol);
1418 switch (34 - taloncnt - cinhand) {
1423 printw("One card used from talon ");
1426 printw("Two cards used from talon ");
1429 printw(">3< cards used from talon ");
1432 getcmd(moverow, movecol, "Move:");
1434 getcmd(moverow, movecol, "Move:");
1436 if (srcpile >= '1' && srcpile <= '4')
1437 source = (int) (srcpile - '1');
1438 if (destpile >= '1' && destpile <= '4')
1439 dest = (int) (destpile - '1');
1441 (srcpile == 't' || srcpile == 's' || srcpile == 'h' ||
1442 srcpile == '1' || srcpile == '2' || srcpile == '3' ||
1446 odestpile = destpile;
1447 if (status != BETTINGBOX)
1450 getcmd(moverow, movecol, "Inspect game?");
1451 } while (srcpile != 'y' && srcpile != 'n');
1452 if (srcpile == 'n') {
1455 this.inspection += costofinspection;
1456 game.inspection += costofinspection;
1457 total.inspection += costofinspection;
1459 destpile = odestpile;
1464 if (destpile == 'f' || destpile == 'F')
1465 movetofound(&talon, source);
1466 else if (destpile >= '1' && destpile <= '4')
1467 simpletableau(&talon, dest);
1472 if (destpile == 'f' || destpile == 'F')
1473 movetofound(&stock, source);
1474 else if (destpile >= '1' && destpile <= '4')
1475 simpletableau(&stock, dest);
1479 if (destpile != 't' && destpile != 'T') {
1487 if (status == BETTINGBOX) {
1489 getcmd(moverow, movecol,
1491 } while (srcpile != 'y' &&
1493 if (srcpile == 'n') {
1500 this.wins += valuepercardup * cardsoff;
1501 game.wins += valuepercardup * cardsoff;
1502 total.wins += valuepercardup * cardsoff;
1503 this.game += costofgame;
1504 game.game += costofgame;
1505 total.game += costofgame;
1513 printtopbettingbox();
1514 printbottombettingbox();
1515 status = BETTINGBOX;
1518 clearabovemovebox();
1519 clearbelowmovebox();
1523 printtopinstructions();
1524 printbottominstructions();
1525 status = INSTRUCTIONBOX;
1534 case '1': case '2': case '3': case '4':
1535 if (destpile == 'f' || destpile == 'F')
1536 movetofound(&tableau[source], source);
1537 else if (destpile >= '1' && destpile <= '4')
1538 tabtotab(source, dest);
1544 fndbase(&stock, stockcol, stockrow);
1545 fndbase(&talon, taloncol, talonrow);
1546 updatebettinginfo();
1550 const char *const basicinstructions[] = {
1551 "Here are brief instuctions to the game of Canfield:\n\n",
1552 " If you have never played solitaire before, it is recom-\n",
1553 "mended that you consult a solitaire instruction book. In\n",
1554 "Canfield, tableau cards may be built on each other downward\n",
1555 "in alternate colors. An entire pile must be moved as a unit\n",
1556 "in building. Top cards of the piles are available to be able\n",
1557 "to be played on foundations, but never into empty spaces.\n\n",
1558 " Spaces must be filled from the stock. The top card of\n",
1559 "the stock also is available to be played on foundations or\n",
1560 "built on tableau piles. After the stock is exhausted, ta-\n",
1561 "bleau spaces may be filled from the talon and the player may\n",
1562 "keep them open until he wishes to use them.\n\n",
1563 " Cards are dealt from the hand to the talon by threes\n",
1564 "and this repeats until there are no more cards in the hand\n",
1565 "or the player quits. To have cards dealt onto the talon the\n",
1566 "player types 'ht' for his move. Foundation base cards are\n",
1567 "also automatically moved to the foundation when they become\n",
1569 "push any key when you are finished: ",
1572 const char *const bettinginstructions[] = {
1573 " The rules for betting are somewhat less strict than\n",
1574 "those used in the official version of the game. The initial\n",
1575 "deal costs $13. You may quit at this point or inspect the\n",
1576 "game. Inspection costs $13 and allows you to make as many\n",
1577 "moves as is possible without moving any cards from your hand\n",
1578 "to the talon. (the initial deal places three cards on the\n",
1579 "talon; if all these cards are used, three more are made\n",
1580 "available) Finally, if the game seems interesting, you must\n",
1581 "pay the final installment of $26. At this point you are\n",
1582 "credited at the rate of $5 for each card on the foundation;\n",
1583 "as the game progresses you are credited with $5 for each\n",
1584 "card that is moved to the foundation. Each run through the\n",
1585 "hand after the first costs $5. The card counting feature\n",
1586 "costs $1 for each unknown card that is identified. If the\n",
1587 "information is toggled on, you are only charged for cards\n",
1588 "that became visible since it was last turned on. Thus the\n",
1589 "maximum cost of information is $34. Playing time is charged\n",
1590 "at a rate of $1 per minute.\n\n",
1591 "push any key when you are finished: ",
1595 * procedure to printout instructions
1600 const char *const *cp;
1602 move(originrow, origincol);
1603 printw("This is the game of solitaire called Canfield. Do\n");
1604 printw("you want instructions for the game?");
1606 getcmd(originrow + 3, origincol, "y or n?");
1607 } while (srcpile != 'y' && srcpile != 'n');
1611 for (cp = basicinstructions; *cp != 0; cp++)
1616 move(originrow, origincol);
1617 printw("Do you want instructions for betting?");
1619 getcmd(originrow + 2, origincol, "y or n?");
1620 } while (srcpile != 'y' && srcpile != 'n');
1624 for (cp = bettinginstructions; *cp != 0; cp++)
1631 * procedure to initialize the game
1645 i = lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET);
1651 i = read(dbfd, (char *)&total, sizeof(total));
1660 * procedure to end the game
1667 if (cardsoff == 52) {
1668 getcmd(moverow, movecol, "Hit return to exit");
1671 move(originrow, origincol);
1672 printw("CONGRATULATIONS!\n");
1673 printw("You won the game. That is a feat to be proud of.\n");
1674 row = originrow + 5;
1677 move(msgrow, msgcol);
1678 printw("You got %d card", cardsoff);
1682 move(msgrow, msgcol);
1687 getcmd(row, col, "Play again (y or n)?");
1688 } while (srcpile != 'y' && srcpile != 'n');
1698 * procedure to clean up and exit
1701 cleanup(int sig __unused)
1704 total.thinktime += 1;
1706 updatebettinginfo();
1708 lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET);
1709 write(dbfd, (char *)&total, sizeof(total));
1721 * Field an interrupt.
1724 askquit(int sig __unused)
1726 move(msgrow, msgcol);
1727 printw("Really wish to quit? ");
1729 getcmd(moverow, movecol, "y or n?");
1730 } while (srcpile != 'y' && srcpile != 'n');
1734 signal(SIGINT, askquit);
1738 * Can you tell that this used to be a Pascal program?
1743 dbfd = open(_PATH_SCORE, O_RDWR);
1752 if (vec[2] >= MAXLOAD) {
1753 puts("The system load is too high. Try again later.");
1757 signal(SIGINT, askquit);
1758 signal(SIGHUP, cleanup);
1759 signal(SIGTERM, cleanup);
1778 return (EXIT_FAILURE);