#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1980, 1993\
The Regents of the University of California. All rights reserved.");
#endif
#ifndef lint
#if 0
static char sccsid[] = "@(#)canfield.c 8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: canfield.c,v 1.31 2021/05/02 12:50:44 rillig Exp $");
#endif
#endif
#include <sys/types.h>
#include <ctype.h>
#include <curses.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include "betinfo.h"
#include "pathnames.h"
#define decksize 52
#define originrow 0
#define origincol 0
#define basecol 1
#define boxcol 42
#define tboxrow 2
#define bboxrow 17
#define movecol 43
#define moverow 16
#define msgcol 43
#define msgrow 15
#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;
bool paid;
int rank;
struct cardtype *next;
};
#define NIL ((struct cardtype *) -1)
static struct cardtype *deck[decksize];
static struct cardtype cards[decksize];
static struct cardtype *bottom[4], *found[4], *tableau[4];
static struct cardtype *talon, *hand, *stock, *basecard;
static int length[4];
static int cardsoff, base, cinhand, taloncnt, stockcnt, timesthru;
static char suitmap[4] = {spades, clubs, hearts, diamonds};
static char colormap[4] = {black, black, red, red};
static char pilemap[4] = {atabcol, btabcol, ctabcol, dtabcol};
static char srcpile, destpile;
static int mtforigin, tempbase;
static int coldcol, cnewcol, coldrow, cnewrow;
static bool errmsg, done;
static bool mtfdone, Cflag = FALSE;
#define INSTRUCTIONBOX 1
#define BETTINGBOX 2
#define NOBOX 3
static int status = INSTRUCTIONBOX;
static uid_t uid;
#define costofhand 13
#define costofinspection 13
#define costofgame 26
#define costofrunthroughhand 5
#define costofinformation 1
#define secondsperdollar 60
#define maxtimecharge 3
#define valuepercardup 5
static struct betinfo this, game, total;
static bool startedgame = FALSE, infullgame = FALSE;
static time_t acctstart;
static int dbfd = -1;
static void askquit(int);
static void cleanup(int) __dead;
static void cleanupboard(void);
static void clearabovemovebox(void);
static void clearbelowmovebox(void);
static void clearmsg(void);
static void clearstat(void);
static void destinerror(void);
static bool diffcolor(const struct cardtype *, const struct cardtype *);
static void dumberror(void);
static bool finish(void);
static void fndbase(struct cardtype **, int, int);
static void getcmd(int, int, const char *);
static void initall(void);
static void initdeck(struct cardtype *[]);
static void initgame(void);
static void instruct(void);
static void makeboard(void);
static void movebox(void);
static void movecard(void);
static void movetofound(struct cardtype **, int);
static void movetotalon(void);
static bool notempty(const struct cardtype *);
static void printbottombettingbox(void);
static void printbottominstructions(void);
static void printcard(int, int, const struct cardtype *);
static void printrank(int, int, const struct cardtype *, bool);
static void printtopbettingbox(void);
static void printtopinstructions(void);
static bool rankhigher(const struct cardtype *, int);
static bool ranklower(const struct cardtype *, const struct cardtype *);
static void removecard(int, int);
static int samesuit(const struct cardtype *, int);
static void showcards(void);
static void showstat(void);
static void shuffle(struct cardtype *[]);
static void simpletableau(struct cardtype **, int);
static void startgame(void);
static void suspend(void);
static bool tabok(const struct cardtype *, int);
static void tabprint(int, int);
static void tabtotab(int, int);
static void transit(struct cardtype **, struct cardtype **);
static void updatebettinginfo(void);
static void usedstock(void);
static void usedtalon(void);
static void
movebox(void)
{
switch (status) {
case BETTINGBOX:
printtopbettingbox();
break;
case NOBOX:
clearabovemovebox();
break;
case INSTRUCTIONBOX:
printtopinstructions();
break;
}
move(moverow, boxcol);
printw("| |");
move(msgrow, boxcol);
printw("| |");
switch (status) {
case BETTINGBOX:
printbottombettingbox();
break;
case NOBOX:
clearbelowmovebox();
break;
case INSTRUCTIONBOX:
printbottominstructions();
break;
}
refresh();
}
static void
printtopinstructions(void)
{
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("|b = present betting information |");
move(tboxrow + 11, boxcol);
printw("|q = quit to end the game |");
move(tboxrow + 12, boxcol);
printw("|==================================|");
}
static void
printtopbettingbox(void)
{
move(tboxrow, boxcol);
printw("*----------------------------------*");
move(tboxrow + 1, boxcol);
printw("|Costs Hand Game Total |");
move(tboxrow + 2, boxcol);
printw("| Hands |");
move(tboxrow + 3, boxcol);
printw("| Inspections |");
move(tboxrow + 4, boxcol);
printw("| Games |");
move(tboxrow + 5, boxcol);
printw("| Runs |");
move(tboxrow + 6, boxcol);
printw("| Information |");
move(tboxrow + 7, boxcol);
printw("| Think time |");
move(tboxrow + 8, boxcol);
printw("|Total Costs |");
move(tboxrow + 9, boxcol);
printw("|Winnings |");
move(tboxrow + 10, boxcol);
printw("|Net Worth |");
move(tboxrow + 11, boxcol);
printw("|Return |");
move(tboxrow + 12, boxcol);
printw("|==================================|");
}
static void
clearabovemovebox(void)
{
int i;
for (i = 0; i <= 11; i++) {
move(tboxrow + i, boxcol);
printw(" ");
}
move(tboxrow + 12, boxcol);
printw("*----------------------------------*");
}
static void
printbottominstructions(void)
{
move(bboxrow, boxcol);
printw("|Replace # with the number of the |");
move(bboxrow + 1, boxcol);
printw("|tableau you want. |");
move(bboxrow + 2, boxcol);
printw("*----------------------------------*");
}
static void
printbottombettingbox(void)
{
move(bboxrow, boxcol);
printw("|x = toggle information box |");
move(bboxrow + 1, boxcol);
printw("|i = list playing instructions |");
move(bboxrow + 2, boxcol);
printw("*----------------------------------*");
}
static void
clearbelowmovebox(void)
{
int i;
move(bboxrow, boxcol);
printw("*----------------------------------*");
for (i = 1; i <= 2; i++) {
move(bboxrow + i, boxcol);
printw(" ");
}
}
static void
makeboard(void)
{
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();
}
static void
cleanupboard(void)
{
int cnt, row, col;
struct cardtype *ptr;
col = 0;
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);
}
}
static void
initdeck(struct cardtype *ideck[])
{
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++) {
ideck[i] = &cards[i];
cards[i].rank = r;
cards[i].suit = s;
cards[i].color = colormap[scnt];
cards[i].next = NIL;
i++;
}
}
}
static void
shuffle(struct cardtype *ideck[])
{
int i,j;
struct cardtype *temp;
for (i=0; i<decksize; i++) {
ideck[i]->visible = FALSE;
ideck[i]->paid = FALSE;
}
for (i = decksize-1; i>=0; i--) {
j = random() % decksize;
if (i != j) {
temp = ideck[i];
ideck[i] = ideck[j];
ideck[j] = temp;
}
}
}
static void
removecard(int a, int b)
{
move(b, a);
printw(" ");
}
static void
printrank(int a, int b, const struct cardtype *cp, bool inverse)
{
move(b, a);
if (cp->rank != 10)
addch(' ');
if (inverse)
standout();
switch (cp->rank) {
case 2: case 3: case 4: case 5: case 6: case 7:
case 8: case 9: case 10:
printw("%d", cp->rank);
break;
case Ace:
addch('A');
break;
case Jack:
addch('J');
break;
case Queen:
addch('Q');
break;
case King:
addch('K');
}
if (inverse)
standend();
}
static void
printcard(int a, int b, const struct cardtype *cp)
{
if (cp == NIL)
removecard(a, b);
else if (cp->visible == FALSE) {
move(b, a);
printw(" ? ");
} else {
bool inverse = (cp->suit == 'd' || cp->suit == 'h');
printrank(a, b, cp, inverse);
if (inverse)
standout();
addch(cp->suit);
if (inverse)
standend();
}
}
static void
transit(struct cardtype **source, struct cardtype **dest)
{
struct cardtype *temp;
temp = *source;
*source = (*source)->next;
temp->next = *dest;
*dest = temp;
}
static void
fndbase(struct cardtype **cp, int column, int row)
{
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++;
if (infullgame) {
this.wins += valuepercardup;
game.wins += valuepercardup;
total.wins += valuepercardup;
}
} else
nomore = TRUE;
} while (nomore == FALSE);
}
static void
initgame(void)
{
int i;
for (i=0; i<18; i++) {
deck[i]->visible = TRUE;
deck[i]->paid = 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;
}
static void
startgame(void)
{
int j;
shuffle(deck);
initgame();
this.hand = costofhand;
game.hand += costofhand;
total.hand += costofhand;
this.inspection = 0;
this.game = 0;
this.runs = 0;
this.information = 0;
this.wins = 0;
this.thinktime = 0;
infullgame = FALSE;
startedgame = FALSE;
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], 0);
for (j=0; j<=3; j++)
fndbase(&tableau[j], pilemap[j], tabrow);
fndbase(&stock, stockcol, stockrow);
showstat();
movetotalon();
updatebettinginfo();
}
static void
clearmsg(void)
{
int i;
if (errmsg == TRUE) {
errmsg = FALSE;
move(msgrow, msgcol);
for (i=0; i<25; i++)
addch(' ');
refresh();
}
}
static void
dumberror(void)
{
errmsg = TRUE;
move(msgrow, msgcol);
printw("Not a proper move ");
}
static void
destinerror(void)
{
errmsg = TRUE;
move(msgrow, msgcol);
printw("Error: Can't move there");
}
static bool
notempty(const struct cardtype *cp)
{
if (cp == NIL) {
errmsg = TRUE;
move(msgrow, msgcol);
printw("Error: no cards to move");
return (FALSE);
} else
return (TRUE);
}
static bool
ranklower(const struct cardtype *cp1, const struct cardtype *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);
}
static bool
diffcolor(const struct cardtype *cp1, const struct cardtype *cp2)
{
if (cp1->color == cp2->color)
return (FALSE);
else
return (TRUE);
}
static bool
tabok(const struct cardtype *cp, int des)
{
if ((cp == stock) && (tableau[des] == NIL))
return (TRUE);
else if (tableau[des] == NIL)
if (stock == NIL &&
cp != bottom[0] && cp != bottom[1] &&
cp != bottom[2] && cp != bottom[3])
return (TRUE);
else
return (FALSE);
else if (ranklower(cp, tableau[des]) && diffcolor(cp, tableau[des]))
return (TRUE);
else
return (FALSE);
}
static void
movetotalon(void)
{
int i, fin;
if (cinhand <= 3 && cinhand > 0) {
move(msgrow, msgcol);
printw("Hand is now empty ");
}
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");
this.runs += costofrunthroughhand;
game.runs += costofrunthroughhand;
total.runs += costofrunthroughhand;
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) {
if (talon->paid == FALSE && talon->visible == TRUE) {
this.information += costofinformation;
game.information += costofinformation;
total.information += costofinformation;
talon->paid = TRUE;
}
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);
}
}
static void
showstat(void)
{
int row, col;
struct cardtype *ptr;
if (!Cflag)
return;
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 ) {
if (ptr->paid == FALSE && ptr->visible == TRUE) {
ptr->paid = TRUE;
this.information += costofinformation;
game.information += costofinformation;
total.information += costofinformation;
}
printcard(col, row, ptr);
DECRHAND(row, col);
}
for ( row = cnewrow, col = cnewcol, ptr = hand;
ptr != NIL;
ptr = ptr->next ) {
if (ptr->paid == FALSE && ptr->visible == TRUE) {
ptr->paid = TRUE;
this.information += costofinformation;
game.information += costofinformation;
total.information += costofinformation;
}
INCRHAND(row, col);
printcard(col, row, ptr);
}
}
static void
clearstat(void)
{
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", " ");
}
}
static void
usedtalon(void)
{
removecard(coldcol, coldrow);
DECRHAND(coldrow, coldcol);
if (talon != NIL && (talon->visible == FALSE)) {
talon->visible = TRUE;
if (Cflag) {
this.information += costofinformation;
game.information += costofinformation;
total.information += costofinformation;
talon->paid = TRUE;
printcard(coldcol, coldrow, talon);
}
}
taloncnt--;
if (Cflag) {
move(talonstatrow, talonstatcol);
printw("%3d", taloncnt);
}
}
static void
usedstock(void)
{
stockcnt--;
if (Cflag) {
move(stockstatrow, stockstatcol);
printw("%3d", stockcnt);
}
}
static void
showcards(void)
{
struct cardtype *ptr;
int row;
if (!Cflag || cardsoff == 52)
return;
for (ptr = talon; ptr != NIL; ptr = ptr->next) {
ptr->visible = TRUE;
ptr->paid = TRUE;
}
for (ptr = hand; ptr != NIL; ptr = ptr->next) {
ptr->visible = TRUE;
ptr->paid = 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("=---=");
if ( cardsoff == 52 )
getcmd(moverow, movecol, "Hit return to exit");
}
static void
updatebettinginfo(void)
{
long thiscosts, gamecosts, totalcosts;
double thisreturn, gamereturn, totalreturn;
time_t now;
long dollars;
time(&now);
dollars = (now - acctstart) / secondsperdollar;
if (dollars > 0) {
acctstart += dollars * secondsperdollar;
if (dollars > maxtimecharge)
dollars = maxtimecharge;
this.thinktime += dollars;
game.thinktime += dollars;
total.thinktime += dollars;
}
thiscosts = this.hand + this.inspection + this.game +
this.runs + this.information + this.thinktime;
gamecosts = game.hand + game.inspection + game.game +
game.runs + game.information + game.thinktime;
totalcosts = total.hand + total.inspection + total.game +
total.runs + total.information + total.thinktime;
this.worth = this.wins - thiscosts;
game.worth = game.wins - gamecosts;
total.worth = total.wins - totalcosts;
thisreturn = ((double)this.wins / (double)thiscosts - 1.0) * 100.0;
gamereturn = ((double)game.wins / (double)gamecosts - 1.0) * 100.0;
totalreturn = ((double)total.wins / (double)totalcosts - 1.0) * 100.0;
if (status != BETTINGBOX)
return;
move(tboxrow + 2, boxcol + 13);
printw("%4ld%8ld%9ld", this.hand, game.hand, total.hand);
move(tboxrow + 3, boxcol + 13);
printw("%4ld%8ld%9ld", this.inspection, game.inspection,
total.inspection);
move(tboxrow + 4, boxcol + 13);
printw("%4ld%8ld%9ld", this.game, game.game, total.game);
move(tboxrow + 5, boxcol + 13);
printw("%4ld%8ld%9ld", this.runs, game.runs, total.runs);
move(tboxrow + 6, boxcol + 13);
printw("%4ld%8ld%9ld", this.information, game.information,
total.information);
move(tboxrow + 7, boxcol + 13);
printw("%4ld%8ld%9ld", this.thinktime, game.thinktime, total.thinktime);
move(tboxrow + 8, boxcol + 13);
printw("%4ld%8ld%9ld", thiscosts, gamecosts, totalcosts);
move(tboxrow + 9, boxcol + 13);
printw("%4ld%8ld%9ld", this.wins, game.wins, total.wins);
move(tboxrow + 10, boxcol + 13);
printw("%4ld%8ld%9ld", this.worth, game.worth, total.worth);
move(tboxrow + 11, boxcol + 13);
printw("%4.0f%%%7.1f%%%8.1f%%", thisreturn, gamereturn, totalreturn);
}
static void
simpletableau(struct cardtype **cp, int des)
{
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();
}
}
static void
tabprint(int sour, int 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++;
}
}
static void
tabtotab(int sour, int des)
{
struct cardtype *temp;
if (notempty(tableau[sour])) {
if (tabok(bottom[sour], des)) {
tabprint(sour, des);
temp = bottom[sour];
bottom[sour] = NIL;
if (bottom[des] == NIL)
bottom[des] = temp;
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();
}
}
static bool
rankhigher(const struct cardtype *cp, int let)
{
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);
}
static int
samesuit(const struct cardtype *cp, int let)
{
if (cp->suit == found[let]->suit)
return (TRUE);
else
return (FALSE);
}
static void
movetofound(struct cardtype **cp, int source)
{
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++;
if (infullgame) {
this.wins += valuepercardup;
game.wins += valuepercardup;
total.wins += valuepercardup;
}
mtfdone = TRUE;
} else
tempbase++;
else
tempbase++;
} while ((tempbase != 4) && !mtfdone);
if (!mtfdone)
destinerror();
}
}
static void
getcmd(int row, int col, const char *cp)
{
char cmd[2] = { '\0', '\0'}, 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 != erasechar() && ch != killchar()) {
if (ch != '\n' && ch != '\r' && ch != ' ')
write(1, "\007", 1);
} else if (ch == erasechar() && i > 0) {
printw("\b \b");
refresh();
cmd[--i] = '\0';
} else if (ch == killchar() && i > 0) {
while (i > 0) {
printw("\b \b");
cmd[--i] = '\0';
}
refresh();
} else if (ch == '\032') {
suspend();
move(row, col + i);
refresh();
} else if (isprint((unsigned char)ch)) {
cmd[i++] = ch;
addch(ch);
refresh();
}
} while (ch != '\n' && ch != '\r' && ch != ' ');
srcpile = cmd[0];
destpile = cmd[1];
}
static void
suspend(void)
{
#ifndef SIGTSTP
char *sh;
#endif
updatebettinginfo();
move(21, 0);
refresh();
if (dbfd != -1) {
lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET);
write(dbfd, (char *)&total, sizeof(total));
}
kill(getpid(), SIGTSTP);
raw();
noecho();
}
static void
movecard(void)
{
int source, dest;
char osrcpile, odestpile;
source = dest = 0;
done = FALSE;
errmsg = FALSE;
do {
if (talon == NIL && hand != NIL)
movetotalon();
if (cardsoff == 52) {
refresh();
srcpile = 'q';
} else if (!startedgame) {
move(msgrow, msgcol);
errmsg = TRUE;
switch (34 - taloncnt - cinhand) {
default:
errmsg = FALSE;
break;
case 1:
printw("One card used from talon ");
break;
case 2:
printw("Two cards used from talon ");
break;
case 3:
printw(">3< cards used from talon ");
break;
}
getcmd(moverow, movecol, "Move:");
} else
getcmd(moverow, movecol, "Move:");
clearmsg();
if (srcpile >= '1' && srcpile <= '4')
source = (int) (srcpile - '1');
if (destpile >= '1' && destpile <= '4')
dest = (int) (destpile - '1');
if (!startedgame &&
(srcpile == 't' || srcpile == 's' || srcpile == 'h' ||
srcpile == '1' || srcpile == '2' || srcpile == '3' ||
srcpile == '4')) {
startedgame = TRUE;
osrcpile = srcpile;
odestpile = destpile;
if (status != BETTINGBOX)
srcpile = 'y';
else do {
getcmd(moverow, movecol, "Inspect game?");
} while (srcpile != 'y' && srcpile != 'n');
if (srcpile == 'n') {
srcpile = 'q';
} else {
this.inspection += costofinspection;
game.inspection += costofinspection;
total.inspection += costofinspection;
srcpile = osrcpile;
destpile = odestpile;
}
}
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') {
dumberror();
break;
}
if (infullgame) {
movetotalon();
break;
}
if (status == BETTINGBOX) {
do {
getcmd(moverow, movecol,
"Buy game?");
} while (srcpile != 'y' &&
srcpile != 'n');
if (srcpile == 'n') {
showcards();
done = TRUE;
break;
}
}
infullgame = TRUE;
this.wins += valuepercardup * cardsoff;
game.wins += valuepercardup * cardsoff;
total.wins += valuepercardup * cardsoff;
this.game += costofgame;
game.game += costofgame;
total.game += costofgame;
movetotalon();
break;
case 'q':
showcards();
done = TRUE;
break;
case 'b':
printtopbettingbox();
printbottombettingbox();
status = BETTINGBOX;
break;
case 'x':
clearabovemovebox();
clearbelowmovebox();
status = NOBOX;
break;
case 'i':
printtopinstructions();
printbottominstructions();
status = INSTRUCTIONBOX;
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);
updatebettinginfo();
} while (!done);
}
static const char *const basicinstructions[] = {
"Here are brief instructions to the game of Canfield:\n\n",
" If you have never played solitaire before, it is recom-\n",
"mended that you consult a solitaire instruction book. In\n",
"Canfield, tableau cards may be built on each other downward\n",
"in alternate colors. An entire pile must be moved as a unit\n",
"in building. Top cards of the piles are available to be able\n",
"to be played on foundations, but never into empty spaces.\n\n",
" Spaces must be filled from the stock. The top card of\n",
"the stock also is available to be played on foundations or\n",
"built on tableau piles. After the stock is exhausted, ta-\n",
"bleau spaces may be filled from the talon and the player may\n",
"keep them open until he wishes to use them.\n\n",
" Cards are dealt from the hand to the talon by threes\n",
"and this repeats until there are no more cards in the hand\n",
"or the player quits. To have cards dealt onto the talon the\n",
"player types 'ht' for his move. Foundation base cards are\n",
"also automatically moved to the foundation when they become\n",
"available.\n\n",
"push any key when you are finished: ",
0 };
static const char *const bettinginstructions[] = {
" The rules for betting are somewhat less strict than\n",
"those used in the official version of the game. The initial\n",
"deal costs $13. You may quit at this point or inspect the\n",
"game. Inspection costs $13 and allows you to make as many\n",
"moves as is possible without moving any cards from your hand\n",
"to the talon. (the initial deal places three cards on the\n",
"talon; if all these cards are used, three more are made\n",
"available) Finally, if the game seems interesting, you must\n",
"pay the final installment of $26. At this point you are\n",
"credited at the rate of $5 for each card on the foundation;\n",
"as the game progresses you are credited with $5 for each\n",
"card that is moved to the foundation. Each run through the\n",
"hand after the first costs $5. The card counting feature\n",
"costs $1 for each unknown card that is identified. If the\n",
"information is toggled on, you are only charged for cards\n",
"that became visible since it was last turned on. Thus the\n",
"maximum cost of information is $34. Playing time is charged\n",
"at a rate of $1 per minute.\n\n",
"push any key when you are finished: ",
0 };
static void
instruct(void)
{
const char *const *cp;
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 == 'n')
return;
clear();
for (cp = basicinstructions; *cp != 0; cp++)
printw("%s", *cp);
refresh();
getch();
clear();
move(originrow, origincol);
printw("Do you want instructions for betting?");
do {
getcmd(originrow + 2, origincol, "y or n?");
} while (srcpile != 'y' && srcpile != 'n');
if (srcpile == 'n')
return;
clear();
for (cp = bettinginstructions; *cp != 0; cp++)
printw("%s", *cp);
refresh();
getch();
}
static void
initall(void)
{
ssize_t i;
srandom(time(NULL));
time(&acctstart);
initdeck(deck);
uid = getuid();
dbfd = open(_PATH_SCORE, O_RDWR);
setgid(getgid());
if (dbfd < 0)
return;
if (dbfd < 3)
exit(1);
if (lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET) < 0) {
close(dbfd);
dbfd = -1;
return;
}
i = read(dbfd, (char *)&total, sizeof(total));
if (i < 0) {
close(dbfd);
dbfd = -1;
return;
}
}
static bool
finish(void)
{
int row, col;
if (cardsoff == 52) {
getcmd(moverow, movecol, "Hit return to exit");
clear();
refresh();
move(originrow, origincol);
printw("CONGRATULATIONS!\n");
printw("You won the game. That is a feat to be proud of.\n");
row = originrow + 5;
col = origincol;
} else {
move(msgrow, msgcol);
printw("You got %d card", cardsoff);
if (cardsoff > 1)
printw("s");
printw(" off ");
move(msgrow, msgcol);
row = moverow;
col = movecol;
}
do {
getcmd(row, col, "Play again (y or n)?");
} while (srcpile != 'y' && srcpile != 'n');
errmsg = TRUE;
clearmsg();
if (srcpile == 'y')
return (FALSE);
else
return (TRUE);
}
static void
cleanup(int dummy __unused)
{
total.thinktime += 1;
status = NOBOX;
updatebettinginfo();
if (dbfd != -1) {
lseek(dbfd, uid * sizeof(struct betinfo), SEEK_SET);
write(dbfd, (char *)&total, sizeof(total));
close(dbfd);
}
clear();
move(22,0);
refresh();
endwin();
exit(0);
}
static void
askquit(int dummy __unused)
{
move(msgrow, msgcol);
printw("Really wish to quit? ");
do {
getcmd(moverow, movecol, "y or n?");
} while (srcpile != 'y' && srcpile != 'n');
clearmsg();
if (srcpile == 'y')
cleanup(0);
signal(SIGINT, askquit);
}
int
main(void)
{
#ifdef MAXLOAD
double vec[3];
loadav(vec);
if (vec[2] >= MAXLOAD) {
puts("The system load is too high. Try again later.");
exit(0);
}
#endif
signal(SIGINT, askquit);
signal(SIGHUP, cleanup);
signal(SIGTERM, cleanup);
if (!initscr()) {
fprintf(stderr, "couldn't initialize screen\n");
exit (0);
}
raw();
noecho();
initall();
instruct();
makeboard();
for (;;) {
startgame();
movecard();
if (finish())
break;
if (cardsoff == 52)
makeboard();
else
cleanupboard();
}
cleanup(0);
return(0);
}