1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)canfield.c 4.1 10/24/82";
4 
5 /*
6  * The canfield program
7  *
8  * Authors:
9  *	Originally written: Steve Levine
10  *	Converted to use curses and debugged: Steve Feldman
11  *	Card counting: Kirk McKusick and Mikey Olson
12  */
13 
14 #include <curses.h>
15 #include <ctype.h>
16 #include <signal.h>
17 
18 #define	decksize	52
19 #define originrow	0
20 #define origincol	0
21 #define	basecol		1
22 #define	boxcol		42
23 #define	tboxrow		2
24 #define	bboxrow		16
25 #define	movecol		43
26 #define	moverow		15
27 #define	msgcol		43
28 #define	msgrow		14
29 #define	titlecol	30
30 #define	titlerow	0
31 #define	sidecol		1
32 #define	ottlrow		6
33 #define	foundcol	11
34 #define	foundrow	3
35 #define	stockcol	2
36 #define	stockrow	8
37 #define	fttlcol		10
38 #define	fttlrow		1
39 #define	taloncol	2
40 #define	talonrow	13
41 #define	tabrow		8
42 #define ctoprow		21
43 #define cbotrow		23
44 #define cinitcol	14
45 #define cheightcol	1
46 #define cwidthcol	4
47 #define handstatrow	21
48 #define handstatcol	7
49 #define talonstatrow	22
50 #define talonstatcol	7
51 #define stockstatrow	23
52 #define stockstatcol	7
53 #define	Ace		1
54 #define	Jack		11
55 #define	Queen		12
56 #define	King		13
57 #define	atabcol		11
58 #define	btabcol		18
59 #define	ctabcol		25
60 #define	dtabcol		32
61 
62 #define	spades		's'
63 #define	clubs		'c'
64 #define	hearts		'h'
65 #define	diamonds	'd'
66 #define	black		'b'
67 #define	red		'r'
68 
69 #define stk		1
70 #define	tal		2
71 #define tab		3
72 #define INCRHAND(row, col) {\
73 	row -= cheightcol;\
74 	if (row < ctoprow) {\
75 		row = cbotrow;\
76 		col += cwidthcol;\
77 	}\
78 }
79 #define DECRHAND(row, col) {\
80 	row += cheightcol;\
81 	if (row > cbotrow) {\
82 		row = ctoprow;\
83 		col -= cwidthcol;\
84 	}\
85 }
86 
87 
88 struct cardtype {
89 	char suit;
90 	char color;
91 	bool visible;
92 	int rank;
93 	struct cardtype *next;
94 };
95 
96 #define	NIL	((struct cardtype *) -1)
97 
98 struct cardtype *deck[decksize];
99 struct cardtype cards[decksize];
100 struct cardtype *bottom[4], *found[4], *tableau[4];
101 struct cardtype *talon, *hand, *stock, *basecard;
102 int length[4];
103 int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru;
104 char suitmap[4] = {spades, clubs, hearts, diamonds};
105 char colormap[4] = {black, black, red, red};
106 char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol};
107 char srcpile, destpile;
108 int mtforigin, tempbase;
109 int coldcol, cnewcol, coldrow, cnewrow;
110 bool errmsg, done;
111 bool mtfdone, Cflag = FALSE;
112 
113 
114 
115 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
116  * The following procedures print the board onto the screen using the
117  * addressible cursor. The end of these procedures will also be
118  * separated from the rest of the program.
119  */
120 /* procedure to set the move command box */
121 movebox()
122 {
123 	    move(tboxrow, boxcol);
124 	    printw("*--------------------------*");
125 	    move(tboxrow + 1, boxcol);
126 	    printw("|         MOVES            |");
127 	    move(tboxrow + 2, boxcol);
128 	    printw("|s# = stock to tableau     |");
129 	    move(tboxrow + 3, boxcol);
130 	    printw("|sf = stock to foundation  |");
131 	    move(tboxrow + 4, boxcol);
132 	    printw("|t# = talon to tableau     |");
133 	    move(tboxrow + 5, boxcol);
134 	    printw("|tf = talon to foundation  |");
135 	    move(tboxrow + 6, boxcol);
136 	    printw("|## = tableau to tableau   |");
137 	    move(tboxrow + 7, boxcol);
138 	    printw("|#f = tableau to foundation|");
139 	    move(tboxrow + 8, boxcol);
140 	    printw("|ht = hand to talon        |");
141 	    move(tboxrow + 9, boxcol);
142 	    printw("|c = toggle card counting  |");
143 	    move(tboxrow + 10, boxcol);
144 	    printw("|q = quit to end the game  |");
145 	    move(tboxrow + 11, boxcol);
146 	    printw("|==========================|");
147 	    move(moverow, boxcol);
148 	    printw("|                          |");
149 	    move(msgrow, boxcol);
150 	    printw("|                          |");
151 	    move(bboxrow, boxcol);
152 	    printw("|Replace the # with the    |");
153 	    move(bboxrow + 1, boxcol);
154 	    printw("|number of the tableau you |");
155 	    move(bboxrow + 2, boxcol);
156 	    printw("|want, 1, 2, 3, or 4.      |");
157 	    move(bboxrow + 3, boxcol);
158 	    printw("*--------------------------*");
159 	    refresh();
160 }
161 
162 /* procedure to put the board on the screen using addressable cursor */
163 makeboard()
164 {
165 	clear();
166 	refresh();
167 	move(titlerow, titlecol);
168 	printw("=-> CANFIELD <-=");
169 	move(fttlrow, fttlcol);
170 	printw("foundation");
171 	move(foundrow - 1, fttlcol);
172 	printw("=---=  =---=  =---=  =---=");
173 	move(foundrow, fttlcol);
174 	printw("|   |  |   |  |   |  |   |");
175 	move(foundrow + 1, fttlcol);
176 	printw("=---=  =---=  =---=  =---=");
177 	move(ottlrow, sidecol);
178 	printw("stock     tableau");
179 	move(stockrow - 1, sidecol);
180 	printw("=---=");
181 	move(stockrow, sidecol);
182 	printw("|   |");
183 	move(stockrow + 1, sidecol);
184 	printw("=---=");
185 	move(talonrow - 2, sidecol);
186 	printw("talon");
187 	move(talonrow - 1, sidecol);
188 	printw("=---=");
189 	move(talonrow, sidecol);
190 	printw("|   |");
191 	move(talonrow + 1, sidecol);
192 	printw("=---=");
193 	move(tabrow - 1, atabcol);
194 	printw("-1-    -2-    -3-    -4-");
195 	movebox();
196 }
197 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
198 
199 /* clean up the board for another game */
200 cleanupboard()
201 {
202 	int cnt, row, col;
203 	struct cardtype *ptr;
204 
205 	if (Cflag) {
206 		clearstat();
207 		for(ptr = stock, row = stockrow;
208 		    ptr != NIL;
209 		    ptr = ptr->next, row++) {
210 			move(row, sidecol);
211 			printw("     ");
212 		}
213 		move(row, sidecol);
214 		printw("     ");
215 		move(stockrow + 1, sidecol);
216 		printw("=---=");
217 		move(talonrow - 2, sidecol);
218 		printw("talon");
219 		move(talonrow - 1, sidecol);
220 		printw("=---=");
221 		move(talonrow + 1, sidecol);
222 		printw("=---=");
223 	}
224 	move(stockrow, sidecol);
225 	printw("|   |");
226 	move(talonrow, sidecol);
227 	printw("|   |");
228 	move(foundrow, fttlcol);
229 	printw("|   |  |   |  |   |  |   |");
230 	for (cnt = 0; cnt < 4; cnt++) {
231 		switch(cnt) {
232 		case 0:
233 			col = atabcol;
234 			break;
235 		case 1:
236 			col = btabcol;
237 			break;
238 		case 2:
239 			col = ctabcol;
240 			break;
241 		case 3:
242 			col = dtabcol;
243 			break;
244 		}
245 		for(ptr = tableau[cnt], row = tabrow;
246 		    ptr != NIL;
247 		    ptr = ptr->next, row++)
248 			removecard(col, row);
249 	}
250 }
251 
252 /* procedure to create a deck of cards */
253 initdeck(deck)
254 struct cardtype *deck[];
255 {
256 	int i;
257 	int scnt;
258 	char s;
259 	int r;
260 
261 	i = 0;
262 	for (scnt=0; scnt<4; scnt++) {
263 		s = suitmap[scnt];
264 		for (r=Ace; r<=King; r++) {
265 			deck[i] = &cards[i];
266 			cards[i].rank = r;
267 			cards[i].suit = s;
268 			cards[i].color = colormap[scnt];
269 			cards[i].next = NIL;
270 			i++;
271 		}
272 	}
273 }
274 
275 /* procedure to shuffle the deck */
276 shuffle(deck)
277 struct cardtype *deck[];
278 {
279 	int i,j;
280 	struct cardtype *temp;
281 
282 	for (i=0; i<decksize; i++)
283 		deck[i]->visible = FALSE;
284 	for (i = decksize-1; i>=0; i--) {
285 		j = rand() % decksize;
286 		if (i != j) {
287 			temp = deck[i];
288 			deck[i] = deck[j];
289 			deck[j] = temp;
290 		}
291 	}
292 }
293 
294 /* procedure to remove the card from the board */
295 removecard(a, b)
296 {
297 	move(b, a);
298 	printw("   ");
299 }
300 
301 /* procedure to print the cards on the board */
302 printrank(a, b, cp)
303 struct cardtype *cp;
304 {
305 	move(b, a);
306 	switch (cp->rank) {
307 		case 2: case 3: case 4: case 5: case 6: case 7:
308 		case 8: case 9: case 10:
309 			printw("%2d", cp->rank);
310 			break;
311 		case Ace:
312 			printw(" A");
313 			break;
314 		case Jack:
315 			printw(" J");
316 			break;
317 		case Queen:
318 			printw(" Q");
319 			break;
320 		case King:
321 			printw(" K");
322 	}
323 }
324 
325 printcard(a, b, cp)
326 int a,b;
327 struct cardtype *cp;
328 {
329 	if (cp == NIL)
330 		removecard(a, b);
331 	else if (cp->visible == FALSE) {
332 		move(b, a);
333 		printw(" ? ");
334 	} else {
335 		printrank(a, b, cp);
336 		addch(cp->suit);
337 	}
338 }
339 
340 /*
341  * procedure to move the top card from one location to the top
342  * of another location. The pointers always point to the top
343  * of the piles.
344  */
345 transit(source, dest)
346 struct cardtype **source, **dest;
347 {
348 	struct cardtype *temp;
349 
350 	temp = *source;
351 	*source = (*source)->next;
352 	temp->next = *dest;
353 	*dest = temp;
354 }
355 
356 /*
357  * Procedure to set the cards on the foundation base when available.
358  * Note that it is only called on a foundation pile at the beginning of
359  * the game, so the pile will have exactly one card in it.
360  */
361 
362 fndbase(cp, column, row)
363 struct cardtype **cp;
364 {
365 	bool nomore;
366 
367 	if (*cp != NIL)
368 		do {
369 			if ((*cp)->rank == basecard->rank) {
370 				base++;
371 				printcard(pilemap[base], foundrow, *cp);
372 				if (*cp == tableau[0])
373 					length[0] = length[0] - 1;
374 				if (*cp == tableau[1])
375 					length[1] = length[1] - 1;
376 				if (*cp == tableau[2])
377 					length[2] = length[2] - 1;
378 				if (*cp == tableau[3])
379 					length[3] = length[3] - 1;
380 				transit(cp, &found[base]);
381 				if (cp == &talon)
382 					usedtalon();
383 				if (cp == &stock)
384 					usedstock();
385 				if (*cp != NIL) {
386 					printcard(column, row, *cp);
387 					nomore = FALSE;
388 				} else {
389 					removecard(column, row);
390 					nomore = TRUE;
391 				}
392 				cardsoff++;
393 			} else
394 				nomore = TRUE;
395 	} while (nomore == FALSE);
396 }
397 
398 /* procedure to initialize the things necessary for the game */
399 initgame()
400 {
401 	register i;
402 
403 	for (i=0; i<18; i++)
404 		deck[i]->visible = TRUE;
405 	stockcnt = 13;
406 	stock = deck[12];
407 	for (i=12; i>=1; i--)
408 		deck[i]->next = deck[i - 1];
409 	deck[0]->next = NIL;
410 	found[0] = deck[13];
411 	deck[13]->next = NIL;
412 	for (i=1; i<4; i++)
413 		found[i] = NIL;
414 	basecard = found[0];
415 	for (i=14; i<18; i++) {
416 		tableau[i - 14] = deck[i];
417 		deck[i]->next = NIL;
418 	}
419 	for (i=0; i<4; i++) {
420 		bottom[i] = tableau[i];
421 		length[i] = tabrow;
422 	}
423 	hand = deck[18];
424 	for (i=18; i<decksize-1; i++)
425 		deck[i]->next = deck[i + 1];
426 	deck[decksize-1]->next = NIL;
427 	talon = NIL;
428 	base = 0;
429 	cinhand = 34;
430 	taloncnt = 0;
431 	timesthru = 0;
432 	cardsoff = 1;
433 	coldrow = ctoprow;
434 	coldcol = cinitcol;
435 	cnewrow = ctoprow;
436 	cnewcol = cinitcol + cwidthcol;
437 }
438 
439 /* procedure to print the beginning cards and to start each game */
440 startgame()
441 {
442 	register int j;
443 
444 	shuffle(deck);
445 	initgame();
446 	printcard(foundcol, foundrow, found[0]);
447 	printcard(stockcol, stockrow, stock);
448 	printcard(atabcol, tabrow, tableau[0]);
449 	printcard(btabcol, tabrow, tableau[1]);
450 	printcard(ctabcol, tabrow, tableau[2]);
451 	printcard(dtabcol, tabrow, tableau[3]);
452 	printcard(taloncol, talonrow, talon);
453 	move(foundrow - 2, basecol);
454 	printw("Base");
455 	move(foundrow - 1, basecol);
456 	printw("Rank");
457 	printrank(basecol, foundrow, found[0]);
458 	for (j=0; j<=3; j++)
459 		fndbase(&tableau[j], pilemap[j], tabrow);
460 	fndbase(&stock, stockcol, stockrow);
461 	showstat();	/* show card counting info to cheaters */
462 }
463 
464 
465 /* procedure to clear the message printed from an error */
466 clearmsg()
467 {
468 	int i;
469 
470 	if (errmsg == TRUE) {
471 		errmsg = FALSE;
472 		move(msgrow, msgcol);
473 		for (i=0; i<25; i++)
474 			addch(' ');
475 		refresh();
476 	}
477 }
478 
479 /* procedure to print an error message if the move is not listed */
480 dumberror()
481 {
482 	errmsg = TRUE;
483 	move(msgrow, msgcol);
484 	printw("Not a proper move       ");
485 }
486 
487 /* procedure to print an error message if the move is not possible */
488 destinerror()
489 {
490 	errmsg = TRUE;
491 	move(msgrow, msgcol);
492 	printw("Error: Can't move there");
493 }
494 
495 /* function to see if the source has cards in it */
496 bool
497 notempty(cp)
498 struct cardtype *cp;
499 {
500 	if (cp == NIL) {
501 		errmsg = TRUE;
502 		move(msgrow, msgcol);
503 		printw("Error: no cards to move");
504 		return (FALSE);
505 	} else
506 		return (TRUE);
507 }
508 
509 
510 /* function to see if the rank of one card is less than another */
511 
512 bool
513 ranklower(cp1, cp2)
514 struct cardtype *cp1, *cp2;
515 {
516 	if (cp2->rank == Ace)
517 		if (cp1->rank == King)
518 			return (TRUE);
519 		else
520 			return (FALSE);
521 	else if (cp1->rank + 1 == cp2->rank)
522 		return (TRUE);
523 	else
524 		return (FALSE);
525 }
526 
527 /* function to check the cardcolor for moving to a tableau */
528 bool
529 diffcolor(cp1, cp2)
530 struct cardtype *cp1, *cp2;
531 {
532 	if (cp1->color == cp2->color)
533 		return (FALSE);
534 	else
535 		return (TRUE);
536 }
537 
538 /* function to see if the card can move to the tableau */
539 bool
540 tabok(cp, des)
541 struct cardtype *cp;
542 {
543 	if ((cp == stock) && (tableau[des] == NIL))
544 		return (TRUE);
545 	else if (tableau[des] == NIL)
546 		if (stock == NIL)
547 			return (TRUE);
548 		else
549 			return (FALSE);
550 	else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des]))
551 		return (TRUE);
552 	else
553 		return (FALSE);
554 }
555 
556 
557 /* procedure to turn the cards onto the talon from the deck */
558 movetotalon()
559 {
560 	int i, fin;
561 
562 	if (cinhand >= 3)
563 		fin = 3;
564 	else if (cinhand > 0)
565 		fin = cinhand;
566 	else if (talon != NIL) {
567 		timesthru++;
568 		errmsg = TRUE;
569 		move(msgrow, msgcol);
570 		if (timesthru != 4) {
571 			printw("Talon is now the new hand");
572 			while (talon != NIL) {
573 				transit(&talon, &hand);
574 				cinhand++;
575 			}
576 			if (cinhand >= 3)
577 				fin = 3;
578 			else
579 				fin = cinhand;
580 			taloncnt = 0;
581 			coldrow = ctoprow;
582 			coldcol = cinitcol;
583 			cnewrow = ctoprow;
584 			cnewcol = cinitcol + cwidthcol;
585 			clearstat();
586 			showstat();
587 		} else {
588 			fin = 0;
589 			done = TRUE;
590 			printw("I believe you have lost");
591 			refresh();
592 			sleep(5);
593 		}
594 	} else {
595 		errmsg = TRUE;
596 		move(msgrow, msgcol);
597 		printw("Talon and hand are empty");
598 		fin = 0;
599 	}
600 	for (i=0; i<fin; i++) {
601 		transit(&hand, &talon);
602 		INCRHAND(cnewrow, cnewcol);
603 		INCRHAND(coldrow, coldcol);
604 		removecard(cnewcol, cnewrow);
605 		if (i == fin - 1)
606 			talon->visible = TRUE;
607 		if (Cflag)
608 			printcard(coldcol, coldrow, talon);
609 	}
610 	if (fin != 0) {
611 		printcard(taloncol, talonrow, talon);
612 		cinhand -= fin;
613 		taloncnt += fin;
614 		if (Cflag) {
615 			move(handstatrow, handstatcol);
616 			printw("%3d", cinhand);
617 			move(talonstatrow, talonstatcol);
618 			printw("%3d", taloncnt);
619 		}
620 		fndbase(&talon, taloncol, talonrow);
621 	}
622 }
623 
624 
625 /* procedure to print card counting info on screen */
626 showstat()
627 {
628 	int row, col;
629 	register struct cardtype *ptr;
630 
631 	if (Cflag) {
632 		move(talonstatrow, talonstatcol - 7);
633 		printw("Talon: %3d", taloncnt);
634 		move(handstatrow, handstatcol - 7);
635 		printw("Hand:  %3d", cinhand);
636 		move(stockstatrow, stockstatcol - 7);
637 		printw("Stock: %3d", stockcnt);
638 		for ( row = coldrow, col = coldcol, ptr = talon;
639 		      ptr != NIL;
640 		      ptr = ptr->next ) {
641 			printcard(col, row, ptr);
642 			DECRHAND(row, col);
643 		}
644 		for ( row = cnewrow, col = cnewcol, ptr = hand;
645 		      ptr != NIL;
646 		      ptr = ptr->next ) {
647 			INCRHAND(row, col);
648 			printcard(col, row, ptr);
649 		}
650 	}
651 }
652 
653 
654 /* procedure to clear card counting info from screen */
655 clearstat()
656 {
657 	int row;
658 
659 	move(talonstatrow, talonstatcol - 7);
660 	printw("          ");
661 	move(handstatrow, handstatcol - 7);
662 	printw("          ");
663 	move(stockstatrow, stockstatcol - 7);
664 	printw("          ");
665 	for ( row = ctoprow ; row <= cbotrow ; row++ ) {
666 		move(row, cinitcol);
667 		printw("%56s", " ");
668 	}
669 }
670 
671 
672 /* procedure to update card counting base */
673 usedtalon()
674 {
675 	removecard(coldcol, coldrow);
676 	DECRHAND(coldrow, coldcol);
677 	if (talon != NIL && (talon->visible == FALSE)) {
678 		talon->visible = TRUE;
679 		if (Cflag)
680 			printcard(coldcol, coldrow, talon);
681 	}
682 	taloncnt--;
683 	if (Cflag) {
684 		move(talonstatrow, talonstatcol);
685 		printw("%3d", taloncnt);
686 	}
687 }
688 
689 
690 /* procedure to update stock card counting base */
691 usedstock()
692 {
693 	stockcnt--;
694 	if (Cflag) {
695 		move(stockstatrow, stockstatcol);
696 		printw("%3d", stockcnt);
697 	}
698 }
699 
700 
701 /* let 'em know how they lost! */
702 showcards()
703 {
704 	register struct cardtype *ptr;
705 	int row;
706 
707 	if (!Cflag)
708 		return;
709 	for (ptr = talon; ptr != NIL; ptr = ptr->next)
710 		ptr->visible = TRUE;
711 	for (ptr = hand; ptr != NIL; ptr = ptr->next)
712 		ptr->visible = TRUE;
713 	showstat();
714 	move(stockrow + 1, sidecol);
715 	printw("     ");
716 	move(talonrow - 2, sidecol);
717 	printw("     ");
718 	move(talonrow - 1, sidecol);
719 	printw("     ");
720 	move(talonrow, sidecol);
721 	printw("     ");
722 	move(talonrow + 1, sidecol);
723 	printw("     ");
724 	for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) {
725 		move(row, stockcol - 1);
726 		printw("|   |");
727 		printcard(stockcol, row, ptr);
728 	}
729 	if (stock == NIL) {
730 		move(row, stockcol - 1);
731 		printw("|   |");
732 		row++;
733 	}
734 	move(handstatrow, handstatcol - 7);
735 	printw("          ");
736 	move(row, stockcol - 1);
737 	printw("=---=");
738 	getcmd(moverow, movecol, "Hit return to exit");
739 }
740 
741 
742 /* procedure to move a card from the stock or talon to the tableau */
743 simpletableau(cp, des)
744 struct cardtype **cp;
745 {
746 	int origin;
747 
748 	if (notempty(*cp)) {
749 		if (tabok(*cp, des)) {
750 			if (*cp == stock)
751 				origin = stk;
752 			else
753 				origin = tal;
754 			if (tableau[des] == NIL)
755 				bottom[des] = *cp;
756 			transit(cp, &tableau[des]);
757 			length[des]++;
758 			printcard(pilemap[des], length[des], tableau[des]);
759 			timesthru = 0;
760 			if (origin == stk) {
761 				usedstock();
762 				printcard(stockcol, stockrow, stock);
763 			} else {
764 				usedtalon();
765 				printcard(taloncol, talonrow, talon);
766 			}
767 		} else
768 			destinerror();
769 	}
770 }
771 
772 
773 tabprint(sour, des)
774 {
775 	int dlength, slength, i;
776 	struct cardtype *tempcard;
777 
778 	for (i=tabrow; i<=length[sour]; i++)
779 		removecard(pilemap[sour], i);
780 	dlength = length[des] + 1;
781 	slength = length[sour];
782 	if (slength == tabrow)
783 		printcard(pilemap[des], dlength, tableau[sour]);
784 	else
785 		while (slength != tabrow - 1) {
786 			tempcard = tableau[sour];
787 			for (i=1; i<=slength-tabrow; i++)
788 			    tempcard = tempcard->next;
789 			printcard(pilemap[des], dlength, tempcard);
790 			slength--;
791 			dlength++;
792 		}
793 }
794 
795 /* procedure to move from the tableau to the tableau */
796 tabtotab(sour, des)
797 {
798 	struct cardtype *temp;
799 
800 	if (notempty(tableau[sour])) {
801 		if (tabok(bottom[sour], des)) {
802 			tabprint(sour, des);
803 			temp = bottom[sour];
804 			bottom[sour] = NIL;
805 			temp->next = tableau[des];
806 			tableau[des] = tableau[sour];
807 			tableau[sour] = NIL;
808 			length[des] = length[des] + (length[sour] - (tabrow - 1));
809 			length[sour] = tabrow - 1;
810 			timesthru = 0;
811 		} else
812 			destinerror();
813 	}
814 }
815 
816 
817 /* functions to see if the card can go onto the foundation */
818 bool
819 rankhigher(cp, let)
820 struct cardtype *cp;
821 {
822 	if (found[let]->rank == King)
823 		if (cp->rank == Ace)
824 			return(TRUE);
825 		else
826 			return(FALSE);
827 	else if (cp->rank - 1 == found[let]->rank)
828 		return(TRUE);
829 	else
830 		return(FALSE);
831 }
832 
833 samesuit(cp, let)
834 struct cardtype *cp;
835 {
836 	if (cp->suit == found[let]->suit)
837 		return (TRUE);
838 	else
839 		return (FALSE);
840 }
841 
842 /* procedure to move a card to the correct foundation pile */
843 
844 movetofound(cp, source)
845 struct cardtype **cp;
846 {
847 	tempbase = 0;
848 	mtfdone = FALSE;
849 	if (notempty(*cp)) {
850 		do {
851 			if (found[tempbase] != NIL)
852 				if (rankhigher(*cp, tempbase)
853 				    && samesuit(*cp, tempbase)) {
854 					if (*cp == stock)
855 						mtforigin = stk;
856 					else if (*cp == talon)
857 						mtforigin = tal;
858 					else
859 						mtforigin = tab;
860 					transit(cp, &found[tempbase]);
861 					printcard(pilemap[tempbase],
862 						foundrow, found[tempbase]);
863 					timesthru = 0;
864 					if (mtforigin == stk) {
865 						usedstock();
866 						printcard(stockcol, stockrow, stock);
867 					} else if (mtforigin == tal) {
868 						usedtalon();
869 						printcard(taloncol, talonrow, talon);
870 					} else {
871 						removecard(pilemap[source], length[source]);
872 						length[source]--;
873 					}
874 					cardsoff++;
875 					mtfdone = TRUE;
876 				} else
877 					tempbase++;
878 			else
879 				tempbase++;
880 		} while ((tempbase != 4) && !mtfdone);
881 		if (!mtfdone)
882 			destinerror();
883 	}
884 }
885 
886 
887 /* procedure to get a command */
888 
889 getcmd(row, col, cp)
890 	int row, col;
891 	char *cp;
892 {
893 	char cmd[2], ch;
894 	int i;
895 
896 	i = 0;
897 	move(row, col);
898 	printw("%-24s", cp);
899 	col += 1 + strlen(cp);
900 	move(row, col);
901 	refresh();
902 	do {
903 		ch = getch() & 0177;
904 		if (ch >= 'A' && ch <= 'Z')
905 			ch += ('a' - 'A');
906 		if (ch == '\f') {
907 			wrefresh(curscr);
908 			refresh();
909 		} else if (i >= 2 && ch != _tty.sg_erase && ch != _tty.sg_kill) {
910 			if (ch != '\n' && ch != '\r' && ch != ' ')
911 				write(1, "\007", 1);
912 		} else if (ch == _tty.sg_erase && i > 0) {
913 			printw("\b \b");
914 			refresh();
915 			i--;
916 		} else if (ch == _tty.sg_kill && i > 0) {
917 			while (i > 0) {
918 				printw("\b \b");
919 				i--;
920 			}
921 			refresh();
922 		} else if (ch == '\032') {	/* Control-Z */
923 			suspend();
924 			move(row, col + i);
925 			refresh();
926 		} else if (isprint(ch)) {
927 			cmd[i++] = ch;
928 			addch(ch);
929 			refresh();
930 		}
931 	} while (ch != '\n' && ch != '\r' && ch != ' ');
932 	srcpile = cmd[0];
933 	destpile = cmd[1];
934 }
935 
936 /* Suspend the game (shell escape if no process control on system) */
937 
938 suspend()
939 {
940 #ifndef SIGTSTP
941 	char *sh;
942 #endif
943 
944 	move(21, 0);
945 	refresh();
946 	endwin();
947 	fflush(stdout);
948 #ifdef SIGTSTP
949 	kill(getpid(), SIGTSTP);
950 #else
951 	sh = getenv("SHELL");
952 	if (sh == NULL)
953 		sh = "/bin/sh";
954 	system(sh);
955 #endif
956 	raw();
957 	noecho();
958 }
959 
960 /* procedure to evaluate and make the specific moves */
961 
962 movecard()
963 {
964 	int source, dest;
965 
966 	done = FALSE;
967 	errmsg = FALSE;
968 	do {
969 		if (cardsoff == 52) {
970 			refresh();
971 			srcpile = 'q';
972 		} else
973 			getcmd(moverow, movecol, "Move:");
974 		clearmsg();
975 		if (srcpile >= '1' && srcpile <= '4')
976 			source = (int) (srcpile - '1');
977 		if (destpile >= '1' && destpile <= '4')
978 			dest = (int) (destpile - '1');
979 		switch (srcpile) {
980 			case 't':
981 				if (destpile == 'f' || destpile == 'F')
982 					movetofound(&talon, source);
983 				else if (destpile >= '1' && destpile <= '4')
984 					simpletableau(&talon, dest);
985 				else
986 					dumberror();
987 				break;
988 			case 's':
989 				if (destpile == 'f' || destpile == 'F')
990 					movetofound(&stock, source);
991 				else if (destpile >= '1' && destpile <= '4')
992 					simpletableau(&stock, dest);
993 				else dumberror();
994 				break;
995 			case 'h':
996 				if (destpile == 't' || destpile == 'T')
997 					movetotalon();
998 				else dumberror();
999 				break;
1000 			case 'q':
1001 				showcards();
1002 				done = TRUE;
1003 				break;
1004 			case 'c':
1005 				Cflag = !Cflag;
1006 				if (Cflag)
1007 					showstat();
1008 				else
1009 					clearstat();
1010 				break;
1011 			case '1': case '2': case '3': case '4':
1012 				if (destpile == 'f' || destpile == 'F')
1013 					movetofound(&tableau[source], source);
1014 				else if (destpile >= '1' && destpile <= '4')
1015 					tabtotab(source, dest);
1016 				else dumberror();
1017 				break;
1018 			default:
1019 				dumberror();
1020 		}
1021 		fndbase(&stock, stockcol, stockrow);
1022 		fndbase(&talon, taloncol, talonrow);
1023 	} while (!done);
1024 }
1025 
1026 /* procedure to printout instructions */
1027 instruct()
1028 {
1029 	move(originrow, origincol);
1030 	printw("This is the game of solitaire called Canfield.  Do\n");
1031 	printw("you want instructions for the game?");
1032 	do {
1033 		getcmd(originrow + 3, origincol, "y or n?");
1034 	} while (srcpile != 'y' && srcpile != 'n');
1035 	if (srcpile == 'y') {
1036 		clear();
1037 		refresh();
1038 		printw("Here are brief instuctions to the game of Canfield:\n");
1039 		printw("\n");
1040 		printw("     If you have never played solitaire before, it is recom-\n");
1041 		printw("mended  that  you  consult  a solitaire instruction book. In\n");
1042 		printw("Canfield, tableau cards may be built on each other  downward\n");
1043 		printw("in  alternate colors. An entire pile must be moved as a unit\n");
1044 		printw("in building. Top cards of the piles are available to be able\n");
1045 		printw("to be played on foundations, but never into empty spaces.\n");
1046 		printw("\n");
1047 		printw("     Spaces must be filled from the stock. The top  card  of\n");
1048 		printw("the  stock  also is available to be played on foundations or\n");
1049 		printw("built on tableau piles. After the stock  is  exhausted,  ta-\n");
1050 		printw("bleau spaces may be filled from the talon and the player may\n");
1051 		printw("keep them open until he wishes to use them.\n");
1052 		printw("\n");
1053 		printw("     Cards are dealt from the hand to the  talon  by  threes\n");
1054 		printw("and  this  repeats until there are no more cards in the hand\n");
1055 		printw("or the player quits. To have cards dealt onto the talon  the\n");
1056 		printw("player  types  'ht'  for his move. Foundation base cards are\n");
1057 		printw("also automatically moved to the foundation when they  become\n");
1058 		printw("available.\n\n");
1059 		printw("push any key when you are finished: ");
1060 		refresh();
1061 		getch();
1062 	}
1063 }
1064 
1065 /* procedure to initialize the game */
1066 initall()
1067 
1068 {
1069 	srand(getpid());
1070 	initdeck(deck);
1071 }
1072 
1073 /* procedure to end the game */
1074 bool
1075 finish()
1076 {
1077 	int row, col;
1078 
1079 	if (cardsoff == 52) {
1080 		clear();
1081 		refresh();
1082 		move(originrow, origincol);
1083 		printw("CONGRATULATIONS!\n");
1084 		printw("You won the game. That is a feat to be proud of.\n");
1085 		move(originrow + 4, origincol);
1086 		printw("Wish to play again?     ");
1087 		row = originrow + 5;
1088 		col = origincol;
1089 	} else {
1090 		move(msgrow, msgcol);
1091 		printw("You got %d card", cardsoff);
1092 		if (cardsoff > 1)
1093 			printw("s");
1094 		printw(" off    ");
1095 		getcmd(moverow, movecol, "Hit return to continue");
1096 		move(msgrow, msgcol);
1097 		printw("Wish to play again?     ");
1098 		row = moverow;
1099 		col = movecol;
1100 	}
1101 	do {
1102 		getcmd(row, col, "y or n?");
1103 	} while (srcpile != 'y' && srcpile != 'n');
1104 	errmsg = TRUE;
1105 	clearmsg();
1106 	if (srcpile == 'y')
1107 		return (FALSE);
1108 	else
1109 		return (TRUE);
1110 }
1111 
1112 main(argc, argv)
1113 	int argc;
1114 	char *argv[];
1115 {
1116 #ifdef MAXLOAD
1117 	double vec[3];
1118 
1119 	loadav(vec);
1120 	if (vec[2] >= MAXLOAD) {
1121 		puts("The system load is too high.  Try again later.");
1122 		exit(0);
1123 	}
1124 #endif
1125 	initscr();
1126 	raw();
1127 	noecho();
1128 	initall();
1129 	instruct();
1130 	makeboard();
1131 	for (;;) {
1132 		startgame();
1133 		movecard();
1134 		if (finish())
1135 			break;
1136 		if (cardsoff == 52)
1137 			makeboard();
1138 		else
1139 			cleanupboard();
1140 	}
1141 	clear();
1142 	move(22,0);
1143 	refresh();
1144 	endwin();
1145 }
1146