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