xref: /netbsd/games/canfield/canfield/canfield.c (revision abca1099)
1 /*	$NetBSD: canfield.c,v 1.31 2021/05/02 12:50:44 rillig Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)canfield.c	8.1 (Berkeley) 5/31/93";
41 #else
42 __RCSID("$NetBSD: canfield.c,v 1.31 2021/05/02 12:50:44 rillig Exp $");
43 #endif
44 #endif /* not lint */
45 
46 /*
47  * The canfield program
48  *
49  * Authors:
50  *	Originally written: Steve Levine
51  *	Converted to use curses and debugged: Steve Feldman
52  *	Card counting: Kirk McKusick and Mikey Olson
53  *	User interface cleanups: Eric Allman and Kirk McKusick
54  *	Betting by Kirk McKusick
55  */
56 
57 #include <sys/types.h>
58 
59 #include <ctype.h>
60 #include <curses.h>
61 #include <fcntl.h>
62 #include <signal.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <termios.h>
66 #include <time.h>
67 #include <unistd.h>
68 
69 #include "betinfo.h"
70 #include "pathnames.h"
71 
72 #define	decksize	52
73 #define originrow	0
74 #define origincol	0
75 #define	basecol		1
76 #define	boxcol		42
77 #define	tboxrow		2
78 #define	bboxrow		17
79 #define	movecol		43
80 #define	moverow		16
81 #define	msgcol		43
82 #define	msgrow		15
83 #define	titlecol	30
84 #define	titlerow	0
85 #define	sidecol		1
86 #define	ottlrow		6
87 #define	foundcol	11
88 #define	foundrow	3
89 #define	stockcol	2
90 #define	stockrow	8
91 #define	fttlcol		10
92 #define	fttlrow		1
93 #define	taloncol	2
94 #define	talonrow	13
95 #define	tabrow		8
96 #define ctoprow		21
97 #define cbotrow		23
98 #define cinitcol	14
99 #define cheightcol	1
100 #define cwidthcol	4
101 #define handstatrow	21
102 #define handstatcol	7
103 #define talonstatrow	22
104 #define talonstatcol	7
105 #define stockstatrow	23
106 #define stockstatcol	7
107 #define	Ace		1
108 #define	Jack		11
109 #define	Queen		12
110 #define	King		13
111 #define	atabcol		11
112 #define	btabcol		18
113 #define	ctabcol		25
114 #define	dtabcol		32
115 
116 #define	spades		's'
117 #define	clubs		'c'
118 #define	hearts		'h'
119 #define	diamonds	'd'
120 #define	black		'b'
121 #define	red		'r'
122 
123 #define stk		1
124 #define	tal		2
125 #define tab		3
126 #define INCRHAND(row, col) {\
127 	row -= cheightcol;\
128 	if (row < ctoprow) {\
129 		row = cbotrow;\
130 		col += cwidthcol;\
131 	}\
132 }
133 #define DECRHAND(row, col) {\
134 	row += cheightcol;\
135 	if (row > cbotrow) {\
136 		row = ctoprow;\
137 		col -= cwidthcol;\
138 	}\
139 }
140 
141 
142 struct cardtype {
143 	char suit;
144 	char color;
145 	bool visible;
146 	bool paid;
147 	int rank;
148 	struct cardtype *next;
149 };
150 
151 #define	NIL	((struct cardtype *) -1)
152 
153 static struct cardtype *deck[decksize];
154 static struct cardtype cards[decksize];
155 static struct cardtype *bottom[4], *found[4], *tableau[4];
156 static struct cardtype *talon, *hand, *stock, *basecard;
157 static int length[4];
158 static int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru;
159 static char suitmap[4] = {spades, clubs, hearts, diamonds};
160 static char colormap[4] = {black, black, red, red};
161 static char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol};
162 static char srcpile, destpile;
163 static int mtforigin, tempbase;
164 static int coldcol, cnewcol, coldrow, cnewrow;
165 static bool errmsg, done;
166 static bool mtfdone, Cflag = FALSE;
167 #define INSTRUCTIONBOX	1
168 #define BETTINGBOX	2
169 #define NOBOX		3
170 static int status = INSTRUCTIONBOX;
171 static uid_t uid;
172 
173 /*
174  * Basic betting costs
175  */
176 #define costofhand		13
177 #define costofinspection	13
178 #define costofgame		26
179 #define costofrunthroughhand	 5
180 #define costofinformation	 1
181 #define secondsperdollar	60
182 #define maxtimecharge		 3
183 #define valuepercardup	 	 5
184 /*
185  * Variables associated with betting
186  */
187 static struct betinfo this, game, total;
188 static bool startedgame = FALSE, infullgame = FALSE;
189 static time_t acctstart;
190 static int dbfd = -1;
191 
192 static void	askquit(int);
193 static void	cleanup(int) __dead;
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(const struct cardtype *, const 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(const struct cardtype *);
215 static void	printbottombettingbox(void);
216 static void	printbottominstructions(void);
217 static void	printcard(int, int, const struct cardtype *);
218 static void	printrank(int, int, const struct cardtype *, bool);
219 static void	printtopbettingbox(void);
220 static void	printtopinstructions(void);
221 static bool	rankhigher(const struct cardtype *, int);
222 static bool	ranklower(const struct cardtype *, const struct cardtype *);
223 static void	removecard(int, int);
224 static int	samesuit(const 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(const 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
movebox(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
printtopinstructions(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
printtopbettingbox(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
clearabovemovebox(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
printbottominstructions(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
printbottombettingbox(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
clearbelowmovebox(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
makeboard(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
cleanupboard(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
initdeck(struct cardtype * ideck[])507 initdeck(struct cardtype *ideck[])
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 			ideck[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
shuffle(struct cardtype * ideck[])532 shuffle(struct cardtype *ideck[])
533 {
534 	int i,j;
535 	struct cardtype *temp;
536 
537 	for (i=0; i<decksize; i++) {
538 		ideck[i]->visible = FALSE;
539 		ideck[i]->paid = FALSE;
540 	}
541 	for (i = decksize-1; i>=0; i--) {
542 		j = random() % decksize;
543 		if (i != j) {
544 			temp = ideck[i];
545 			ideck[i] = ideck[j];
546 			ideck[j] = temp;
547 		}
548 	}
549 }
550 
551 /*
552  * procedure to remove the card from the board
553  */
554 static void
removecard(int a,int b)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
printrank(int a,int b,const struct cardtype * cp,bool inverse)565 printrank(int a, int b, const 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
printcard(int a,int b,const struct cardtype * cp)597 printcard(int a, int b, const 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
transit(struct cardtype ** source,struct cardtype ** dest)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
fndbase(struct cardtype ** cp,int column,int row)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
initgame(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
startgame(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
clearmsg(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
dumberror(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
destinerror(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
notempty(const struct cardtype * cp)808 notempty(const 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
ranklower(const struct cardtype * cp1,const struct cardtype * cp2)823 ranklower(const struct cardtype *cp1, const 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
diffcolor(const struct cardtype * cp1,const struct cardtype * cp2)840 diffcolor(const struct cardtype *cp1, const 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
tabok(const struct cardtype * cp,int des)852 tabok(const 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
movetotalon(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 
958 static void
showstat(void)959 showstat(void)
960 {
961 	int row, col;
962 	struct cardtype *ptr;
963 
964 	if (!Cflag)
965 		return;
966 	move(talonstatrow, talonstatcol - 7);
967 	printw("Talon: %3d", taloncnt);
968 	move(handstatrow, handstatcol - 7);
969 	printw("Hand:  %3d", cinhand);
970 	move(stockstatrow, stockstatcol - 7);
971 	printw("Stock: %3d", stockcnt);
972 	for ( row = coldrow, col = coldcol, ptr = talon;
973 	      ptr != NIL;
974 	      ptr = ptr->next ) {
975 		if (ptr->paid == FALSE && ptr->visible == TRUE) {
976 			ptr->paid = TRUE;
977 			this.information += costofinformation;
978 			game.information += costofinformation;
979 			total.information += costofinformation;
980 		}
981 		printcard(col, row, ptr);
982 		DECRHAND(row, col);
983 	}
984 	for ( row = cnewrow, col = cnewcol, ptr = hand;
985 	      ptr != NIL;
986 	      ptr = ptr->next ) {
987 		if (ptr->paid == FALSE && ptr->visible == TRUE) {
988 			ptr->paid = TRUE;
989 			this.information += costofinformation;
990 			game.information += costofinformation;
991 			total.information += costofinformation;
992 		}
993 		INCRHAND(row, col);
994 		printcard(col, row, ptr);
995 	}
996 }
997 
998 /*
999  * procedure to clear card counting info from screen
1000  */
1001 static void
clearstat(void)1002 clearstat(void)
1003 {
1004 	int row;
1005 
1006 	move(talonstatrow, talonstatcol - 7);
1007 	printw("          ");
1008 	move(handstatrow, handstatcol - 7);
1009 	printw("          ");
1010 	move(stockstatrow, stockstatcol - 7);
1011 	printw("          ");
1012 	for ( row = ctoprow ; row <= cbotrow ; row++ ) {
1013 		move(row, cinitcol);
1014 		printw("%56s", " ");
1015 	}
1016 }
1017 
1018 /*
1019  * procedure to update card counting base
1020  */
1021 static void
usedtalon(void)1022 usedtalon(void)
1023 {
1024 	removecard(coldcol, coldrow);
1025 	DECRHAND(coldrow, coldcol);
1026 	if (talon != NIL && (talon->visible == FALSE)) {
1027 		talon->visible = TRUE;
1028 		if (Cflag) {
1029 			this.information += costofinformation;
1030 			game.information += costofinformation;
1031 			total.information += costofinformation;
1032 			talon->paid = TRUE;
1033 			printcard(coldcol, coldrow, talon);
1034 		}
1035 	}
1036 	taloncnt--;
1037 	if (Cflag) {
1038 		move(talonstatrow, talonstatcol);
1039 		printw("%3d", taloncnt);
1040 	}
1041 }
1042 
1043 /*
1044  * procedure to update stock card counting base
1045  */
1046 static void
usedstock(void)1047 usedstock(void)
1048 {
1049 	stockcnt--;
1050 	if (Cflag) {
1051 		move(stockstatrow, stockstatcol);
1052 		printw("%3d", stockcnt);
1053 	}
1054 }
1055 
1056 /*
1057  * let 'em know how they lost!
1058  */
1059 static void
showcards(void)1060 showcards(void)
1061 {
1062 	struct cardtype *ptr;
1063 	int row;
1064 
1065 	if (!Cflag || cardsoff == 52)
1066 		return;
1067 	for (ptr = talon; ptr != NIL; ptr = ptr->next) {
1068 		ptr->visible = TRUE;
1069 		ptr->paid = TRUE;
1070 	}
1071 	for (ptr = hand; ptr != NIL; ptr = ptr->next) {
1072 		ptr->visible = TRUE;
1073 		ptr->paid = TRUE;
1074 	}
1075 	showstat();
1076 	move(stockrow + 1, sidecol);
1077 	printw("     ");
1078 	move(talonrow - 2, sidecol);
1079 	printw("     ");
1080 	move(talonrow - 1, sidecol);
1081 	printw("     ");
1082 	move(talonrow, sidecol);
1083 	printw("     ");
1084 	move(talonrow + 1, sidecol);
1085 	printw("     ");
1086 	for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) {
1087 		move(row, stockcol - 1);
1088 		printw("|   |");
1089 		printcard(stockcol, row, ptr);
1090 	}
1091 	if (stock == NIL) {
1092 		move(row, stockcol - 1);
1093 		printw("|   |");
1094 		row++;
1095 	}
1096 	move(handstatrow, handstatcol - 7);
1097 	printw("          ");
1098 	move(row, stockcol - 1);
1099 	printw("=---=");
1100 	if ( cardsoff == 52 )
1101 		getcmd(moverow, movecol, "Hit return to exit");
1102 }
1103 
1104 /*
1105  * procedure to update the betting values
1106  */
1107 static void
updatebettinginfo(void)1108 updatebettinginfo(void)
1109 {
1110 	long thiscosts, gamecosts, totalcosts;
1111 	double thisreturn, gamereturn, totalreturn;
1112 	time_t now;
1113 	long dollars;
1114 
1115 	time(&now);
1116 	dollars = (now - acctstart) / secondsperdollar;
1117 	if (dollars > 0) {
1118 		acctstart += dollars * secondsperdollar;
1119 		if (dollars > maxtimecharge)
1120 			dollars = maxtimecharge;
1121 		this.thinktime += dollars;
1122 		game.thinktime += dollars;
1123 		total.thinktime += dollars;
1124 	}
1125 	thiscosts = this.hand + this.inspection + this.game +
1126 		this.runs + this.information + this.thinktime;
1127 	gamecosts = game.hand + game.inspection + game.game +
1128 		game.runs + game.information + game.thinktime;
1129 	totalcosts = total.hand + total.inspection + total.game +
1130 		total.runs + total.information + total.thinktime;
1131 	this.worth = this.wins - thiscosts;
1132 	game.worth = game.wins - gamecosts;
1133 	total.worth = total.wins - totalcosts;
1134 	thisreturn = ((double)this.wins / (double)thiscosts - 1.0) * 100.0;
1135 	gamereturn = ((double)game.wins / (double)gamecosts - 1.0) * 100.0;
1136 	totalreturn = ((double)total.wins / (double)totalcosts - 1.0) * 100.0;
1137 	if (status != BETTINGBOX)
1138 		return;
1139 	move(tboxrow + 2, boxcol + 13);
1140 	printw("%4ld%8ld%9ld", this.hand, game.hand, total.hand);
1141 	move(tboxrow + 3, boxcol + 13);
1142 	printw("%4ld%8ld%9ld", this.inspection, game.inspection,
1143 	    total.inspection);
1144 	move(tboxrow + 4, boxcol + 13);
1145 	printw("%4ld%8ld%9ld", this.game, game.game, total.game);
1146 	move(tboxrow + 5, boxcol + 13);
1147 	printw("%4ld%8ld%9ld", this.runs, game.runs, total.runs);
1148 	move(tboxrow + 6, boxcol + 13);
1149 	printw("%4ld%8ld%9ld", this.information, game.information,
1150 		total.information);
1151 	move(tboxrow + 7, boxcol + 13);
1152 	printw("%4ld%8ld%9ld", this.thinktime, game.thinktime, total.thinktime);
1153 	move(tboxrow + 8, boxcol + 13);
1154 	printw("%4ld%8ld%9ld", thiscosts, gamecosts, totalcosts);
1155 	move(tboxrow + 9, boxcol + 13);
1156 	printw("%4ld%8ld%9ld", this.wins, game.wins, total.wins);
1157 	move(tboxrow + 10, boxcol + 13);
1158 	printw("%4ld%8ld%9ld", this.worth, game.worth, total.worth);
1159 	move(tboxrow + 11, boxcol + 13);
1160 	printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn, gamereturn, totalreturn);
1161 }
1162 
1163 /*
1164  * procedure to move a card from the stock or talon to the tableau
1165  */
1166 static void
simpletableau(struct cardtype ** cp,int des)1167 simpletableau(struct cardtype **cp, int des)
1168 {
1169 	int origin;
1170 
1171 	if (notempty(*cp)) {
1172 		if (tabok(*cp, des)) {
1173 			if (*cp == stock)
1174 				origin = stk;
1175 			else
1176 				origin = tal;
1177 			if (tableau[des] == NIL)
1178 				bottom[des] = *cp;
1179 			transit(cp, &tableau[des]);
1180 			length[des]++;
1181 			printcard(pilemap[des], length[des], tableau[des]);
1182 			timesthru = 0;
1183 			if (origin == stk) {
1184 				usedstock();
1185 				printcard(stockcol, stockrow, stock);
1186 			} else {
1187 				usedtalon();
1188 				printcard(taloncol, talonrow, talon);
1189 			}
1190 		} else
1191 			destinerror();
1192 	}
1193 }
1194 
1195 /*
1196  * print the tableau
1197  */
1198 static void
tabprint(int sour,int des)1199 tabprint(int sour, int des)
1200 {
1201 	int dlength, slength, i;
1202 	struct cardtype *tempcard;
1203 
1204 	for (i=tabrow; i<=length[sour]; i++)
1205 		removecard(pilemap[sour], i);
1206 	dlength = length[des] + 1;
1207 	slength = length[sour];
1208 	if (slength == tabrow)
1209 		printcard(pilemap[des], dlength, tableau[sour]);
1210 	else
1211 		while (slength != tabrow - 1) {
1212 			tempcard = tableau[sour];
1213 			for (i=1; i<=slength-tabrow; i++)
1214 			    tempcard = tempcard->next;
1215 			printcard(pilemap[des], dlength, tempcard);
1216 			slength--;
1217 			dlength++;
1218 		}
1219 }
1220 
1221 /*
1222  * procedure to move from the tableau to the tableau
1223  */
1224 static void
tabtotab(int sour,int des)1225 tabtotab(int sour, int des)
1226 {
1227 	struct cardtype *temp;
1228 
1229 	if (notempty(tableau[sour])) {
1230 		if (tabok(bottom[sour], des)) {
1231 			tabprint(sour, des);
1232 			temp = bottom[sour];
1233 			bottom[sour] = NIL;
1234 			if (bottom[des] == NIL)
1235 				bottom[des] = temp;
1236 			temp->next = tableau[des];
1237 			tableau[des] = tableau[sour];
1238 			tableau[sour] = NIL;
1239 			length[des] = length[des] +
1240 			    (length[sour] - (tabrow - 1));
1241 			length[sour] = tabrow - 1;
1242 			timesthru = 0;
1243 		} else
1244 			destinerror();
1245 	}
1246 }
1247 
1248 /*
1249  * functions to see if the card can go onto the foundation
1250  */
1251 static bool
rankhigher(const struct cardtype * cp,int let)1252 rankhigher(const struct cardtype *cp, int let)
1253 {
1254 	if (found[let]->rank == King)
1255 		if (cp->rank == Ace)
1256 			return(TRUE);
1257 		else
1258 			return(FALSE);
1259 	else if (cp->rank - 1 == found[let]->rank)
1260 		return(TRUE);
1261 	else
1262 		return(FALSE);
1263 }
1264 
1265 /*
1266  * function to determine if two cards are the same suit
1267  */
1268 static int
samesuit(const struct cardtype * cp,int let)1269 samesuit(const struct cardtype *cp, int let)
1270 {
1271 	if (cp->suit == found[let]->suit)
1272 		return (TRUE);
1273 	else
1274 		return (FALSE);
1275 }
1276 
1277 /*
1278  * procedure to move a card to the correct foundation pile
1279  */
1280 static void
movetofound(struct cardtype ** cp,int source)1281 movetofound(struct cardtype **cp, int source)
1282 {
1283 	tempbase = 0;
1284 	mtfdone = FALSE;
1285 	if (notempty(*cp)) {
1286 		do {
1287 			if (found[tempbase] != NIL)
1288 				if (rankhigher(*cp, tempbase)
1289 				    && samesuit(*cp, tempbase)) {
1290 					if (*cp == stock)
1291 						mtforigin = stk;
1292 					else if (*cp == talon)
1293 						mtforigin = tal;
1294 					else
1295 						mtforigin = tab;
1296 					transit(cp, &found[tempbase]);
1297 					printcard(pilemap[tempbase],
1298 						foundrow, found[tempbase]);
1299 					timesthru = 0;
1300 					if (mtforigin == stk) {
1301 						usedstock();
1302 						printcard(stockcol, stockrow,
1303 						    stock);
1304 					} else if (mtforigin == tal) {
1305 						usedtalon();
1306 						printcard(taloncol, talonrow,
1307 						    talon);
1308 					} else {
1309 						removecard(pilemap[source],
1310 						    length[source]);
1311 						length[source]--;
1312 					}
1313 					cardsoff++;
1314 					if (infullgame) {
1315 						this.wins += valuepercardup;
1316 						game.wins += valuepercardup;
1317 						total.wins += valuepercardup;
1318 					}
1319 					mtfdone = TRUE;
1320 				} else
1321 					tempbase++;
1322 			else
1323 				tempbase++;
1324 		} while ((tempbase != 4) && !mtfdone);
1325 		if (!mtfdone)
1326 			destinerror();
1327 	}
1328 }
1329 
1330 /*
1331  * procedure to get a command
1332  */
1333 static void
getcmd(int row,int col,const char * cp)1334 getcmd(int row, int col, const char *cp)
1335 {
1336 	char cmd[2] = { '\0', '\0'}, ch;
1337 	int i;
1338 
1339 	i = 0;
1340 	move(row, col);
1341 	printw("%-24s", cp);
1342 	col += 1 + strlen(cp);
1343 	move(row, col);
1344 	refresh();
1345 	do {
1346 		ch = getch() & 0177;
1347 		if (ch >= 'A' && ch <= 'Z')
1348 			ch += ('a' - 'A');
1349 		if (ch == '\f') {
1350 			wrefresh(curscr);
1351 			refresh();
1352 		} else if (i >= 2 && ch != erasechar() && ch != killchar()) {
1353 			if (ch != '\n' && ch != '\r' && ch != ' ')
1354 				write(1, "\007", 1);
1355 		} else if (ch == erasechar() && i > 0) {
1356 			printw("\b \b");
1357 			refresh();
1358 			cmd[--i] = '\0';
1359 		} else if (ch == killchar() && i > 0) {
1360 			while (i > 0) {
1361 				printw("\b \b");
1362 				cmd[--i] = '\0';
1363 			}
1364 			refresh();
1365 		} else if (ch == '\032') {	/* Control-Z */
1366 			suspend();
1367 			move(row, col + i);
1368 			refresh();
1369 		} else if (isprint((unsigned char)ch)) {
1370 			cmd[i++] = ch;
1371 			addch(ch);
1372 			refresh();
1373 		}
1374 	} while (ch != '\n' && ch != '\r' && ch != ' ');
1375 	srcpile = cmd[0];
1376 	destpile = cmd[1];
1377 }
1378 
1379 /*
1380  * Suspend the game (shell escape if no process control on system)
1381  */
1382 static void
suspend(void)1383 suspend(void)
1384 {
1385 #ifndef SIGTSTP
1386 	char *sh;
1387 #endif
1388 
1389 	updatebettinginfo();
1390 	move(21, 0);
1391 	refresh();
1392 	if (dbfd != -1) {
1393 		lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET);
1394 		write(dbfd, (char *)&total, sizeof(total));
1395 	}
1396 	kill(getpid(), SIGTSTP);
1397 	raw();
1398 	noecho();
1399 }
1400 
1401 /*
1402  * procedure to evaluate and make the specific moves
1403  */
1404 static void
movecard(void)1405 movecard(void)
1406 {
1407 	int source, dest;
1408 	char osrcpile, odestpile;
1409 
1410 	source = dest = 0;
1411 	done = FALSE;
1412 	errmsg = FALSE;
1413 	do {
1414 		if (talon == NIL && hand != NIL)
1415 			movetotalon();
1416 		if (cardsoff == 52) {
1417 			refresh();
1418 			srcpile = 'q';
1419 		} else if (!startedgame) {
1420 			move(msgrow, msgcol);
1421 			errmsg = TRUE;
1422 			switch (34 - taloncnt - cinhand) {
1423 			default:
1424 				errmsg = FALSE;
1425 				break;
1426 			case 1:
1427 				printw("One card used from talon  ");
1428 				break;
1429 			case 2:
1430 				printw("Two cards used from talon ");
1431 				break;
1432 			case 3:
1433 				printw(">3< cards used from talon ");
1434 				break;
1435 			}
1436 			getcmd(moverow, movecol, "Move:");
1437 		} else
1438 			getcmd(moverow, movecol, "Move:");
1439 		clearmsg();
1440 		if (srcpile >= '1' && srcpile <= '4')
1441 			source = (int) (srcpile - '1');
1442 		if (destpile >= '1' && destpile <= '4')
1443 			dest = (int) (destpile - '1');
1444 		if (!startedgame &&
1445 		    (srcpile == 't' || srcpile == 's' || srcpile == 'h' ||
1446 		     srcpile == '1' || srcpile == '2' || srcpile == '3' ||
1447 		     srcpile == '4')) {
1448 			startedgame = TRUE;
1449 			osrcpile = srcpile;
1450 			odestpile = destpile;
1451 			if (status != BETTINGBOX)
1452 				srcpile = 'y';
1453 			else do {
1454 				getcmd(moverow, movecol, "Inspect game?");
1455 			} while (srcpile != 'y' && srcpile != 'n');
1456 			if (srcpile == 'n') {
1457 				srcpile = 'q';
1458 			} else {
1459 				this.inspection += costofinspection;
1460 				game.inspection += costofinspection;
1461 				total.inspection += costofinspection;
1462 				srcpile = osrcpile;
1463 				destpile = odestpile;
1464 			}
1465 		}
1466 		switch (srcpile) {
1467 			case 't':
1468 				if (destpile == 'f' || destpile == 'F')
1469 					movetofound(&talon, source);
1470 				else if (destpile >= '1' && destpile <= '4')
1471 					simpletableau(&talon, dest);
1472 				else
1473 					dumberror();
1474 				break;
1475 			case 's':
1476 				if (destpile == 'f' || destpile == 'F')
1477 					movetofound(&stock, source);
1478 				else if (destpile >= '1' && destpile <= '4')
1479 					simpletableau(&stock, dest);
1480 				else dumberror();
1481 				break;
1482 			case 'h':
1483 				if (destpile != 't' && destpile != 'T') {
1484 					dumberror();
1485 					break;
1486 				}
1487 				if (infullgame) {
1488 					movetotalon();
1489 					break;
1490 				}
1491 				if (status == BETTINGBOX) {
1492 					do {
1493 						getcmd(moverow, movecol,
1494 							"Buy game?");
1495 					} while (srcpile != 'y' &&
1496 						 srcpile != 'n');
1497 					if (srcpile == 'n') {
1498 						showcards();
1499 						done = TRUE;
1500 						break;
1501 					}
1502 				}
1503 				infullgame = TRUE;
1504 				this.wins += valuepercardup * cardsoff;
1505 				game.wins += valuepercardup * cardsoff;
1506 				total.wins += valuepercardup * cardsoff;
1507 				this.game += costofgame;
1508 				game.game += costofgame;
1509 				total.game += costofgame;
1510 				movetotalon();
1511 				break;
1512 			case 'q':
1513 				showcards();
1514 				done = TRUE;
1515 				break;
1516 			case 'b':
1517 				printtopbettingbox();
1518 				printbottombettingbox();
1519 				status = BETTINGBOX;
1520 				break;
1521 			case 'x':
1522 				clearabovemovebox();
1523 				clearbelowmovebox();
1524 				status = NOBOX;
1525 				break;
1526 			case 'i':
1527 				printtopinstructions();
1528 				printbottominstructions();
1529 				status = INSTRUCTIONBOX;
1530 				break;
1531 			case 'c':
1532 				Cflag = !Cflag;
1533 				if (Cflag)
1534 					showstat();
1535 				else
1536 					clearstat();
1537 				break;
1538 			case '1': case '2': case '3': case '4':
1539 				if (destpile == 'f' || destpile == 'F')
1540 					movetofound(&tableau[source], source);
1541 				else if (destpile >= '1' && destpile <= '4')
1542 					tabtotab(source, dest);
1543 				else dumberror();
1544 				break;
1545 			default:
1546 				dumberror();
1547 		}
1548 		fndbase(&stock, stockcol, stockrow);
1549 		fndbase(&talon, taloncol, talonrow);
1550 		updatebettinginfo();
1551 	} while (!done);
1552 }
1553 
1554 static const char *const basicinstructions[] = {
1555 	"Here are brief instructions to the game of Canfield:\n\n",
1556 	"     If you have never played solitaire before, it is recom-\n",
1557 	"mended  that  you  consult  a solitaire instruction book. In\n",
1558 	"Canfield, tableau cards may be built on each other  downward\n",
1559 	"in  alternate colors. An entire pile must be moved as a unit\n",
1560 	"in building. Top cards of the piles are available to be able\n",
1561 	"to be played on foundations, but never into empty spaces.\n\n",
1562 	"     Spaces must be filled from the stock. The top  card  of\n",
1563 	"the  stock  also is available to be played on foundations or\n",
1564 	"built on tableau piles. After the stock  is  exhausted,  ta-\n",
1565 	"bleau spaces may be filled from the talon and the player may\n",
1566 	"keep them open until he wishes to use them.\n\n",
1567 	"     Cards are dealt from the hand to the  talon  by  threes\n",
1568 	"and  this  repeats until there are no more cards in the hand\n",
1569 	"or the player quits. To have cards dealt onto the talon  the\n",
1570 	"player  types  'ht'  for his move. Foundation base cards are\n",
1571 	"also automatically moved to the foundation when they  become\n",
1572 	"available.\n\n",
1573 	"push any key when you are finished: ",
1574 	0 };
1575 
1576 static const char *const bettinginstructions[] = {
1577 	"     The rules for betting are  somewhat  less  strict  than\n",
1578 	"those  used in the official version of the game. The initial\n",
1579 	"deal costs $13. You may quit at this point  or  inspect  the\n",
1580 	"game.  Inspection  costs  $13 and allows you to make as many\n",
1581 	"moves as is possible without moving any cards from your hand\n",
1582 	"to  the  talon.  (the initial deal places three cards on the\n",
1583 	"talon; if all these cards are  used,  three  more  are  made\n",
1584 	"available)  Finally, if the game seems interesting, you must\n",
1585 	"pay the final installment of $26.  At  this  point  you  are\n",
1586 	"credited  at the rate of $5 for each card on the foundation;\n",
1587 	"as the game progresses you are credited  with  $5  for  each\n",
1588 	"card  that is moved to the foundation.  Each run through the\n",
1589 	"hand after the first costs $5.  The  card  counting  feature\n",
1590 	"costs  $1  for  each unknown card that is identified. If the\n",
1591 	"information is toggled on, you are only  charged  for  cards\n",
1592 	"that  became  visible  since it was last turned on. Thus the\n",
1593 	"maximum cost of information is $34.  Playing time is charged\n",
1594 	"at a rate of $1 per minute.\n\n",
1595 	"push any key when you are finished: ",
1596 	0 };
1597 
1598 /*
1599  * procedure to printout instructions
1600  */
1601 static void
instruct(void)1602 instruct(void)
1603 {
1604 	const char *const *cp;
1605 
1606 	move(originrow, origincol);
1607 	printw("This is the game of solitaire called Canfield.  Do\n");
1608 	printw("you want instructions for the game?");
1609 	do {
1610 		getcmd(originrow + 3, origincol, "y or n?");
1611 	} while (srcpile != 'y' && srcpile != 'n');
1612 	if (srcpile == 'n')
1613 		return;
1614 	clear();
1615 	for (cp = basicinstructions; *cp != 0; cp++)
1616 		printw("%s", *cp);
1617 	refresh();
1618 	getch();
1619 	clear();
1620 	move(originrow, origincol);
1621 	printw("Do you want instructions for betting?");
1622 	do {
1623 		getcmd(originrow + 2, origincol, "y or n?");
1624 	} while (srcpile != 'y' && srcpile != 'n');
1625 	if (srcpile == 'n')
1626 		return;
1627 	clear();
1628 	for (cp = bettinginstructions; *cp != 0; cp++)
1629 		printw("%s", *cp);
1630 	refresh();
1631 	getch();
1632 }
1633 
1634 /*
1635  * procedure to initialize the game
1636  */
1637 static void
initall(void)1638 initall(void)
1639 {
1640 	ssize_t i;
1641 
1642 	srandom(time(NULL));
1643 	time(&acctstart);
1644 	initdeck(deck);
1645 	uid = getuid();
1646 	dbfd = open(_PATH_SCORE, O_RDWR);
1647 
1648 	/* Revoke setgid privileges */
1649 	setgid(getgid());
1650 
1651 	if (dbfd < 0)
1652 		return;
1653 	if (dbfd < 3)
1654 		exit(1);
1655 	if (lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET) < 0) {
1656 		close(dbfd);
1657 		dbfd = -1;
1658 		return;
1659 	}
1660 	i = read(dbfd, (char *)&total, sizeof(total));
1661 	if (i < 0) {
1662 		close(dbfd);
1663 		dbfd = -1;
1664 		return;
1665 	}
1666 }
1667 
1668 /*
1669  * procedure to end the game
1670  */
1671 static bool
finish(void)1672 finish(void)
1673 {
1674 	int row, col;
1675 
1676 	if (cardsoff == 52) {
1677 		getcmd(moverow, movecol, "Hit return to exit");
1678 		clear();
1679 		refresh();
1680 		move(originrow, origincol);
1681 		printw("CONGRATULATIONS!\n");
1682 		printw("You won the game. That is a feat to be proud of.\n");
1683 		row = originrow + 5;
1684 		col = origincol;
1685 	} else {
1686 		move(msgrow, msgcol);
1687 		printw("You got %d card", cardsoff);
1688 		if (cardsoff > 1)
1689 			printw("s");
1690 		printw(" off    ");
1691 		move(msgrow, msgcol);
1692 		row = moverow;
1693 		col = movecol;
1694 	}
1695 	do {
1696 		getcmd(row, col, "Play again (y or n)?");
1697 	} while (srcpile != 'y' && srcpile != 'n');
1698 	errmsg = TRUE;
1699 	clearmsg();
1700 	if (srcpile == 'y')
1701 		return (FALSE);
1702 	else
1703 		return (TRUE);
1704 }
1705 
1706 /*
1707  * procedure to clean up and exit
1708  */
1709 static void
cleanup(int dummy __unused)1710 cleanup(int dummy __unused)
1711 {
1712 
1713 	total.thinktime += 1;
1714 	status = NOBOX;
1715 	updatebettinginfo();
1716 	if (dbfd != -1) {
1717 		lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET);
1718 		write(dbfd, (char *)&total, sizeof(total));
1719 		close(dbfd);
1720 	}
1721 	clear();
1722 	move(22,0);
1723 	refresh();
1724 	endwin();
1725 	exit(0);
1726 	/* NOTREACHED */
1727 }
1728 
1729 /*
1730  * Field an interrupt.
1731  */
1732 static void
askquit(int dummy __unused)1733 askquit(int dummy __unused)
1734 {
1735 	move(msgrow, msgcol);
1736 	printw("Really wish to quit?    ");
1737 	do {
1738 		getcmd(moverow, movecol, "y or n?");
1739 	} while (srcpile != 'y' && srcpile != 'n');
1740 	clearmsg();
1741 	if (srcpile == 'y')
1742 		cleanup(0);
1743 	signal(SIGINT, askquit);
1744 }
1745 
1746 /*
1747  * Can you tell that this used to be a Pascal program?
1748  */
1749 int
main(void)1750 main(void)
1751 {
1752 #ifdef MAXLOAD
1753 	double vec[3];
1754 
1755 	loadav(vec);
1756 	if (vec[2] >= MAXLOAD) {
1757 		puts("The system load is too high.  Try again later.");
1758 		exit(0);
1759 	}
1760 #endif
1761 	signal(SIGINT, askquit);
1762 	signal(SIGHUP, cleanup);
1763 	signal(SIGTERM, cleanup);
1764 	if (!initscr()) {
1765 		fprintf(stderr, "couldn't initialize screen\n");
1766 		exit (0);
1767 	}
1768 	raw();
1769 	noecho();
1770 	initall();
1771 	instruct();
1772 	makeboard();
1773 	for (;;) {
1774 		startgame();
1775 		movecard();
1776 		if (finish())
1777 			break;
1778 		if (cardsoff == 52)
1779 			makeboard();
1780 		else
1781 			cleanupboard();
1782 	}
1783 	cleanup(0);
1784 	/* NOTREACHED */
1785 	return(0);
1786 }
1787