/* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)canfield.c 4.1 10/24/82";

/*
 * The canfield program
 *
 * Authors:
 *	Originally written: Steve Levine
 *	Converted to use curses and debugged: Steve Feldman
 *	Card counting: Kirk McKusick and Mikey Olson
 */

#include <curses.h>
#include <ctype.h>
#include <signal.h>

#define	decksize	52
#define originrow	0
#define origincol	0
#define	basecol		1
#define	boxcol		42
#define	tboxrow		2
#define	bboxrow		16
#define	movecol		43
#define	moverow		15
#define	msgcol		43
#define	msgrow		14
#define	titlecol	30
#define	titlerow	0
#define	sidecol		1
#define	ottlrow		6
#define	foundcol	11
#define	foundrow	3
#define	stockcol	2
#define	stockrow	8
#define	fttlcol		10
#define	fttlrow		1
#define	taloncol	2
#define	talonrow	13
#define	tabrow		8
#define ctoprow		21
#define cbotrow		23
#define cinitcol	14
#define cheightcol	1
#define cwidthcol	4
#define handstatrow	21
#define handstatcol	7
#define talonstatrow	22
#define talonstatcol	7
#define stockstatrow	23
#define stockstatcol	7
#define	Ace		1
#define	Jack		11
#define	Queen		12
#define	King		13
#define	atabcol		11
#define	btabcol		18
#define	ctabcol		25
#define	dtabcol		32

#define	spades		's'
#define	clubs		'c'
#define	hearts		'h'
#define	diamonds	'd'
#define	black		'b'
#define	red		'r'

#define stk		1
#define	tal		2
#define tab		3
#define INCRHAND(row, col) {\
	row -= cheightcol;\
	if (row < ctoprow) {\
		row = cbotrow;\
		col += cwidthcol;\
	}\
}
#define DECRHAND(row, col) {\
	row += cheightcol;\
	if (row > cbotrow) {\
		row = ctoprow;\
		col -= cwidthcol;\
	}\
}


struct cardtype {
	char suit;
	char color;
	bool visible;
	int rank;
	struct cardtype *next;
};

#define	NIL	((struct cardtype *) -1)

struct cardtype *deck[decksize];
struct cardtype cards[decksize];
struct cardtype *bottom[4], *found[4], *tableau[4];
struct cardtype *talon, *hand, *stock, *basecard;
int length[4];
int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru;
char suitmap[4] = {spades, clubs, hearts, diamonds};
char colormap[4] = {black, black, red, red};
char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol};
char srcpile, destpile;
int mtforigin, tempbase;
int coldcol, cnewcol, coldrow, cnewrow;
bool errmsg, done;
bool mtfdone, Cflag = FALSE;



/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * The following procedures print the board onto the screen using the
 * addressible cursor. The end of these procedures will also be
 * separated from the rest of the program.
 */
/* procedure to set the move command box */
movebox()
{
	    move(tboxrow, boxcol);
	    printw("*--------------------------*");
	    move(tboxrow + 1, boxcol);
	    printw("|         MOVES            |");
	    move(tboxrow + 2, boxcol);
	    printw("|s# = stock to tableau     |");
	    move(tboxrow + 3, boxcol);
	    printw("|sf = stock to foundation  |");
	    move(tboxrow + 4, boxcol);
	    printw("|t# = talon to tableau     |");
	    move(tboxrow + 5, boxcol);
	    printw("|tf = talon to foundation  |");
	    move(tboxrow + 6, boxcol);
	    printw("|## = tableau to tableau   |");
	    move(tboxrow + 7, boxcol);
	    printw("|#f = tableau to foundation|");
	    move(tboxrow + 8, boxcol);
	    printw("|ht = hand to talon        |");
	    move(tboxrow + 9, boxcol);
	    printw("|c = toggle card counting  |");
	    move(tboxrow + 10, boxcol);
	    printw("|q = quit to end the game  |");
	    move(tboxrow + 11, boxcol);
	    printw("|==========================|");
	    move(moverow, boxcol);
	    printw("|                          |");
	    move(msgrow, boxcol);
	    printw("|                          |");
	    move(bboxrow, boxcol);
	    printw("|Replace the # with the    |");
	    move(bboxrow + 1, boxcol);
	    printw("|number of the tableau you |");
	    move(bboxrow + 2, boxcol);
	    printw("|want, 1, 2, 3, or 4.      |");
	    move(bboxrow + 3, boxcol);
	    printw("*--------------------------*");
	    refresh();
}

