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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)canfield.c	8.1 (Berkeley) 5/31/93
35  * $FreeBSD: src/games/canfield/canfield/canfield.c,v 1.11 1999/12/12 07:25:13 billf Exp $
36  * $DragonFly: src/games/canfield/canfield/canfield.c,v 1.4 2005/11/19 09:50:30 swildner Exp $
37  */
38 
39 /*
40  * The canfield program
41  *
42  * Authors:
43  *	Originally written: Steve Levine
44  *	Converted to use curses and debugged: Steve Feldman
45  *	Card counting: Kirk McKusick and Mikey Olson
46  *	User interface cleanups: Eric Allman and Kirk McKusick
47  *	Betting by Kirk McKusick
48  */
49 
50 #include <sys/types.h>
51 
52 #include <curses.h>
53 #include <ctype.h>
54 #include <signal.h>
55 #include <unistd.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <fcntl.h>
59 #include <time.h>
60 
61 #include "pathnames.h"
62 
63 #define	decksize	52
64 #define originrow	0
65 #define origincol	0
66 #define	basecol		1
67 #define	boxcol		42
68 #define	tboxrow		2
69 #define	bboxrow		17
70 #define	movecol		43
71 #define	moverow		16
72 #define	msgcol		43
73 #define	msgrow		15
74 #define	titlecol	30
75 #define	titlerow	0
76 #define	sidecol		1
77 #define	ottlrow		6
78 #define	foundcol	11
79 #define	foundrow	3
80 #define	stockcol	2
81 #define	stockrow	8
82 #define	fttlcol		10
83 #define	fttlrow		1
84 #define	taloncol	2
85 #define	talonrow	13
86 #define	tabrow		8
87 #define ctoprow		21
88 #define cbotrow		23
89 #define cinitcol	14
90 #define cheightcol	1
91 #define cwidthcol	4
92 #define handstatrow	21
93 #define handstatcol	7
94 #define talonstatrow	22
95 #define talonstatcol	7
96 #define stockstatrow	23
97 #define stockstatcol	7
98 #define	Ace		1
99 #define	Jack		11
100 #define	Queen		12
101 #define	King		13
102 #define	atabcol		11
103 #define	btabcol		18
104 #define	ctabcol		25
105 #define	dtabcol		32
106 
107 #define	spades		's'
108 #define	clubs		'c'
109 #define	hearts		'h'
110 #define	diamonds	'd'
111 #define	black		'b'
112 #define	red		'r'
113 
114 #define stk		1
115 #define	tal		2
116 #define tab		3
117 #define INCRHAND(row, col) {\
118 	row -= cheightcol;\
119 	if (row < ctoprow) {\
120 		row = cbotrow;\
121 		col += cwidthcol;\
122 	}\
123 }
124 #define DECRHAND(row, col) {\
125 	row += cheightcol;\
126 	if (row > cbotrow) {\
127 		row = ctoprow;\
128 		col -= cwidthcol;\
129 	}\
130 }
131 
132 
133 struct cardtype {
134 	char suit;
135 	char color;
136 	bool visible;
137 	bool paid;
138 	int rank;
139 	struct cardtype *next;
140 };
141 
142 #define	NIL	((struct cardtype *) -1)
143 
144 struct cardtype *deck[decksize];
145 struct cardtype cards[decksize];
146 struct cardtype *bottom[4], *found[4], *tableau[4];
147 struct cardtype *talon, *hand, *stock, *basecard;
148 int length[4];
149 int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru;
150 char suitmap[4] = {spades, clubs, hearts, diamonds};
151 char colormap[4] = {black, black, red, red};
152 char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol};
153 char srcpile, destpile;
154 int mtforigin, tempbase;
155 int coldcol, cnewcol, coldrow, cnewrow;
156 bool errmsg, done;
157 bool mtfdone, Cflag = FALSE;
158 #define INSTRUCTIONBOX	1
159 #define BETTINGBOX	2
160 #define NOBOX		3
161 int status = INSTRUCTIONBOX;
162 int uid;
163 
164 /*
165  * Basic betting costs
166  */
167 #define costofhand		13
168 #define costofinspection	13
169 #define costofgame		26
170 #define costofrunthroughhand	 5
171 #define costofinformation	 1
172 #define secondsperdollar	60
173 #define maxtimecharge		 3
174 #define valuepercardup	 	 5
175 /*
176  * Variables associated with betting
177  */
178 struct betinfo {
179 	long	hand;		/* cost of dealing hand */
180 	long	inspection;	/* cost of inspecting hand */
181 	long	game;		/* cost of buying game */
182 	long	runs;		/* cost of running through hands */
183 	long	information;	/* cost of information */
184 	long	thinktime;	/* cost of thinking time */
185 	long	wins;		/* total winnings */
186 	long	worth;		/* net worth after costs */
187 };
188 struct betinfo this, game, total;
189 bool startedgame = FALSE, infullgame = FALSE;
190 time_t acctstart;
191 int dbfd = -1;
192 
193 static void	askquit(int);
194 static void	cleanup(int);
195 static void	cleanupboard(void);
196 static void	clearabovemovebox(void);
197 static void	clearbelowmovebox(void);
198 static void	clearmsg(void);
199 static void	clearstat(void);
200 static void	destinerror(void);
201 static bool	diffcolor(struct cardtype *, struct cardtype *);
202 static void	dumberror(void);
203 static bool	finish(void);
204 static void	fndbase(struct cardtype **, int, int);
205 static void	getcmd(int, int, const char *);
206 static void	initall(void);
207 static void	initdeck(struct cardtype **);
208 static void	initgame(void);
209 static void	instruct(void);
210 static void	makeboard(void);
211 static void	movebox(void);
212 static void	movecard(void);
213 static void	movetofound(struct cardtype **, int);
214 static void	movetotalon(void);
215 static bool	notempty(struct cardtype *);
216 static void	printbottombettingbox(void);
217 static void	printbottominstructions(void);
218 static void	printcard(int, int, struct cardtype *);
219 static void	printrank(int, int, struct cardtype *, bool);
220 static void	printtopbettingbox(void);
221 static void	printtopinstructions(void);
222 static bool	rankhigher(struct cardtype *, int);
223 static bool	ranklower(struct cardtype *, struct cardtype *);
224 static void	removecard(int, int);
225 static bool	samesuit(struct cardtype *, int);
226 static void	showcards(void);
227 static void	showstat(void);
228 static void	shuffle(struct cardtype **);
229 static void	simpletableau(struct cardtype **, int);
230 static void	startgame(void);
231 static void	suspend(void);
232 static bool	tabok(struct cardtype *, int);
233 static void	tabprint(int, int);
234 static void	tabtotab(int, int);
235 static void	transit(struct cardtype **, struct cardtype **);
236 static void	updatebettinginfo(void);
237 static void	usedstock(void);
238 static void	usedtalon(void);
239 
240 /*
241  * The following procedures print the board onto the screen using the
242  * addressible cursor. The end of these procedures will also be
243  * separated from the rest of the program.
244  *
245  * procedure to set the move command box
246  */
247 static void
248 movebox(void)
249 {
250 	switch (status) {
251 	case BETTINGBOX:
252 		printtopbettingbox();
253 		break;
254 	case NOBOX:
255 		clearabovemovebox();
256 		break;
257 	case INSTRUCTIONBOX:
258 		printtopinstructions();
259 		break;
260 	}
261 	move(moverow, boxcol);
262 	printw("|                                  |");
263 	move(msgrow, boxcol);
264 	printw("|                                  |");
265 	switch (status) {
266 	case BETTINGBOX:
267 		printbottombettingbox();
268 		break;
269 	case NOBOX:
270 		clearbelowmovebox();
271 		break;
272 	case INSTRUCTIONBOX:
273 		printbottominstructions();
274 		break;
275 	}
276 	refresh();
277 }
278 
279 /*
280  * print directions above move box
281  */
282 static void
283 printtopinstructions(void)
284 {
285 	    move(tboxrow, boxcol);
286 	    printw("*----------------------------------*");
287 	    move(tboxrow + 1, boxcol);
288 	    printw("|         MOVES                    |");
289 	    move(tboxrow + 2, boxcol);
290 	    printw("|s# = stock to tableau             |");
291 	    move(tboxrow + 3, boxcol);
292 	    printw("|sf = stock to foundation          |");
293 	    move(tboxrow + 4, boxcol);
294 	    printw("|t# = talon to tableau             |");
295 	    move(tboxrow + 5, boxcol);
296 	    printw("|tf = talon to foundation          |");
297 	    move(tboxrow + 6, boxcol);
298 	    printw("|## = tableau to tableau           |");
299 	    move(tboxrow + 7, boxcol);
300 	    printw("|#f = tableau to foundation        |");
301 	    move(tboxrow + 8, boxcol);
302 	    printw("|ht = hand to talon                |");
303 	    move(tboxrow + 9, boxcol);
304 	    printw("|c = toggle card counting          |");
305 	    move(tboxrow + 10, boxcol);
306 	    printw("|b = present betting information   |");
307 	    move(tboxrow + 11, boxcol);
308 	    printw("|q = quit to end the game          |");
309 	    move(tboxrow + 12, boxcol);
310 	    printw("|==================================|");
311 }
312 
313 /*
314  * Print the betting box.
315  */
316 static void
317 printtopbettingbox(void)
318 {
319 
320 	    move(tboxrow, boxcol);
321 	    printw("*----------------------------------*");
322 	    move(tboxrow + 1, boxcol);
323 	    printw("|Costs        Hand   Game    Total |");
324 	    move(tboxrow + 2, boxcol);
325 	    printw("| Hands                            |");
326 	    move(tboxrow + 3, boxcol);
327 	    printw("| Inspections                      |");
328 	    move(tboxrow + 4, boxcol);
329 	    printw("| Games                            |");
330 	    move(tboxrow + 5, boxcol);
331 	    printw("| Runs                             |");
332 	    move(tboxrow + 6, boxcol);
333 	    printw("| Information                      |");
334 	    move(tboxrow + 7, boxcol);
335 	    printw("| Think time                       |");
336 	    move(tboxrow + 8, boxcol);
337 	    printw("|Total Costs                       |");
338 	    move(tboxrow + 9, boxcol);
339 	    printw("|Winnings                          |");
340 	    move(tboxrow + 10, boxcol);
341 	    printw("|Net Worth                         |");
342 	    move(tboxrow + 11, boxcol);
343 	    printw("|Return                            |");
344 	    move(tboxrow + 12, boxcol);
345 	    printw("|==================================|");
346 }
347 
348 /*
349  * clear info above move box
350  */
351 static void
352 clearabovemovebox(void)
353 {
354 	int i;
355 
356 	for (i = 0; i <= 11; i++) {
357 		move(tboxrow + i, boxcol);
358 		printw("                                    ");
359 	}
360 	move(tboxrow + 12, boxcol);
361 	printw("*----------------------------------*");
362 }
363 
364 /*
365  * print instructions below move box
366  */
367 static void
368 printbottominstructions(void)
369 {
370 	    move(bboxrow, boxcol);
371 	    printw("|Replace # with the number of the  |");
372 	    move(bboxrow + 1, boxcol);
373 	    printw("|tableau you want.                 |");
374 	    move(bboxrow + 2, boxcol);
375 	    printw("*----------------------------------*");
376 }
377 
378 /*
379  * print betting information below move box
380  */
381 static void
382 printbottombettingbox(void)
383 {
384 	    move(bboxrow, boxcol);
385 	    printw("|x = toggle information box        |");
386 	    move(bboxrow + 1, boxcol);
387 	    printw("|i = list playing instructions     |");
388 	    move(bboxrow + 2, boxcol);
389 	    printw("*----------------------------------*");
390 }
391 
392 /*
393  * clear info below move box
394  */
395 static void
396 clearbelowmovebox(void)
397 {
398 	int i;
399 
400 	move(bboxrow, boxcol);
401 	printw("*----------------------------------*");
402 	for (i = 1; i <= 2; i++) {
403 		move(bboxrow + i, boxcol);
404 		printw("                                    ");
405 	}
406 }
407 
408 /*
409  * procedure to put the board on the screen using addressable cursor
410  */
411 static void
412 makeboard(void)
413 {
414 	clear();
415 	refresh();
416 	move(titlerow, titlecol);
417 	printw("=-> CANFIELD <-=");
418 	move(fttlrow, fttlcol);
419 	printw("foundation");
420 	move(foundrow - 1, fttlcol);
421 	printw("=---=  =---=  =---=  =---=");
422 	move(foundrow, fttlcol);
423 	printw("|   |  |   |  |   |  |   |");
424 	move(foundrow + 1, fttlcol);
425 	printw("=---=  =---=  =---=  =---=");
426 	move(ottlrow, sidecol);
427 	printw("stock     tableau");
428 	move(stockrow - 1, sidecol);
429 	printw("=---=");
430 	move(stockrow, sidecol);
431 	printw("|   |");
432 	move(stockrow + 1, sidecol);
433 	printw("=---=");
434 	move(talonrow - 2, sidecol);
435 	printw("talon");
436 	move(talonrow - 1, sidecol);
437 	printw("=---=");
438 	move(talonrow, sidecol);
439 	printw("|   |");
440 	move(talonrow + 1, sidecol);
441 	printw("=---=");
442 	move(tabrow - 1, atabcol);
443 	printw("-1-    -2-    -3-    -4-");
444 	movebox();
445 }
446 
447 /*
448  * clean up the board for another game
449  */
450 static void
451 cleanupboard(void)
452 {
453 	int cnt, row, col;
454 	struct cardtype *ptr;
455 
456 	col = 0;
457 	if (Cflag) {
458 		clearstat();
459 		for(ptr = stock, row = stockrow;
460 		    ptr != NIL;
461 		    ptr = ptr->next, row++) {
462 			move(row, sidecol);
463 			printw("     ");
464 		}
465 		move(row, sidecol);
466 		printw("     ");
467 		move(stockrow + 1, sidecol);
468 		printw("=---=");
469 		move(talonrow - 2, sidecol);
470 		printw("talon");
471 		move(talonrow - 1, sidecol);
472 		printw("=---=");
473 		move(talonrow + 1, sidecol);
474 		printw("=---=");
475 	}
476 	move(stockrow, sidecol);
477 	printw("|   |");
478 	move(talonrow, sidecol);
479 	printw("|   |");
480 	move(foundrow, fttlcol);
481 	printw("|   |  |   |  |   |  |   |");
482 	for (cnt = 0; cnt < 4; cnt++) {
483 		switch(cnt) {
484 		case 0:
485 			col = atabcol;
486 			break;
487 		case 1:
488 			col = btabcol;
489 			break;
490 		case 2:
491 			col = ctabcol;
492 			break;
493 		case 3:
494 			col = dtabcol;
495 			break;
496 		}
497 		for(ptr = tableau[cnt], row = tabrow;
498 		    ptr != NIL;
499 		    ptr = ptr->next, row++)
500 			removecard(col, row);
501 	}
502 }
503 
504 /*
505  * procedure to create a deck of cards
506  */
507 static void
508 initdeck(struct cardtype **ldeck)
509 {
510 	int i;
511 	int scnt;
512 	char s;
513 	int r;
514 
515 	i = 0;
516 	for (scnt=0; scnt<4; scnt++) {
517 		s = suitmap[scnt];
518 		for (r=Ace; r<=King; r++) {
519 			ldeck[i] = &cards[i];
520 			cards[i].rank = r;
521 			cards[i].suit = s;
522 			cards[i].color = colormap[scnt];
523 			cards[i].next = NIL;
524 			i++;
525 		}
526 	}
527 }
528 
529 /*
530  * procedure to shuffle the deck
531  */
532 static void
533 shuffle(struct cardtype **ldeck)
534 {
535 	int i,j;
536 	struct cardtype *temp;
537 
538 	for (i=0; i<decksize; i++) {
539 		ldeck[i]->visible = FALSE;
540 		ldeck[i]->paid = FALSE;
541 	}
542 	for (i = decksize-1; i>=0; i--) {
543 		j = random() % decksize;
544 		if (i != j) {
545 			temp = ldeck[i];
546 			ldeck[i] = ldeck[j];
547 			ldeck[j] = temp;
548 		}
549 	}
550 }
551 
552 /*
553  * procedure to remove the card from the board
554  */
555 static void
556 removecard(int a, int b)
557 {
558 	move(b, a);
559 	printw("   ");
560 }
561 
562 /*
563  * procedure to print the cards on the board
564  */
565 static void
566 printrank(int a, int b, struct cardtype *cp, bool inverse)
567 {
568 	move(b, a);
569 	if (cp->rank != 10)
570 		addch(' ');
571 	if (inverse)
572 		standout();
573 	switch (cp->rank) {
574 		case 2: case 3: case 4: case 5: case 6: case 7:
575 		case 8: case 9: case 10:
576 			printw("%d", cp->rank);
577 			break;
578 		case Ace:
579 			addch('A');
580 			break;
581 		case Jack:
582 			addch('J');
583 			break;
584 		case Queen:
585 			addch('Q');
586 			break;
587 		case King:
588 			addch('K');
589 	}
590 	if (inverse)
591 		standend();
592 }
593 
594 /*
595  * procedure to print out a card
596  */
597 static void
598 printcard(int a, int b, struct cardtype *cp)
599 {
600 	if (cp == NIL)
601 		removecard(a, b);
602 	else if (cp->visible == FALSE) {
603 		move(b, a);
604 		printw(" ? ");
605 	} else {
606 		bool inverse = (cp->suit == 'd' || cp->suit == 'h');
607 
608 		printrank(a, b, cp, inverse);
609 		if (inverse)
610 			standout();
611 		addch(cp->suit);
612 		if (inverse)
613 			standend();
614 	}
615 }
616 
617 /*
618  * procedure to move the top card from one location to the top
619  * of another location. The pointers always point to the top
620  * of the piles.
621  */
622 static void
623 transit(struct cardtype **source, struct cardtype **dest)
624 {
625 	struct cardtype *temp;
626 
627 	temp = *source;
628 	*source = (*source)->next;
629 	temp->next = *dest;
630 	*dest = temp;
631 }
632 
633 /*
634  * Procedure to set the cards on the foundation base when available.
635  * Note that it is only called on a foundation pile at the beginning of
636  * the game, so the pile will have exactly one card in it.
637  */
638 static void
639 fndbase(struct cardtype **cp, int column, int row)
640 {
641 	bool nomore;
642 
643 	if (*cp != NIL)
644 		do {
645 			if ((*cp)->rank == basecard->rank) {
646 				base++;
647 				printcard(pilemap[base], foundrow, *cp);
648 				if (*cp == tableau[0])
649 					length[0] = length[0] - 1;
650 				if (*cp == tableau[1])
651 					length[1] = length[1] - 1;
652 				if (*cp == tableau[2])
653 					length[2] = length[2] - 1;
654 				if (*cp == tableau[3])
655 					length[3] = length[3] - 1;
656 				transit(cp, &found[base]);
657 				if (cp == &talon)
658 					usedtalon();
659 				if (cp == &stock)
660 					usedstock();
661 				if (*cp != NIL) {
662 					printcard(column, row, *cp);
663 					nomore = FALSE;
664 				} else {
665 					removecard(column, row);
666 					nomore = TRUE;
667 				}
668 				cardsoff++;
669 				if (infullgame) {
670 					this.wins += valuepercardup;
671 					game.wins += valuepercardup;
672 					total.wins += valuepercardup;
673 				}
674 			} else
675 				nomore = TRUE;
676 	} while (nomore == FALSE);
677 }
678 
679 /*
680  * procedure to initialize the things necessary for the game
681  */
682 static void
683 initgame(void)
684 {
685 	int i;
686 
687 	for (i=0; i<18; i++) {
688 		deck[i]->visible = TRUE;
689 		deck[i]->paid = TRUE;
690 	}
691 	stockcnt = 13;
692 	stock = deck[12];
693 	for (i=12; i>=1; i--)
694 		deck[i]->next = deck[i - 1];
695 	deck[0]->next = NIL;
696 	found[0] = deck[13];
697 	deck[13]->next = NIL;
698 	for (i=1; i<4; i++)
699 		found[i] = NIL;
700 	basecard = found[0];
701 	for (i=14; i<18; i++) {
702 		tableau[i - 14] = deck[i];
703 		deck[i]->next = NIL;
704 	}
705 	for (i=0; i<4; i++) {
706 		bottom[i] = tableau[i];
707 		length[i] = tabrow;
708 	}
709 	hand = deck[18];
710 	for (i=18; i<decksize-1; i++)
711 		deck[i]->next = deck[i + 1];
712 	deck[decksize-1]->next = NIL;
713 	talon = NIL;
714 	base = 0;
715 	cinhand = 34;
716 	taloncnt = 0;
717 	timesthru = 0;
718 	cardsoff = 1;
719 	coldrow = ctoprow;
720 	coldcol = cinitcol;
721 	cnewrow = ctoprow;
722 	cnewcol = cinitcol + cwidthcol;
723 }
724 
725 /*
726  * procedure to print the beginning cards and to start each game
727  */
728 static void
729 startgame(void)
730 {
731 	int j;
732 
733 	shuffle(deck);
734 	initgame();
735 	this.hand = costofhand;
736 	game.hand += costofhand;
737 	total.hand += costofhand;
738 	this.inspection = 0;
739 	this.game = 0;
740 	this.runs = 0;
741 	this.information = 0;
742 	this.wins = 0;
743 	this.thinktime = 0;
744 	infullgame = FALSE;
745 	startedgame = FALSE;
746 	printcard(foundcol, foundrow, found[0]);
747 	printcard(stockcol, stockrow, stock);
748 	printcard(atabcol, tabrow, tableau[0]);
749 	printcard(btabcol, tabrow, tableau[1]);
750 	printcard(ctabcol, tabrow, tableau[2]);
751 	printcard(dtabcol, tabrow, tableau[3]);
752 	printcard(taloncol, talonrow, talon);
753 	move(foundrow - 2, basecol);
754 	printw("Base");
755 	move(foundrow - 1, basecol);
756 	printw("Rank");
757 	printrank(basecol, foundrow, found[0], 0);
758 	for (j=0; j<=3; j++)
759 		fndbase(&tableau[j], pilemap[j], tabrow);
760 	fndbase(&stock, stockcol, stockrow);
761 	showstat();	/* show card counting info to cheaters */
762 	movetotalon();
763 	updatebettinginfo();
764 }
765 
766 /*
767  * procedure to clear the message printed from an error
768  */
769 static void
770 clearmsg(void)
771 {
772 	int i;
773 
774 	if (errmsg == TRUE) {
775 		errmsg = FALSE;
776 		move(msgrow, msgcol);
777 		for (i=0; i<25; i++)
778 			addch(' ');
779 		refresh();
780 	}
781 }
782 
783 /*
784  * procedure to print an error message if the move is not listed
785  */
786 static void
787 dumberror(void)
788 {
789 	errmsg = TRUE;
790 	move(msgrow, msgcol);
791 	printw("Not a proper move       ");
792 }
793 
794 /*
795  * procedure to print an error message if the move is not possible
796  */
797 static void
798 destinerror(void)
799 {
800 	errmsg = TRUE;
801 	move(msgrow, msgcol);
802 	printw("Error: Can't move there");
803 }
804 
805 /*
806  * function to see if the source has cards in it
807  */
808 static bool
809 notempty(struct cardtype *cp)
810 {
811 	if (cp == NIL) {
812 		errmsg = TRUE;
813 		move(msgrow, msgcol);
814 		printw("Error: no cards to move");
815 		return (FALSE);
816 	} else
817 		return (TRUE);
818 }
819 
820 /*
821  * function to see if the rank of one card is less than another
822  */
823 static bool
824 ranklower(struct cardtype *cp1, struct cardtype *cp2)
825 {
826 	if (cp2->rank == Ace)
827 		if (cp1->rank == King)
828 			return (TRUE);
829 		else
830 			return (FALSE);
831 	else if (cp1->rank + 1 == cp2->rank)
832 		return (TRUE);
833 	else
834 		return (FALSE);
835 }
836 
837 /*
838  * function to check the cardcolor for moving to a tableau
839  */
840 static bool
841 diffcolor(struct cardtype *cp1, struct cardtype *cp2)
842 {
843 	if (cp1->color == cp2->color)
844 		return (FALSE);
845 	else
846 		return (TRUE);
847 }
848 
849 /*
850  * function to see if the card can move to the tableau
851  */
852 static bool
853 tabok(struct cardtype *cp, int des)
854 {
855 	if ((cp == stock) && (tableau[des] == NIL))
856 		return (TRUE);
857 	else if (tableau[des] == NIL)
858 		if (stock == NIL &&
859 		    cp != bottom[0] && cp != bottom[1] &&
860 		    cp != bottom[2] && cp != bottom[3])
861 			return (TRUE);
862 		else
863 			return (FALSE);
864 	else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des]))
865 		return (TRUE);
866 	else
867 		return (FALSE);
868 }
869 
870 /*
871  * procedure to turn the cards onto the talon from the deck
872  */
873 static void
874 movetotalon(void)
875 {
876 	int i, fin;
877 
878 	if (cinhand <= 3 && cinhand > 0) {
879 		move(msgrow, msgcol);
880 		printw("Hand is now empty        ");
881 	}
882 	if (cinhand >= 3)
883 		fin = 3;
884 	else if (cinhand > 0)
885 		fin = cinhand;
886 	else if (talon != NIL) {
887 		timesthru++;
888 		errmsg = TRUE;
889 		move(msgrow, msgcol);
890 		if (timesthru != 4) {
891 			printw("Talon is now the new hand");
892 			this.runs += costofrunthroughhand;
893 			game.runs += costofrunthroughhand;
894 			total.runs += costofrunthroughhand;
895 			while (talon != NIL) {
896 				transit(&talon, &hand);
897 				cinhand++;
898 			}
899 			if (cinhand >= 3)
900 				fin = 3;
901 			else
902 				fin = cinhand;
903 			taloncnt = 0;
904 			coldrow = ctoprow;
905 			coldcol = cinitcol;
906 			cnewrow = ctoprow;
907 			cnewcol = cinitcol + cwidthcol;
908 			clearstat();
909 			showstat();
910 		} else {
911 			fin = 0;
912 			done = TRUE;
913 			printw("I believe you have lost");
914 			refresh();
915 			sleep(5);
916 		}
917 	} else {
918 		errmsg = TRUE;
919 		move(msgrow, msgcol);
920 		printw("Talon and hand are empty");
921 		fin = 0;
922 	}
923 	for (i=0; i<fin; i++) {
924 		transit(&hand, &talon);
925 		INCRHAND(cnewrow, cnewcol);
926 		INCRHAND(coldrow, coldcol);
927 		removecard(cnewcol, cnewrow);
928 		if (i == fin - 1)
929 			talon->visible = TRUE;
930 		if (Cflag) {
931 			if (talon->paid == FALSE && talon->visible == TRUE) {
932 				this.information += costofinformation;
933 				game.information += costofinformation;
934 				total.information += costofinformation;
935 				talon->paid = TRUE;
936 			}
937 			printcard(coldcol, coldrow, talon);
938 		}
939 	}
940 	if (fin != 0) {
941 		printcard(taloncol, talonrow, talon);
942 		cinhand -= fin;
943 		taloncnt += fin;
944 		if (Cflag) {
945 			move(handstatrow, handstatcol);
946 			printw("%3d", cinhand);
947 			move(talonstatrow, talonstatcol);
948 			printw("%3d", taloncnt);
949 		}
950 		fndbase(&talon, taloncol, talonrow);
951 	}
952 }
953 
954 
955 /*
956  * procedure to print card counting info on screen
957  */
958 static void
959 showstat(void)
960 {
961 	int row, col;
962 	struct cardtype *ptr;
963 
964 	if (!Cflag)
965 		return;
966 	move(talonstatrow, talonstatcol - 7);
967 	printw("Talon: %3d", taloncnt);
968 	move(handstatrow, handstatcol - 7);
969 	printw("Hand:  %3d", cinhand);
970 	move(stockstatrow, stockstatcol - 7);
971 	printw("Stock: %3d", stockcnt);
972 	for ( row = coldrow, col = coldcol, ptr = talon;
973 	      ptr != NIL;
974 	      ptr = ptr->next ) {
975 		if (ptr->paid == FALSE && ptr->visible == TRUE) {
976 			ptr->paid = TRUE;
977 			this.information += costofinformation;
978 			game.information += costofinformation;
979 			total.information += costofinformation;
980 		}
981 		printcard(col, row, ptr);
982 		DECRHAND(row, col);
983 	}
984 	for ( row = cnewrow, col = cnewcol, ptr = hand;
985 	      ptr != NIL;
986 	      ptr = ptr->next ) {
987 		if (ptr->paid == FALSE && ptr->visible == TRUE) {
988 			ptr->paid = TRUE;
989 			this.information += costofinformation;
990 			game.information += costofinformation;
991 			total.information += costofinformation;
992 		}
993 		INCRHAND(row, col);
994 		printcard(col, row, ptr);
995 	}
996 }
997 
998 /*
999  * procedure to clear card counting info from screen
1000  */
1001 static void
1002 clearstat(void)
1003 {
1004 	int row;
1005 
1006 	move(talonstatrow, talonstatcol - 7);
1007 	printw("          ");
1008 	move(handstatrow, handstatcol - 7);
1009 	printw("          ");
1010 	move(stockstatrow, stockstatcol - 7);
1011 	printw("          ");
1012 	for ( row = ctoprow ; row <= cbotrow ; row++ ) {
1013 		move(row, cinitcol);
1014 		printw("%56s", " ");
1015 	}
1016 }
1017 
1018 /*
1019  * procedure to update card counting base
1020  */
1021 static void
1022 usedtalon(void)
1023 {
1024 	removecard(coldcol, coldrow);
1025 	DECRHAND(coldrow, coldcol);
1026 	if (talon != NIL && (talon->visible == FALSE)) {
1027 		talon->visible = TRUE;
1028 		if (Cflag) {
1029 			this.information += costofinformation;
1030 			game.information += costofinformation;
1031 			total.information += costofinformation;
1032 			talon->paid = TRUE;
1033 			printcard(coldcol, coldrow, talon);
1034 		}
1035 	}
1036 	taloncnt--;
1037 	if (Cflag) {
1038 		move(talonstatrow, talonstatcol);
1039 		printw("%3d", taloncnt);
1040 	}
1041 }
1042 
1043 /*
1044  * procedure to update stock card counting base
1045  */
1046 static void
1047 usedstock(void)
1048 {
1049 	stockcnt--;
1050 	if (Cflag) {
1051 		move(stockstatrow, stockstatcol);
1052 		printw("%3d", stockcnt);
1053 	}
1054 }
1055 
1056 /*
1057  * let 'em know how they lost!
1058  */
1059 static void
1060 showcards(void)
1061 {
1062 	struct cardtype *ptr;
1063 	int row;
1064 
1065 	if (!Cflag || cardsoff == 52)
1066 		return;
1067 	for (ptr = talon; ptr != NIL; ptr = ptr->next) {
1068 		ptr->visible = TRUE;
1069 		ptr->paid = TRUE;
1070 	}
1071 	for (ptr = hand; ptr != NIL; ptr = ptr->next) {
1072 		ptr->visible = TRUE;
1073 		ptr->paid = TRUE;
1074 	}
1075 	showstat();
1076 	move(stockrow + 1, sidecol);
1077 	printw("     ");
1078 	move(talonrow - 2, sidecol);
1079 	printw("     ");
1080 	move(talonrow - 1, sidecol);
1081 	printw("     ");
1082 	move(talonrow, sidecol);
1083 	printw("     ");
1084 	move(talonrow + 1, sidecol);
1085 	printw("     ");
1086 	for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) {
1087 		move(row, stockcol - 1);
1088 		printw("|   |");
1089 		printcard(stockcol, row, ptr);
1090 	}
1091 	if (stock == NIL) {
1092 		move(row, stockcol - 1);
1093 		printw("|   |");
1094 		row++;
1095 	}
1096 	move(handstatrow, handstatcol - 7);
1097 	printw("          ");
1098 	move(row, stockcol - 1);
1099 	printw("=---=");
1100 	if ( cardsoff == 52 )
1101 		getcmd(moverow, movecol, "Hit return to exit");
1102 }
1103 
1104 /*
1105  * procedure to update the betting values
1106  */
1107 static void
1108 updatebettinginfo(void)
1109 {
1110 	long thiscosts, gamecosts, totalcosts;
1111 	double thisreturn, gamereturn, totalreturn;
1112 	time_t now;
1113 	long dollars;
1114 
1115 	time(&now);
1116 	dollars = (now - acctstart) / secondsperdollar;
1117 	if (dollars > 0) {
1118 		acctstart += dollars * secondsperdollar;
1119 		if (dollars > maxtimecharge)
1120 			dollars = maxtimecharge;
1121 		this.thinktime += dollars;
1122 		game.thinktime += dollars;
1123 		total.thinktime += dollars;
1124 	}
1125 	thiscosts = this.hand + this.inspection + this.game +
1126 		this.runs + this.information + this.thinktime;
1127 	gamecosts = game.hand + game.inspection + game.game +
1128 		game.runs + game.information + game.thinktime;
1129 	totalcosts = total.hand + total.inspection + total.game +
1130 		total.runs + total.information + total.thinktime;
1131 	this.worth = this.wins - thiscosts;
1132 	game.worth = game.wins - gamecosts;
1133 	total.worth = total.wins - totalcosts;
1134 	thisreturn = ((double)this.wins / (double)thiscosts - 1.0) * 100.0;
1135 	gamereturn = ((double)game.wins / (double)gamecosts - 1.0) * 100.0;
1136 	totalreturn = ((double)total.wins / (double)totalcosts - 1.0) * 100.0;
1137 	if (status != BETTINGBOX)
1138 		return;
1139 	move(tboxrow + 2, boxcol + 13);
1140 	printw("%4d%8d%9d", this.hand, game.hand, total.hand);
1141 	move(tboxrow + 3, boxcol + 13);
1142 	printw("%4d%8d%9d", this.inspection, game.inspection, total.inspection);
1143 	move(tboxrow + 4, boxcol + 13);
1144 	printw("%4d%8d%9d", this.game, game.game, total.game);
1145 	move(tboxrow + 5, boxcol + 13);
1146 	printw("%4d%8d%9d", this.runs, game.runs, total.runs);
1147 	move(tboxrow + 6, boxcol + 13);
1148 	printw("%4d%8d%9d", this.information, game.information,
1149 		total.information);
1150 	move(tboxrow + 7, boxcol + 13);
1151 	printw("%4d%8d%9d", this.thinktime, game.thinktime, total.thinktime);
1152 	move(tboxrow + 8, boxcol + 13);
1153 	printw("%4d%8d%9d", thiscosts, gamecosts, totalcosts);
1154 	move(tboxrow + 9, boxcol + 13);
1155 	printw("%4d%8d%9d", this.wins, game.wins, total.wins);
1156 	move(tboxrow + 10, boxcol + 13);
1157 	printw("%4d%8d%9d", this.worth, game.worth, total.worth);
1158 	move(tboxrow + 11, boxcol + 13);
1159 	printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn, gamereturn, totalreturn);
1160 }
1161 
1162 /*
1163  * procedure to move a card from the stock or talon to the tableau
1164  */
1165 static void
1166 simpletableau(struct cardtype **cp, int des)
1167 {
1168 	int origin;
1169 
1170 	if (notempty(*cp)) {
1171 		if (tabok(*cp, des)) {
1172 			if (*cp == stock)
1173 				origin = stk;
1174 			else
1175 				origin = tal;
1176 			if (tableau[des] == NIL)
1177 				bottom[des] = *cp;
1178 			transit(cp, &tableau[des]);
1179 			length[des]++;
1180 			printcard(pilemap[des], length[des], tableau[des]);
1181 			timesthru = 0;
1182 			if (origin == stk) {
1183 				usedstock();
1184 				printcard(stockcol, stockrow, stock);
1185 			} else {
1186 				usedtalon();
1187 				printcard(taloncol, talonrow, talon);
1188 			}
1189 		} else
1190 			destinerror();
1191 	}
1192 }
1193 
1194 /*
1195  * print the tableau
1196  */
1197 static void
1198 tabprint(int sour, int des)
1199 {
1200 	int dlength, slength, i;
1201 	struct cardtype *tempcard;
1202 
1203 	for (i=tabrow; i<=length[sour]; i++)
1204 		removecard(pilemap[sour], i);
1205 	dlength = length[des] + 1;
1206 	slength = length[sour];
1207 	if (slength == tabrow)
1208 		printcard(pilemap[des], dlength, tableau[sour]);
1209 	else
1210 		while (slength != tabrow - 1) {
1211 			tempcard = tableau[sour];
1212 			for (i=1; i<=slength-tabrow; i++)
1213 			    tempcard = tempcard->next;
1214 			printcard(pilemap[des], dlength, tempcard);
1215 			slength--;
1216 			dlength++;
1217 		}
1218 }
1219 
1220 /*
1221  * procedure to move from the tableau to the tableau
1222  */
1223 static void
1224 tabtotab(int sour, int des)
1225 {
1226 	struct cardtype *temp;
1227 
1228 	if (notempty(tableau[sour])) {
1229 		if (tabok(bottom[sour], des)) {
1230 			tabprint(sour, des);
1231 			temp = bottom[sour];
1232 			bottom[sour] = NIL;
1233 			if (bottom[des] == NIL)
1234 				bottom[des] = temp;
1235 			temp->next = tableau[des];
1236 			tableau[des] = tableau[sour];
1237 			tableau[sour] = NIL;
1238 			length[des] = length[des] + (length[sour] - (tabrow - 1));
1239 			length[sour] = tabrow - 1;
1240 			timesthru = 0;
1241 		} else
1242 			destinerror();
1243 	}
1244 }
1245 
1246 /*
1247  * functions to see if the card can go onto the foundation
1248  */
1249 static bool
1250 rankhigher(struct cardtype *cp, int let)
1251 {
1252 	if (found[let]->rank == King)
1253 		if (cp->rank == Ace)
1254 			return(TRUE);
1255 		else
1256 			return(FALSE);
1257 	else if (cp->rank - 1 == found[let]->rank)
1258 		return(TRUE);
1259 	else
1260 		return(FALSE);
1261 }
1262 
1263 /*
1264  * function to determine if two cards are the same suit
1265  */
1266 static bool
1267 samesuit(struct cardtype *cp, int let)
1268 {
1269 	if (cp->suit == found[let]->suit)
1270 		return (TRUE);
1271 	else
1272 		return (FALSE);
1273 }
1274 
1275 /*
1276  * procedure to move a card to the correct foundation pile
1277  */
1278 static void
1279 movetofound(struct cardtype **cp, int source)
1280 {
1281 	tempbase = 0;
1282 	mtfdone = FALSE;
1283 	if (notempty(*cp)) {
1284 		do {
1285 			if (found[tempbase] != NIL)
1286 				if (rankhigher(*cp, tempbase)
1287 				    && samesuit(*cp, tempbase)) {
1288 					if (*cp == stock)
1289 						mtforigin = stk;
1290 					else if (*cp == talon)
1291 						mtforigin = tal;
1292 					else
1293 						mtforigin = tab;
1294 					transit(cp, &found[tempbase]);
1295 					printcard(pilemap[tempbase],
1296 						foundrow, found[tempbase]);
1297 					timesthru = 0;
1298 					if (mtforigin == stk) {
1299 						usedstock();
1300 						printcard(stockcol, stockrow, stock);
1301 					} else if (mtforigin == tal) {
1302 						usedtalon();
1303 						printcard(taloncol, talonrow, talon);
1304 					} else {
1305 						removecard(pilemap[source], 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(__unused int sig)
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(__unused int sig)
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 
1765 	instruct();
1766 	makeboard();
1767 	for (;;) {
1768 		startgame();
1769 		movecard();
1770 		if (finish())
1771 			break;
1772 		if (cardsoff == 52)
1773 			makeboard();
1774 		else
1775 			cleanupboard();
1776 	}
1777 	cleanup(0);
1778 	/* NOTREACHED */
1779 	return (EXIT_FAILURE);
1780 }
1781