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