/* procedure to put the board on the screen using addressable cursor */
makeboard()
{
	clear();
	refresh();
	move(titlerow, titlecol);
	printw("=-> CANFIELD <-=");
	move(fttlrow, fttlcol);
	printw("foundation");
	move(foundrow - 1, fttlcol);
	printw("=---=  =---=  =---=  =---=");
	move(foundrow, fttlcol);
	printw("|   |  |   |  |   |  |   |");
	move(foundrow + 1, fttlcol);
	printw("=---=  =---=  =---=  =---=");
	move(ottlrow, sidecol);
	printw("stock     tableau");
	move(stockrow - 1, sidecol);
	printw("=---=");
	move(stockrow, sidecol);
	printw("|   |");
	move(stockrow + 1, sidecol);
	printw("=---=");
	move(talonrow - 2, sidecol);
	printw("talon");
	move(talonrow - 1, sidecol);
	printw("=---=");
	move(talonrow, sidecol);
	printw("|   |");
	move(talonrow + 1, sidecol);
	printw("=---=");
	move(tabrow - 1, atabcol);
	printw("-1-    -2-    -3-    -4-");
	movebox();
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

/* clean up the board for another game */
cleanupboard()
{
	int cnt, row, col;
	struct cardtype *ptr;

	if (Cflag) {
		clearstat();
		for(ptr = stock, row = stockrow;
		    ptr != NIL;
		    ptr = ptr->next, row++) {
			move(row, sidecol);
			printw("     ");
		}
		move(row, sidecol);
		printw("     ");
		move(stockrow + 1, sidecol);
		printw("=---=");
		move(talonrow - 2, sidecol);
		printw("talon");
		move(talonrow - 1, sidecol);
		printw("=---=");
		move(talonrow + 1, sidecol);
		printw("=---=");
	}
	move(stockrow, sidecol);
	printw("|   |");
	move(talonrow, sidecol);
	printw("|   |");
	move(foundrow, fttlcol);
	printw("|   |  |   |  |   |  |   |");
	for (cnt = 0; cnt < 4; cnt++) {
		switch(cnt) {
		case 0:
			col = atabcol;
			break;
		case 1:
			col = btabcol;
			break;
		case 2:
			col = ctabcol;
			break;
		case 3:
			col = dtabcol;
			break;
		}
		for(ptr = tableau[cnt], row = tabrow;
		    ptr != NIL;
		    ptr = ptr->next, row++)
			removecard(col, row);
	}
}

/* procedure to create a deck of cards */
initdeck(deck)
struct cardtype *deck[];
{
	int i;
	int scnt;
	char s;
	int r;

	i = 0;
	for (scnt=0; scnt<4; scnt++) {
		s = suitmap[scnt];
		for (r=Ace; r<=King; r++) {
			deck[i] = &cards[i];
			cards[i].rank = r;
			cards[i].suit = s;
			cards[i].color = colormap[scnt];
			cards[i].next = NIL;
			i++;
		}
	}
}

/* procedure to shuffle the deck */
shuffle(deck)
struct cardtype *deck[];
{
	int i,j;
	struct cardtype *temp;

	for (i=0; i<decksize; i++)
		deck[i]->visible = FALSE;
	for (i = decksize-1; i>=0; i--) {
		j = rand() % decksize;
		if (i != j) {
			temp = deck[i];
			deck[i] = deck[j];
			deck[j] = temp;
		}
	}
}

/* procedure to remove the card from the board */
removecard(a, b)
{
	move(b, a);
	printw("   ");
}

/* procedure to print the cards on the board */
printrank(a, b, cp)
struct cardtype *cp;
{
	move(b, a);
	switch (cp->rank) {
		case 2: case 3: case 4: case 5: case 6: case 7:
		case 8: case 9: case 10:
			printw("%2d", cp->rank);
			break;
		case Ace:
			printw(" A");
			break;
		case Jack:
			printw(" J");
			break;
		case Queen:
			printw(" Q");
			break;
		case King:
			printw(" K");
	}
}

printcard(a, b, cp)
int a,b;
struct cardtype *cp;
{
	if (cp == NIL)
		removecard(a, b);
	else if (cp->visible == FALSE) {
		move(b, a);
		printw(" ? ");
	} else {
		printrank(a, b, cp);
		addch(cp->suit);
	}
}

/*
 * procedure to move the top card from one location to the top 
 * of another location. The pointers always point to the top
 * of the piles.
 */
transit(source, dest)
struct cardtype **source, **dest;
{
	struct cardtype *temp;
	
	temp = *source;
	*source = (*source)->next;
	temp->next = *dest;
	*dest = temp;
}

/*
 * Procedure to set the cards on the foundation base when available.
 * Note that it is only called on a foundation pile at the beginning of
 * the game, so the pile will have exactly one card in it.
 */

fndbase(cp, column, row)
struct cardtype **cp;
{
	bool nomore;

	if (*cp != NIL)
		do {
			if ((*cp)->rank == basecard->rank) {
				base++;
				printcard(pilemap[base], foundrow, *cp);
				if (*cp == tableau[0])
					length[0] = length[0] - 1;
				if (*cp == tableau[1])
					length[1] = length[1] - 1;
				if (*cp == tableau[2])
					length[2] = length[2] - 1;
				if (*cp == tableau[3])
					length[3] = length[3] - 1;
				transit(cp, &found[base]);
				if (cp == &talon)
					usedtalon();
				if (cp == &stock)
					usedstock();
				if (*cp != NIL) {
					printcard(column, row, *cp);
					nomore = FALSE;
				} else {
					removecard(column, row);
					nomore = TRUE;
				}
				cardsoff++;
			} else 
				nomore = TRUE;
	} while (nomore == FALSE);
}

/* procedure to initialize the things necessary for the game */
initgame()
{
	register i;

	for (i=0; i<18; i++)
		deck[i]->visible = TRUE;
	stockcnt = 13;
	stock = deck[12];
	for (i=12; i>=1; i--)
		deck[i]->next = deck[i - 1];
	deck[0]->next = NIL;
	found[0] = deck[13];
	deck[13]->next = NIL;
	for (i=1; i<4; i++)
		found[i] = NIL;
	basecard = found[0];
	for (i=14; i<18; i++) {
		tableau[i - 14] = deck[i];
		deck[i]->next = NIL;
	}
	for (i=0; i<4; i++) {
		bottom[i] = tableau[i];
		length[i] = tabrow;
	}
	hand = deck[18];
	for (i=18; i<decksize-1; i++)
		deck[i]->next = deck[i + 1];
	deck[decksize-1]->next = NIL;
	talon = NIL;
	base = 0;
	cinhand = 34;
	taloncnt = 0;
	timesthru = 0;
	cardsoff = 1;
	coldrow = ctoprow;
	coldcol = cinitcol;
	cnewrow = ctoprow;
	cnewcol = cinitcol + cwidthcol;
}

/* procedure to print the beginning cards and to start each game */
startgame()
{
	register int j;

	shuffle(deck);
	initgame();
	printcard(foundcol, foundrow, found[0]);
	printcard(stockcol, stockrow, stock);
	printcard(atabcol, tabrow, tableau[0]);
	printcard(btabcol, tabrow, tableau[1]);
	printcard(ctabcol, tabrow, tableau[2]);
	printcard(dtabcol, tabrow, tableau[3]);
	printcard(taloncol, talonrow, talon);
	move(foundrow - 2, basecol);
	printw("Base");
	move(foundrow - 1, basecol);
	printw("Rank");
	printrank(basecol, foundrow, found[0]);
	for (j=0; j<=3; j++)
		fndbase(&tableau[j], pilemap[j], tabrow);
	fndbase(&stock, stockcol, stockrow);
	showstat();	/* show card counting info to cheaters */
}


/* procedure to clear the message printed from an error */
clearmsg()
{
	int i;

	if (errmsg == TRUE) {
		errmsg = FALSE;
		move(msgrow, msgcol);
		for (i=0; i<25; i++)
			addch(' ');
		refresh();
	}
}

/* procedure to print an error message if the move is not listed */
dumberror()
{
	errmsg = TRUE;
	move(msgrow, msgcol);
	printw("Not a proper move       ");
}

/* procedure to print an error message if the move is not possible */
destinerror()
{
	errmsg = TRUE;
	move(msgrow, msgcol);
	printw("Error: Can't move there");
}

/* function to see if the source has cards in it */
bool
notempty(cp)
struct cardtype *cp;
{
	if (cp == NIL) {
		errmsg = TRUE;
		move(msgrow, msgcol);
		printw("Error: no cards to move");
		return (FALSE);
	} else 
		return (TRUE);
}


/* function to see if the rank of one card is less than another */

bool
ranklower(cp1, cp2)
struct cardtype *cp1, *cp2;
{
	if (cp2->rank == Ace) 
		if (cp1->rank == King)
			return (TRUE);
		else 
			return (FALSE);
	else if (cp1->rank + 1 == cp2->rank)
		return (TRUE);
	else 
		return (FALSE);
}

/* function to check the cardcolor for moving to a tableau */
bool
diffcolor(cp1, cp2)
struct cardtype *cp1, *cp2;
{
	if (cp1->color == cp2->color)
		return (FALSE);
	else 
		return (TRUE);
}

/* function to see if the card can move to the tableau */
bool
tabok(cp, des)
struct cardtype *cp;
{
	if ((cp == stock) && (tableau[des] == NIL))
		return (TRUE);
	else if (tableau[des] == NIL)
		if (stock == NIL)
			return (TRUE);
		else 
			return (FALSE);
	else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des]))
		return (TRUE);
	else 
		return (FALSE);
}


