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