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