/* procedure to turn the cards onto the talon from the deck */
movetotalon()
{
	int i, fin;

	if (cinhand >= 3)
		fin = 3;
	else if (cinhand > 0)
		fin = cinhand;
	else if (talon != NIL) {
		timesthru++;
		errmsg = TRUE;
		move(msgrow, msgcol);
		if (timesthru != 4) {
			printw("Talon is now the new hand");
			while (talon != NIL) {
				transit(&talon, &hand);
				cinhand++;
			}
			if (cinhand >= 3)
				fin = 3;
			else
				fin = cinhand;
			taloncnt = 0;
			coldrow = ctoprow;
			coldcol = cinitcol;
			cnewrow = ctoprow;
			cnewcol = cinitcol + cwidthcol;
			clearstat();
			showstat();
		} else {
			fin = 0;
			done = TRUE;
			printw("I believe you have lost");
			refresh();
			sleep(5);
		}
	} else {
		errmsg = TRUE;
		move(msgrow, msgcol);
		printw("Talon and hand are empty");
		fin = 0;
	}
	for (i=0; i<fin; i++) {
		transit(&hand, &talon);
		INCRHAND(cnewrow, cnewcol);
		INCRHAND(coldrow, coldcol);
		removecard(cnewcol, cnewrow);
		if (i == fin - 1)
			talon->visible = TRUE;
		if (Cflag)
			printcard(coldcol, coldrow, talon);
	}
	if (fin != 0) {
		printcard(taloncol, talonrow, talon);
		cinhand -= fin;
		taloncnt += fin;
		if (Cflag) {
			move(handstatrow, handstatcol);
			printw("%3d", cinhand);
			move(talonstatrow, talonstatcol);
			printw("%3d", taloncnt);
		}
		fndbase(&talon, taloncol, talonrow);
	}
}


