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