/* $Id: game.c,v 1.9 2002/03/02 21:02:21 sverrehu Exp $ */ /************************************************************************** * * FILE game.c * MODULE OF Card game. * * WRITTEN BY Sverre H. Huseby * **************************************************************************/ #include #include #include #include #include #include #include #include "rcfile.h" #include "card.h" #include "pile.h" #include "score.h" #include "win.h" #include "board.h" #include "game.h" /* for testing purposes */ #undef ALMOST_SOLVED /************************************************************************** * * * P R I V A T E D A T A * * * **************************************************************************/ #define BLACK 0 #define RED 1 /************************************************************************** * * * P U B L I C D A T A * * * **************************************************************************/ int gameRunning; int gameExit; int gameNumCardsToDraw = 3; int gameIncludeInHighscoreList = 1; int gameDoTiming = 1; Card *card[NUM_CARDS]; Pile *pile[NUM_PILES]; time_t gameStartTime = 0; time_t gameTime = 0; int gameNumPlayed = 0; int gameNumSolved = 0; time_t gameTotalTime = 0; /************************************************************************** * * * P R I V A T E F U N C T I O N S * * * **************************************************************************/ static int gameGetColor(int suit) { if (suit == CARD_CLUBS || suit == CARD_SPADES) return BLACK; return RED; } /************************************************************************** * * * P U B L I C F U N C T I O N S * * * **************************************************************************/ void gameInit(void) { int q; RANDOMIZE(time(NULL) + getpid()); gameRunning = 0; gameExit = 0; for (q = 0; q < NUM_PILES; q++) pile[q] = pileNew(); for (q = 0; q < NUM_CARDS; q++) card[q] = cardNew(); /* do some pile init here, since board.c relies on it to build the pile cache. */ pile[0]->x = CARD_SEP_X; pile[0]->y = CARD_SEP_Y; pile[0]->maxWidth = CARD_WIDTH + CARD_SEP_X; pile[0]->maxHeight = CARD_HEIGHT + CARD_SEP_Y; pile[1]->x = pile[0]->x + CARD_WIDTH + CARD_SEP_X; pile[1]->y = pile[0]->y; pile[1]->maxWidth = CARD_WIDTH + 2 * CARD_SHOW_X + CARD_SEP_X; pile[1]->maxHeight = CARD_HEIGHT + CARD_SEP_Y; pile[2]->x = pile[1]->x + 2 * (CARD_WIDTH + CARD_SEP_X); pile[2]->y = pile[1]->y; for (q = 2; q < 6; q++) { if (q > 2) { pile[q]->x = pile[q - 1]->x + CARD_WIDTH + CARD_SEP_X; pile[q]->y = pile[q - 1]->y; } pile[q]->maxWidth = CARD_WIDTH + CARD_SEP_X; pile[q]->maxHeight = CARD_HEIGHT + CARD_SEP_Y; } pile[6]->x = CARD_SEP_X; pile[6]->y = pile[0]->y + CARD_HEIGHT + CARD_SEP_Y; for (q = 6; q < 13; q++) { if (q > 6) { pile[q]->x = pile[q - 1]->x + CARD_WIDTH + CARD_SEP_X; pile[q]->y = pile[q - 1]->y; } pile[q]->maxWidth = CARD_WIDTH; pile[q]->maxHeight = CARD_HEIGHT + (12 + q - 5) * CARD_SHOW_Y; } rcRead(); scoreInit(); winInit(); boardInit(); /* init-functions for other `modules' are called from board.c */ } void gameFinish(void) { int q; cardFinish(); boardFinish(); winFinish(); scoreFinish(); rcWrite(); for (q = 0; q < 54; q++) { free(card[q]); card[q] = NULL; } for (q = 0; q < NUM_PILES; q++) { free(pile[q]); pile[q] = NULL; } } void gameInitGame(void) { int q, w, i, curr; Card *tmp, *tmps[NUM_CARDS]; gameRunning = 1; gameStartTime = gameTime = 0; scoreInitGame(); winInitGame(); for (q = 0; q < NUM_PILES; q++) pileRemoveAllCards(pile[q]); pile[0]->dx = 2; pile[0]->dy = 2; pile[0]->deltaEach = 7; pile[0]->outline = 1; if (gameNumCardsToDraw > 1) { /* TODO: can't have 3D look here yet. */ pile[1]->dx = CARD_SHOW_X; pile[1]->dy = 0; pile[1]->deltaEach = -gameNumCardsToDraw; } else { /* give 3D look when drawing only one card. */ pile[1]->dx = 2; pile[1]->dy = 2; pile[1]->deltaEach = 7; } for (q = 2; q < 6; q++) { pile[q]->dx = 2; pile[q]->dy = 2; pile[q]->deltaEach = 7; pile[q]->outline = 2; } for (q = 6; q < 13; q++) { pile[q]->dx = 0; pile[q]->dy = CARD_SHOW_Y; } for (q = 0; q < 4; q++) for (w = 0; w < 13; w++) { i = 13 * q + w; tmps[i] = card[i]; switch (q) { case 0: tmps[i]->suit = CARD_CLUBS; break; case 1: tmps[i]->suit = CARD_DIAMONDS; break; case 2: tmps[i]->suit = CARD_HEARTS; break; case 3: tmps[i]->suit = CARD_SPADES; break; } tmps[i]->value = w + 1; } #ifdef ALMOST_SOLVED for (q = 0; q < 4; q++) for (w = 0; w < 13; w++) { i = 13 * q + w; card[i] = tmps[i]; if (!(q == 0 && w == 12)) pileAddCardTop(pile[2 + q], card[i]); } pileAddCardTop(pile[6], card[13 * 0 + 12]); #else for (q = 0; q < NUM_CARDS; q++) { i = RANDOM(NUM_CARDS - q); card[q] = tmps[i]; tmp = tmps[i]; tmps[i] = tmps[NUM_CARDS - 1 - q]; tmps[NUM_CARDS - 1 - q] = tmp; } curr = 0; /* the pile with spare cards */ for (q = 0; q < 24; q++) { card[curr]->frontUp = 0; pileAddCardBottom(pile[0], card[curr++]); } /* the open piles */ for (q = 0; q < 7; q++) for (w = 0; w <= q; w++) { if (w < q) card[curr]->frontUp = 0; else card[curr]->frontUp = 1; pileAddCardTop(pile[6 + q], card[curr++]); } #endif boardInitGame(); } void gameFinishGame(void) { gameUpdateTime(); gameRunning = 0; /* must be after gameUpdateTime() */ boardFinishGame(); winFinishGame(); scoreFinishGame(); if (gameTime) { ++gameNumPlayed; gameTotalTime += gameTime; rcWrite(); } } void gameDrawNext(void) { int q, drawn; Card *c; if (pile[0]->numCards) { drawn = 0; for (q = 0; q < gameNumCardsToDraw; q++) { if (pile[0]->numCards == 0) break; c = pile[0]->top; pileRemoveCard(pile[0], c); pileAddCardTop(pile[1], c); c->frontUp = 1; ++drawn; } if (gameNumCardsToDraw > 1) pile[1]->deltaEach = -drawn; } else { while (pile[1]->numCards) { c = pile[1]->top; pileRemoveCard(pile[1], c); c->frontUp = 0; pileAddCardTop(pile[0], c); } } boardUpdatePileCache(pile[0]); boardUpdatePileCache(pile[1]); boardDrawPile(pile[0], 0); boardDrawPile(pile[1], 0); } int gameFindPileNumOfPile(Pile *p) { int q; for (q = 0; q < NUM_PILES; q++) if (pile[q] == p) return q; return -1; } int gameFindPileNumOfCard(Card *c) { return gameFindPileNumOfPile(c->pile); } void gameHandleSingleClick(Card *c) { int pileNum; gameStartTiming(); pileNum = gameFindPileNumOfCard(c); if (pileNum == 0) { gameDrawNext(); } else if (pileNum == 1) { if (c == c->pile->top) { boardStartDragCardAndAbove(c); if (pile[1]->deltaEach < 0) ++pile[1]->deltaEach; boardUpdatePileCache(pile[1]); boardDrawPile(pile[1], 0); } } else if (pileNum >= 2 && pileNum < 6) { if (c == c->pile->top) boardStartDragCardAndAbove(c); } else if (pileNum >= 6 && pileNum < 13) { if (c == c->pile->top && !c->frontUp) { c->frontUp = 1; boardUpdatePileCache(c->pile); boardDrawPile(c->pile, 0); } else { if (c->frontUp) boardStartDragCardAndAbove(c); } } } void gameHandleDoubleClick(Card *c) { int q, pileNum; Pile *source, *dest = NULL; if (c != c->pile->top) return; gameStartTiming(); if (!c->frontUp) { gameHandleSingleClick(c); return; /* added 1997-08-09 */ } pileNum = gameFindPileNumOfCard(c); if (pileNum != 1 && (pileNum < 6 || pileNum > 12)) return; if (c->value == 1) { for (q = 2; q < 6; q++) if (pile[q]->numCards == 0) { dest = pile[q]; break; } } else { for (q = 2; q < 6; q++) { if (pile[q]->numCards == 0) continue; if (pile[q]->top->suit == c->suit && pile[q]->top->value == c->value - 1) { dest = pile[q]; break; } } } if (dest) { source = c->pile; pileRemoveCard(source, c); if (source == pile[1] && pile[1]->deltaEach < 0) ++pile[1]->deltaEach; boardUpdatePileCache(source); boardDrawPile(source, 0); pileAddCardTop(dest, c); boardUpdatePileCache(dest); boardDrawPile(dest, 0); gameCheckIfSolution(); } } Pile * gameCardDropped(Pile *from, Pile *drop, int intersect[], int n) { int q, i; Pile *ret, *p; Card *bot, *top; ret = from; bot = drop->bottom; for (q = 0; q < n; q++) { i = intersect[q]; p = pile[i]; top = p->top; if (i == 0 || i == 1) continue; if (i >= 2 && i < 6) { if (drop->numCards == 1 && ((p->numCards == 0 && bot->value == 1) || (p->numCards > 0 && bot->value == top->value + 1 && bot->suit == top->suit))) { ret = p; break; } } else { if (p->numCards == 0) { if (bot->value == 13) { ret = p; break; } } else if (top->frontUp) { if (top->value == bot->value + 1 && gameGetColor(top->suit) != gameGetColor(bot->suit)) { ret = p; break; } } } } if (ret == from && from == pile[1] && gameNumCardsToDraw > 1 && pile[1]->deltaEach > -gameNumCardsToDraw) --pile[1]->deltaEach; return ret; } void gameCheckIfSolution(void) { int q; Pile *p; char *hurray[] = { "Play it again, Sam!", /* no longer than this */ "Hurray!", "Yess!", "You made it!", "That's it!", "No problemo", "Yee-ha!", "Finito!", "The end", "Congratulations!", "Good work!", "Once more?", "Ka-zam!", "Ka-boom!", "Ta-da!" }; for (q = 2; q < 6; q++) { p = pile[q]; if (p == NULL || p->top == NULL || p->top->value < 13) return; } /* game over -- solution */ gameUpdateTime(); gameRunning = 0; ++gameNumSolved; boardShowText(hurray[RANDOM(sizeof(hurray) / sizeof(char *))]); if (gameIncludeInHighscoreList && gameDoTiming) scorePossiblyUpdateScores(); } void gameUpdateTime(void) { if (gameRunning && gameStartTime) { time(&gameTime); gameTime -= gameStartTime; } winShowTime(); } void gameStartTiming(void) { if (gameStartTime) return; time(&gameStartTime); gameUpdateTime(); winStartTiming(); } char * gameCountToStr(int n) { static char text[20]; static char *count[] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; if (n < sizeof(count) / sizeof(char *)) strcpy(text, count[n]); else sprintf(text, "%d", n); return text; } void gameTimeToHMS(time_t t, int *h, int *m, int *s) { *h = t / 3600; *m = (t % 3600) / 60; *s = t % 60; } char * gameTimeToStr(time_t t) { static char text[40]; int h, m, s; gameTimeToHMS(t, &h, &m, &s); if (h) sprintf(text, "%2d:%02d:%02d", h, m, s); else sprintf(text, "%2d:%02d", m, s); return text; } char * gameTimeToVerboseStr(time_t t) { static char text[80]; int h, m, s; gameTimeToHMS(t, &h, &m, &s); if (h == 0 && m == 0 && s == 0) strcpy(text, "no time"); else { *text = '\0'; if (h) { strcat(text, gameCountToStr(h)); strcat(text, h == 1 ? " hour" : " hours"); } if (m) { if (h) strcat(text, s ? ", " : " and "); strcat(text, gameCountToStr(m)); strcat(text, m == 1 ? " minute" : " minutes"); } if (s) { if (h || m) strcat(text, " and "); strcat(text, gameCountToStr(s)); strcat(text, s == 1 ? " second" : " seconds"); } } return text; }