/* procedure to print card counting info on screen */
showstat()
{
	int row, col;
	register struct cardtype *ptr;

	if (Cflag) {
		move(talonstatrow, talonstatcol - 7);
		printw("Talon: %3d", taloncnt);
		move(handstatrow, handstatcol - 7);
		printw("Hand:  %3d", cinhand);
		move(stockstatrow, stockstatcol - 7);
		printw("Stock: %3d", stockcnt);
		for ( row = coldrow, col = coldcol, ptr = talon;
		      ptr != NIL;
		      ptr = ptr->next ) {
			printcard(col, row, ptr);
			DECRHAND(row, col);
		}
		for ( row = cnewrow, col = cnewcol, ptr = hand;
		      ptr != NIL;
		      ptr = ptr->next ) {
			INCRHAND(row, col);
			printcard(col, row, ptr);
		}
	}
}


/* procedure to clear card counting info from screen */
clearstat()
{
	int row;

	move(talonstatrow, talonstatcol - 7);
	printw("          ");
	move(handstatrow, handstatcol - 7);
	printw("          ");
	move(stockstatrow, stockstatcol - 7);
	printw("          ");
	for ( row = ctoprow ; row <= cbotrow ; row++ ) {
		move(row, cinitcol);
		printw("%56s", " ");
	}
}


