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