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