/* procedure to update card counting base */
usedtalon()
{
	removecard(coldcol, coldrow);
	DECRHAND(coldrow, coldcol);
	if (talon != NIL && (talon->visible == FALSE)) {
		talon->visible = TRUE;
		if (Cflag)
			printcard(coldcol, coldrow, talon);
	}
	taloncnt--;
	if (Cflag) {
		move(talonstatrow, talonstatcol);
		printw("%3d", taloncnt);
	}
}


/* procedure to update stock card counting base */
usedstock()
{
	stockcnt--;
	if (Cflag) {
		move(stockstatrow, stockstatcol);
		printw("%3d", stockcnt);
	}
}


/* let 'em know how they lost! */
showcards()
{
	register struct cardtype *ptr;
	int row;

	if (!Cflag)
		return;
	for (ptr = talon; ptr != NIL; ptr = ptr->next)
		ptr->visible = TRUE;
	for (ptr = hand; ptr != NIL; ptr = ptr->next)
		ptr->visible = TRUE;
	showstat();
	move(stockrow + 1, sidecol);
	printw("     ");
	move(talonrow - 2, sidecol);
	printw("     ");
	move(talonrow - 1, sidecol);
	printw("     ");
	move(talonrow, sidecol);
	printw("     ");
	move(talonrow + 1, sidecol);
	printw("     ");
	for (ptr = stock, row = stockrow; ptr != NIL; ptr = ptr->next, row++) {
		move(row, stockcol - 1);
		printw("|   |");
		printcard(stockcol, row, ptr);
	}
	if (stock == NIL) {
		move(row, stockcol - 1);
		printw("|   |");
		row++;
	}
	move(handstatrow, handstatcol - 7);
	printw("          ");
	move(row, stockcol - 1);
	printw("=---=");
	getcmd(moverow, movecol, "Hit return to exit");
}


/* procedure to move a card from the stock or talon to the tableau */
simpletableau(cp, des)
struct cardtype **cp;
{
	int origin;

	if (notempty(*cp)) {
		if (tabok(*cp, des)) {
			if (*cp == stock)
				origin = stk;
			else
				origin = tal;
			if (tableau[des] == NIL)
				bottom[des] = *cp;
			transit(cp, &tableau[des]);
			length[des]++;
			printcard(pilemap[des], length[des], tableau[des]);
			timesthru = 0;
			if (origin == stk) {
				usedstock();
				printcard(stockcol, stockrow, stock);
			} else {
				usedtalon();
				printcard(taloncol, talonrow, talon);
			}
		} else 
			destinerror();
	}
}


tabprint(sour, des)
{
	int dlength, slength, i;
	struct cardtype *tempcard;

	for (i=tabrow; i<=length[sour]; i++)
		removecard(pilemap[sour], i);
	dlength = length[des] + 1;
	slength = length[sour];
	if (slength == tabrow)
		printcard(pilemap[des], dlength, tableau[sour]);
	else 
		while (slength != tabrow - 1) {
			tempcard = tableau[sour];
			for (i=1; i<=slength-tabrow; i++)
			    tempcard = tempcard->next;
			printcard(pilemap[des], dlength, tempcard);
			slength--;
			dlength++;
		}
}

/* procedure to move from the tableau to the tableau */
tabtotab(sour, des)
{
	struct cardtype *temp;

	if (notempty(tableau[sour])) {
		if (tabok(bottom[sour], des)) {
			tabprint(sour, des);
			temp = bottom[sour];
			bottom[sour] = NIL;
			temp->next = tableau[des];
			tableau[des] = tableau[sour];
			tableau[sour] = NIL;
			length[des] = length[des] + (length[sour] - (tabrow - 1));
			length[sour] = tabrow - 1;
			timesthru = 0;
		} else 
			destinerror();
	}
}


