From c4e4d7c76563216b598feba7abc6b9b72df4724b Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Fri, 22 Mar 2019 15:22:10 -0400 Subject: Implement the game --- sol.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 181 insertions(+), 66 deletions(-) (limited to 'sol.c') diff --git a/sol.c b/sol.c index 8ab89d5..ed5981d 100644 --- a/sol.c +++ b/sol.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -24,6 +25,27 @@ typedef unsigned uint; +static Sint8 cardSuit(Sint8 card) { + card = abs(card); + if (card > Cards_Spade) { + return Cards_Spade; + } else if (card > Cards_Heart) { + return Cards_Heart; + } else if (card > Cards_Diamond) { + return Cards_Diamond; + } else { + return Cards_Club; + } +} + +static Sint8 cardColor(Sint8 card) { + return cardSuit(card) == Cards_Diamond || cardSuit(card) == Cards_Heart; +} + +static Sint8 cardRank(Sint8 card) { + return abs(card) - cardSuit(card); +} + enum { Stock, Waste1, @@ -44,35 +66,104 @@ enum { static struct Stack stacks[StacksLen]; -static void restock(void) { +// TODO: Scoring method, score, time. +static struct { + uint draw; +} game = { + .draw = 3, +}; + +static void gameDeal(void) { for (uint i = 0; i < StacksLen; ++i) { stackClear(&stacks[i]); } stackDeck(&stacks[Stock]); -} - -static void reveal(uint i) { - if (!stacks[i].len) return; - stackPush(&stacks[i], abs(stackPop(&stacks[i]))); -} - -static void deal(void) { stackShuffle(&stacks[Stock]); for (uint i = Tableau1; i <= Tableau7; ++i) { - stackMoveTo(&stacks[i], &stacks[Stock], 1 + i - Tableau1); - reveal(i); + stackMoveTo(&stacks[i], &stacks[Stock], i - Tableau1); + stackFlipTo(&stacks[i], &stacks[Stock], 1); } } -static void draw(void) { +static void gameDraw(void) { stackMoveTo(&stacks[Waste1], &stacks[Waste3], stacks[Waste3].len); if (stacks[Stock].len) { - stackFlipTo(&stacks[Waste3], &stacks[Stock], 3); + if (game.draw == 1) { + stackFlipTo(&stacks[Waste1], &stacks[Stock], 1); + } else { + stackFlipTo(&stacks[Waste3], &stacks[Stock], 3); + } } else { stackFlipTo(&stacks[Stock], &stacks[Waste1], stacks[Waste1].len); } } +static bool gameFind(uint *stack, uint *index, Sint8 card) { + for (*stack = 0; *stack < StacksLen; ++*stack) { + for (*index = 0; *index < stacks[*stack].len; ++*index) { + if (stacks[*stack].cards[*index] == card) return true; + } + } + return false; +} + +static bool gameAvail(Sint8 card) { + uint stack, index; + if (card < 0) return false; + if (!gameFind(&stack, &index, card)) return false; + + bool top = (index == stacks[stack].len - 1); + if (stack == Waste1) { + return top && !stacks[Waste3].len; + } else if (stack == Waste3) { + return top; + } else if (stack >= Foundation1 && stack <= Foundation4) { + return top; + } else if (stack >= Tableau1 && stack <= Tableau7) { + return true; + } else { + return false; + } +} + +static bool gameReveal(Sint8 card) { + uint stack, index; + if (!gameFind(&stack, &index, card)) return false; + if (stack < Tableau1 || stack > Tableau7) return false; + if (index != stacks[stack].len - 1) return false; + if (card > 0) return false; + stackFlipTo(&stacks[stack], &stacks[stack], 1); + return true; +} + +static bool gameMove(uint dest, Sint8 card) { + uint source, index; + if (!gameFind(&source, &index, card)) return false; + if (source == dest) return false; + + uint count = stacks[source].len - index; + Sint8 destTop = stackTop(&stacks[dest]); + + if (dest >= Foundation1 && dest <= Foundation4) { + if (count > 1) return false; + if (!destTop && cardRank(card) != Cards_A) return false; + if (destTop && cardSuit(card) != cardSuit(destTop)) return false; + if (destTop && cardRank(card) != cardRank(destTop) + 1) return false; + stackMoveTo(&stacks[dest], &stacks[source], 1); + return true; + } + + if (dest >= Tableau1 && dest <= Tableau7) { + if (!destTop && cardRank(card) != Cards_K) return false; + if (destTop && cardColor(card) == cardColor(destTop)) return false; + if (destTop && cardRank(card) != cardRank(destTop) - 1) return false; + stackMoveTo(&stacks[dest], &stacks[source], count); + return true; + } + + return false; +} + enum { StackMarginX = 11, StackMarginY = 6, @@ -108,7 +199,7 @@ enum { struct Item { SDL_Rect rect; - uint tex; + uint texture; Sint8 card; }; @@ -123,15 +214,25 @@ static void listPush(struct List *list, struct Item item) { } static struct { + SDL_Rect stacks[StacksLen]; struct List base; struct List main; struct List drag; struct Item dragItem; - uint cardBack; + uint backTexture; } layout = { - .cardBack = Cards_Back1, + .backTexture = Cards_Back1, }; +static struct Item *layoutFind(const SDL_Point *point) { + for (uint i = layout.main.len - 1; i < layout.main.len; --i) { + if (SDL_PointInRect(point, &layout.main.items[i].rect)) { + return &layout.main.items[i]; + } + } + return NULL; +} + static void layoutClear(void) { layout.base.len = 0; layout.main.len = 0; @@ -143,7 +244,7 @@ static void layoutCard(struct List **list, SDL_Rect *rect, Sint8 card) { *list = &layout.drag; *rect = layout.dragItem.rect; } - struct Item item = { *rect, (card > 0 ? card : layout.cardBack), card }; + struct Item item = { *rect, (card > 0 ? card : layout.backTexture), card }; listPush(*list, item); } @@ -162,6 +263,7 @@ static void layoutStock(void) { SDL_Rect rect = { StockX, StockY, Cards_Width, Cards_Height }; struct Item item = { rect, Cards_O, 0 }; listPush(&layout.base, item); + layout.stacks[Stock] = rect; layoutStack(&rect, &stacks[Stock], 24); } @@ -181,6 +283,7 @@ static void layoutFoundations(void) { for (uint i = Foundation1; i <= Foundation4; ++i) { struct Item item = { base, Cards_Empty, 0 }; listPush(&layout.base, item); + layout.stacks[i] = base; SDL_Rect rect = base; layoutStack(&rect, &stacks[i], 13); base.x += Cards_Width + StackMarginX; @@ -190,6 +293,8 @@ static void layoutFoundations(void) { static void layoutTableau(void) { SDL_Rect base = { TableauX, TableauY, Cards_Width, Cards_Height }; for (uint i = Tableau1; i <= Tableau7; ++i) { + SDL_Rect stack = { base.x, base.y, base.w, WindowHeight }; + layout.stacks[i] = stack; struct List *list = &layout.main; SDL_Rect rect = base; for (uint j = 0; j < stacks[i].len; ++j) { @@ -202,6 +307,44 @@ static void layoutTableau(void) { } } +static bool mouseButtonDown(SDL_MouseButtonEvent button) { + struct SDL_Point point = { button.x, button.y }; + if (SDL_PointInRect(&point, &layout.stacks[Stock])) { + gameDraw(); + return true; + } + struct Item *item = layoutFind(&point); + if (!item) return false; + if (!gameAvail(item->card)) return gameReveal(item->card); + if (button.clicks == 2) { + for (uint dest = Foundation1; dest <= Foundation4; ++dest) { + if (gameMove(dest, item->card)) return true; + } + return false; + } + layout.dragItem = *item; + return true; +} + +static bool mouseButtonUp(SDL_MouseButtonEvent button) { + (void)button; + if (!layout.dragItem.card) return false; + for (uint dest = 0; dest < StacksLen; ++dest) { + if (SDL_HasIntersection(&layout.dragItem.rect, &layout.stacks[dest])) { + if (gameMove(dest, layout.dragItem.card)) break; + } + } + layout.dragItem.card = 0; + return true; +} + +static bool mouseMotion(SDL_MouseMotionEvent motion) { + if (!motion.state) return false; + layout.dragItem.rect.x += motion.xrel; + layout.dragItem.rect.y += motion.yrel; + return true; +} + static SDL_Renderer *render; static SDL_Texture *textures[Cards_Count]; @@ -209,12 +352,13 @@ static void renderList(const struct List *list) { for (uint i = 0; i < list->len; ++i) { SDL_RenderCopy( render, - textures[list->items[i].tex], + textures[list->items[i].texture], NULL, &list->items[i].rect ); } } +// TODO: Use SDL_ShowMessageBox. static void err(const char *prefix) { fprintf(stderr, "%s: %s\n", prefix, SDL_GetError()); exit(EXIT_FAILURE); @@ -244,7 +388,7 @@ int main(void) { ); } - // TODO: Path search/option. + // TODO: Path search. SDL_RWops *rw = SDL_RWFromFile("CARDS.DLL", "rb"); if (!rw) err("CARDS.DLL"); @@ -262,53 +406,12 @@ int main(void) { } Cards_Free(cards); - restock(); - deal(); - - for (;;) { - SDL_Event event; - SDL_WaitEvent(&event); - if (event.type == SDL_QUIT) goto quit; - - if (event.type == SDL_KEYDOWN) { - switch (event.key.keysym.sym) { - break; case SDLK_q: goto quit; - break; case SDLK_r: restock(); deal(); - break; case SDLK_SPACE: draw(); - break; case SDLK_f: { - stackFlipTo(&stacks[Foundation1], &stacks[Stock], 1); - } - break; case SDLK_t: { - stackFlipTo(&stacks[Tableau7], &stacks[Stock], 1); - } - break; case SDLK_b: { - layout.cardBack++; - layout.cardBack -= Cards_Back1; - layout.cardBack %= 12; - layout.cardBack += Cards_Back1; - } - } - } - - if (event.type == SDL_MOUSEBUTTONDOWN) { - struct SDL_Point point = { event.button.x, event.button.y }; - for (uint i = layout.main.len - 1; i < layout.main.len; --i) { - if (SDL_PointInRect(&point, &layout.main.items[i].rect)) { - layout.dragItem = layout.main.items[i]; - break; - } - } - } - - if (event.type == SDL_MOUSEBUTTONUP) { - layout.dragItem.card = 0; - } + gameDeal(); - if (event.type == SDL_MOUSEMOTION && event.motion.state) { - layout.dragItem.rect.x += event.motion.xrel; - layout.dragItem.rect.y += event.motion.yrel; - } + // FIXME: Use a portable random. + layout.backTexture = Cards_Back1 + arc4random_uniform(12); + for (;;) { layoutClear(); layoutStock(); layoutWaste(); @@ -317,12 +420,24 @@ int main(void) { SDL_SetRenderDrawColor(render, 0x00, 0xAA, 0x55, 0xFF); SDL_RenderClear(render); - renderList(&layout.base); renderList(&layout.main); renderList(&layout.drag); - SDL_RenderPresent(render); + + SDL_Event event; + for (;;) { + SDL_WaitEvent(&event); + if (event.type == SDL_QUIT) { + goto quit; + } else if (event.type == SDL_MOUSEBUTTONDOWN) { + if (mouseButtonDown(event.button)) break; + } else if (event.type == SDL_MOUSEBUTTONUP) { + if (mouseButtonUp(event.button)) break; + } else if (event.type == SDL_MOUSEMOTION) { + if (mouseMotion(event.motion)) break; + } + } } quit: -- cgit 1.4.1