/* functions to see if the card can go onto the foundation */
bool
rankhigher(cp, let)
struct cardtype *cp;
{
	if (found[let]->rank == King)
		if (cp->rank == Ace)
			return(TRUE);
		else 
			return(FALSE);
	else if (cp->rank - 1 == found[let]->rank)
		return(TRUE);
	else 
		return(FALSE);
}

samesuit(cp, let)
struct cardtype *cp;
{
	if (cp->suit == found[let]->suit)
		return (TRUE);
	else 
		return (FALSE);
}

/* procedure to move a card to the correct foundation pile */

movetofound(cp, source)
struct cardtype **cp;
{
	tempbase = 0;
	mtfdone = FALSE;
	if (notempty(*cp)) {
		do {
			if (found[tempbase] != NIL)
				if (rankhigher(*cp, tempbase)
				    && samesuit(*cp, tempbase)) {
					if (*cp == stock)
						mtforigin = stk;
					else if (*cp == talon)
						mtforigin = tal;
					else
						mtforigin = tab;
					transit(cp, &found[tempbase]);
					printcard(pilemap[tempbase],
						foundrow, found[tempbase]);
					timesthru = 0;
					if (mtforigin == stk) {
						usedstock();
						printcard(stockcol, stockrow, stock);
					} else if (mtforigin == tal) {
						usedtalon();
						printcard(taloncol, talonrow, talon);
					} else {
						removecard(pilemap[source], length[source]);
						length[source]--;
					}
					cardsoff++;
					mtfdone = TRUE;
				} else
					tempbase++;
			else 
				tempbase++;
		} while ((tempbase != 4) && !mtfdone);
		if (!mtfdone)
			destinerror();
	}
}


/* procedure to get a command */

getcmd(row, col, cp)
	int row, col;
	char *cp;
{
	char cmd[2], ch;
	int i;

	i = 0;
	move(row, col);
	printw("%-24s", cp);
	col += 1 + strlen(cp);
	move(row, col);
	refresh();
	do {
		ch = getch() & 0177;
		if (ch >= 'A' && ch <= 'Z')
			ch += ('a' - 'A');
		if (ch == '\f') {
			wrefresh(curscr);
			refresh();
		} else if (i >= 2 && ch != _tty.sg_erase && ch != _tty.sg_kill) {
			if (ch != '\n' && ch != '\r' && ch != ' ')
				write(1, "\007", 1);
		} else if (ch == _tty.sg_erase && i > 0) {
			printw("\b \b");
			refresh();
			i--;
		} else if (ch == _tty.sg_kill && i > 0) {
			while (i > 0) {
				printw("\b \b");
				i--;
			}
			refresh();
		} else if (ch == '\032') {	/* Control-Z */
			suspend();
			move(row, col + i);
			refresh();
		} else if (isprint(ch)) {
			cmd[i++] = ch;
			addch(ch);
			refresh();
		}
	} while (ch != '\n' && ch != '\r' && ch != ' ');
	srcpile = cmd[0];
	destpile = cmd[1];
}

/* Suspend the game (shell escape if no process control on system) */

suspend()
{
#ifndef SIGTSTP
	char *sh;
#endif

	move(21, 0);
	refresh();
	endwin();
	fflush(stdout);
#ifdef SIGTSTP
	kill(getpid(), SIGTSTP);
#else
	sh = getenv("SHELL");
	if (sh == NULL)
		sh = "/bin/sh";
	system(sh);
#endif
	raw();
	noecho();
}

/* procedure to evaluate and make the specific moves */

movecard()
{
	int source, dest;

	done = FALSE;
	errmsg = FALSE;
	do {
		if (cardsoff == 52) {
			refresh();
			srcpile = 'q';
		} else
			getcmd(moverow, movecol, "Move:");
		clearmsg();
		if (srcpile >= '1' && srcpile <= '4')
			source = (int) (srcpile - '1');
		if (destpile >= '1' && destpile <= '4')
			dest = (int) (destpile - '1');
		switch (srcpile) {
			case 't':
				if (destpile == 'f' || destpile == 'F')
					movetofound(&talon, source);
				else if (destpile >= '1' && destpile <= '4')
					simpletableau(&talon, dest);
				else
					dumberror();
				break;
			case 's':
				if (destpile == 'f' || destpile == 'F')
					movetofound(&stock, source);
				else if (destpile >= '1' && destpile <= '4')
					simpletableau(&stock, dest);
				else dumberror();
				break;
			case 'h':
				if (destpile == 't' || destpile == 'T')
					movetotalon();
				else dumberror();
				break;
			case 'q':
				showcards();
				done = TRUE;
				break;
			case 'c':
				Cflag = !Cflag;
				if (Cflag)
					showstat();
				else
					clearstat();
				break;
			case '1': case '2': case '3': case '4':
				if (destpile == 'f' || destpile == 'F')
					movetofound(&tableau[source], source);
				else if (destpile >= '1' && destpile <= '4')
					tabtotab(source, dest);
				else dumberror();
				break;
			default:
				dumberror();
		}
		fndbase(&stock, stockcol, stockrow);
		fndbase(&talon, taloncol, talonrow);
	} while (!done);
}

/* procedure to printout instructions */
instruct()
{
	move(originrow, origincol);
	printw("This is the game of solitaire called Canfield.  Do\n");
	printw("you want instructions for the game?");
	do {
		getcmd(originrow + 3, origincol, "y or n?");
	} while (srcpile != 'y' && srcpile != 'n');
	if (srcpile == 'y') {
		clear();
		refresh();
		printw("Here are brief instuctions to the game of Canfield:\n");
		printw("\n");
		printw("     If you have never played solitaire before, it is recom-\n");
		printw("mended  that  you  consult  a solitaire instruction book. In\n");
		printw("Canfield, tableau cards may be built on each other  downward\n");
		printw("in  alternate colors. An entire pile must be moved as a unit\n");
		printw("in building. Top cards of the piles are available to be able\n");
		printw("to be played on foundations, but never into empty spaces.\n");
		printw("\n");
		printw("     Spaces must be filled from the stock. The top  card  of\n");
		printw("the  stock  also is available to be played on foundations or\n");
		printw("built on tableau piles. After the stock  is  exhausted,  ta-\n");
		printw("bleau spaces may be filled from the talon and the player may\n");
		printw("keep them open until he wishes to use them.\n");
		printw("\n");
		printw("     Cards are dealt from the hand to the  talon  by  threes\n");
		printw("and  this  repeats until there are no more cards in the hand\n");
		printw("or the player quits. To have cards dealt onto the talon  the\n");
		printw("player  types  'ht'  for his move. Foundation base cards are\n");
		printw("also automatically moved to the foundation when they  become\n");
		printw("available.\n\n");
		printw("push any key when you are finished: ");
		refresh();
		getch();
	}
}

/* procedure to initialize the game */
initall()

{
	srand(getpid());
	initdeck(deck);
}

/* procedure to end the game */
bool
finish()
{
	int row, col;

	if (cardsoff == 52) {
		clear();
		refresh();
		move(originrow, origincol);
		printw("CONGRATULATIONS!\n");
		printw("You won the game. That is a feat to be proud of.\n");
		move(originrow + 4, origincol);
		printw("Wish to play again?     ");
		row = originrow + 5;
		col = origincol;
	} else {
		move(msgrow, msgcol);
		printw("You got %d card", cardsoff);
		if (cardsoff > 1)
			printw("s");
		printw(" off    ");
		getcmd(moverow, movecol, "Hit return to continue");
		move(msgrow, msgcol);
		printw("Wish to play again?     ");
		row = moverow;
		col = movecol;
	}
	do {
		getcmd(row, col, "y or n?");
	} while (srcpile != 'y' && srcpile != 'n');
	errmsg = TRUE;
	clearmsg();
	if (srcpile == 'y')
		return (FALSE);
	else
		return (TRUE);
}

main(argc, argv)
	int argc;
	char *argv[];
{
#ifdef MAXLOAD
	double vec[3];

	loadav(vec);
	if (vec[2] >= MAXLOAD) {
		puts("The system load is too high.  Try again later.");
		exit(0);
	}
#endif
	initscr();
	raw();
	noecho();
	initall();
	instruct();
	makeboard();
	for (;;) {
		startgame();
		movecard();
		if (finish())
			break;
		if (cardsoff == 52)
			makeboard();
		else
			cleanupboard();
	}
	clear();
	move(22,0);
	refresh();
	endwin();
}
