diff options
author | June McEnroe <june@causal.agency> | 2019-08-22 15:43:24 -0400 |
---|---|---|
committer | June McEnroe <june@causal.agency> | 2019-08-22 15:43:24 -0400 |
commit | beba76119fbc8e29bf22093bfa54316ad2591d30 (patch) | |
tree | 49a978628c83d61dffd55ded8580b35338db3a38 | |
parent | Mark card functions inline (diff) | |
download | cards-beba76119fbc8e29bf22093bfa54316ad2591d30.tar.gz cards-beba76119fbc8e29bf22093bfa54316ad2591d30.zip |
Remove games
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | asset.h | 72 | ||||
-rw-r--r-- | freecell.c | 484 | ||||
-rw-r--r-- | layout.h | 110 | ||||
-rw-r--r-- | sol.c | 393 | ||||
-rw-r--r-- | stack.h | 108 |
7 files changed, 7 insertions, 1177 deletions
diff --git a/.gitignore b/.gitignore index c15f5a2..0ca9d1f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,4 @@ FREECELL.EXE SOL.EXE config.mk dump -freecell -sol +example diff --git a/Makefile b/Makefile index 073f0be..8990dd0 100644 --- a/Makefile +++ b/Makefile @@ -2,18 +2,16 @@ CFLAGS += -std=c99 -Wall -Wextra -Wpedantic include config.mk -BINS = dump freecell sol +BINS = dump -all: $(BINS) +all: ${BINS} -$(BINS): cards.o +${BINS}: cards.o .o: - $(CC) $(LDFLAGS) $< cards.o $(LDLIBS) -o $@ + ${CC} ${LDFLAGS} $< cards.o ${LDLIBS} -o $@ -cards.o dump.o freecell.o sol.o: cards.h - -freecell.o sol.o: asset.h layout.h stack.h +cards.o dump.o: cards.h clean: - rm -f $(BINS) *.o *.bmp + rm -f ${BINS} *.o *.bmp diff --git a/asset.h b/asset.h deleted file mode 100644 index cc37709..0000000 --- a/asset.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef ASSET_H -#define ASSET_H - -#include <SDL_filesystem.h> -#include <SDL_rwops.h> -#include <stdio.h> -#include <stdlib.h> - -struct Paths { - char *base; - char *pref; -}; - -static inline int assetPaths(struct Paths *paths) { - paths->base = SDL_GetBasePath(); - if (!paths->base) return -1; - paths->pref = SDL_GetPrefPath("Causal Agency", "Entertainment Pack"); - if (!paths->pref) return -1; - return 0; -} - -static inline SDL_RWops * -assetOpen(const struct Paths *paths, const char *names[], size_t len) { - const char *dirs[3] = { paths->pref, paths->base, "" }; - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < len; ++j) { - char path[1024]; - snprintf(path, sizeof(path), "%s%s", dirs[i], names[j]); - SDL_RWops *rw = SDL_RWFromFile(path, "rb"); - if (rw) return rw; - } - } - return NULL; -} - -static inline SDL_RWops *assetOpenCards(const struct Paths *paths) { - const char *names[3] = { "CARDS.DLL", "SOL.EXE", "cards.dll" }; - SDL_RWops *rw = assetOpen(paths, names, 3); - if (rw) return rw; - char msg[4096]; - snprintf( - msg, sizeof(msg), "%s or %s not found in:\n%s\n%s", - names[0], names[1], paths->pref, paths->base - ); - SDL_ShowSimpleMessageBox( - SDL_MESSAGEBOX_ERROR, "Entertainment Pack", msg, NULL - ); - return NULL; -} - -static inline SDL_RWops *assetOpenFreeCell(const struct Paths *paths) { - const char *names[2] = { "FREECELL.EXE", "freecell.exe" }; - return assetOpen(paths, names, 2); -} - -#endif diff --git a/freecell.c b/freecell.c deleted file mode 100644 index b7f6378..0000000 --- a/freecell.c +++ /dev/null @@ -1,484 +0,0 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <SDL.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> - -#include "asset.h" -#include "cards.h" -#include "layout.h" -#include "stack.h" - -enum { - Foundation1, - Foundation2, - Foundation3, - Foundation4, - Cell1, - Cell2, - Cell3, - Cell4, - Tableau1, - Tableau2, - Tableau3, - Tableau4, - Tableau5, - Tableau6, - Tableau7, - Tableau8, - StacksLen, -}; - -static struct Stack stacks[StacksLen]; - -static struct { - bool avail; - uint dst; - uint src; -} undo; - -static uint kingFace = Cards_KingRight; - -static void gameDeal(void) { - for (uint i = 0; i < StacksLen; ++i) { - stackClear(&stacks[i]); - } - struct Stack deck = {0}; - for (Card i = 1; i <= 52; ++i) { - stackPush(&deck, i); - } - stackShuffle(&deck); - for (uint i = Tableau1; i <= Tableau8; ++i) { - stackMoveTo(&stacks[i], &deck, (i < Tableau5 ? 7 : 6)); - } - undo.avail = false; - kingFace = Cards_KingRight; -} - -static bool gameWin(void) { - for (uint i = Foundation1; i <= Foundation4; ++i) { - if (stacks[i].len != 13) return false; - } - return true; -} - -static bool gameFind(uint *stack, Card card) { - for (*stack = 0; *stack < StacksLen; ++*stack) { - for (uint i = 0; i < stacks[*stack].len; ++i) { - if (stacks[*stack].cards[i] == card) return true; - } - } - return false; -} - -static bool gameAvail(Card card) { - uint stack; - assert(gameFind(&stack, card)); - if (stack <= Foundation4) return false; - return card == stackTop(&stacks[stack]); -} - -static bool gameMove(uint dst, Card card) { - uint src; - assert(gameFind(&src, card)); - Card top = stackTop(&stacks[dst]); - - if (src == dst) return false; - if (dst >= Cell1 && dst <= Cell4) { - if (stacks[dst].len) return false; - kingFace = Cards_KingLeft; - } - if (dst >= Foundation1 && dst <= Foundation4) { - if (!top && cardRank(card) != Cards_A) return false; - if (top && cardSuit(card) != cardSuit(top)) return false; - if (top && cardRank(card) != cardRank(top) + 1) return false; - kingFace = Cards_KingRight; - } - if (dst >= Tableau1 && dst <= Tableau8) { - if (top && cardColor(card) == cardColor(top)) return false; - if (top && cardRank(card) != cardRank(top) - 1) return false; - } - - undo.dst = src; - undo.src = dst; - undo.avail = true; - stackPush(&stacks[dst], stackPop(&stacks[src])); - return true; -} - -static bool gameUndo(void) { - if (!undo.avail) return false; - stackPush(&stacks[undo.dst], stackPop(&stacks[undo.src])); - undo.avail = false; - return true; -} - -static bool gameAuto(void) { - Card min[2] = { Cards_K, Cards_K }; - for (uint i = Cell1; i <= Tableau8; ++i) { - for (uint j = 0; j < stacks[i].len; ++j) { - Card card = stacks[i].cards[j]; - if (cardRank(card) < min[cardColor(card)]) { - min[cardColor(card)] = cardRank(card); - } - } - } - for (uint i = Cell1; i <= Tableau8; ++i) { - Card card = stackTop(&stacks[i]); - if (!card) continue; - if (cardRank(card) > Cards_2) { - if (min[!cardColor(card)] < cardRank(card)) continue; - } - for (uint dst = Foundation1; dst <= Foundation4; ++dst) { - if (gameMove(dst, card)) return true; - } - } - return false; -} - -enum { - CardWidth = Cards_CardWidth, - CardHeight = Cards_CardHeight, - - CellX = 0, - CellY = 0, - - KingMarginX = 13, - KingX = CellX + 4 * CardWidth + KingMarginX, - KingY = 18, - KingPadX = 3, - KingPadY = 3, - KingWidth = Cards_KingWidth + 2 * KingPadX, - KingHeight = Cards_KingHeight + 2 * KingPadY, - - FoundationX = KingX + KingWidth + KingMarginX, - FoundationY = CellY, - - StackMarginX = 7, - StackMarginY = 10, - - TableauX = StackMarginX, - TableauY = CellY + CardHeight + StackMarginY, - FanDownDeltaY = 17, - - KingWinMarginX = 10, - KingWinMarginY = 10, - KingWinX = KingWinMarginX, - KingWinY = CellY + CardHeight + KingWinMarginY, - KingWinWidth = 320, - KingWinHeight = 320, - - WindowWidth = 8 * CardWidth + 9 * StackMarginX + 1, - WindowHeight = KingWinY + KingWinHeight + KingWinMarginY, -}; - -static const struct Style FanDown = { 1, 0, 0, 0, FanDownDeltaY }; - -static struct SDL_Rect stackRects[StacksLen]; -static struct Layout layout; -static struct List reveal; - -static void updateLayout(void) { - layoutClear(&layout); - - SDL_Rect cell = { CellX, CellY, CardWidth, CardHeight }; - for (uint i = Cell1; i <= Cell4; ++i) { - stackRects[i] = cell; - layoutStack(&layout, &cell, &stacks[i], &Flat); - cell.x += CardWidth; - } - - SDL_Rect found = { FoundationX, FoundationY, CardWidth, CardHeight }; - for (uint i = Foundation1; i <= Foundation4; ++i) { - stackRects[i] = found; - layoutStack(&layout, &found, &stacks[i], &Flat); - found.x += CardWidth; - } - - SDL_Rect table = { TableauX, TableauY, CardWidth, CardHeight }; - for (uint i = Tableau1; i <= Tableau8; ++i) { - stackRects[i] = table; - stackRects[i].h = WindowHeight; - SDL_Rect rect = table; - layoutStack(&layout, &rect, &stacks[i], &FanDown); - table.x += CardWidth + StackMarginX; - } -} - -static void revealRank(Card rank) { - for (uint i = 0; i < layout.main.len; ++i) { - struct Item item = layout.main.items[i]; - if (cardRank(item.card) != rank) continue; - listPush(&reveal, &item.rect, item.card); - } -} - -static bool keyDown(SDL_KeyboardEvent key) { - switch (key.keysym.sym) { - case SDLK_F2: gameDeal(); return true; - case SDLK_BACKSPACE: return gameUndo(); - } - if (key.repeat) return false; - switch (key.keysym.sym) { - break; case SDLK_a: revealRank(Cards_A); - break; case SDLK_2: revealRank(Cards_2); - break; case SDLK_3: revealRank(Cards_3); - break; case SDLK_4: revealRank(Cards_4); - break; case SDLK_5: revealRank(Cards_5); - break; case SDLK_6: revealRank(Cards_6); - break; case SDLK_7: revealRank(Cards_7); - break; case SDLK_8: revealRank(Cards_8); - break; case SDLK_9: revealRank(Cards_9); - break; case SDLK_1: revealRank(Cards_10); - break; case SDLK_0: revealRank(Cards_10); - break; case SDLK_j: revealRank(Cards_J); - break; case SDLK_q: revealRank(Cards_Q); - break; case SDLK_k: revealRank(Cards_K); - break; default: return false; - } - return true; -} - -static bool keyUp(SDL_KeyboardEvent key) { - (void)key; - if (!reveal.len) return false; - listClear(&reveal); - return true; -} - -static bool mouseButtonDown(SDL_MouseButtonEvent button) { - if (button.button != SDL_BUTTON_RIGHT) return false; - struct SDL_Point point = { button.x, button.y }; - struct Item *item = listFind(&layout.main, &point); - if (!item) return false; - listPush(&reveal, &item->rect, item->card); - return true; -} - -static bool mouseButtonUp(SDL_MouseButtonEvent button) { - struct SDL_Point point = { button.x, button.y }; - - if (button.button == SDL_BUTTON_RIGHT) { - if (!reveal.len) return false; - listClear(&reveal); - return true; - } - - if (button.clicks % 2 == 0) { - Card card = layout.dragItem.card; - if (!card) { - struct Item *item = listFind(&layout.main, &point); - if (!item) return false; - card = item->card; - } - if (!gameAvail(card)) return false; - for (uint dst = Cell1; dst <= Cell4; ++dst) { - if (gameMove(dst, card)) break; - } - layout.dragItem.card = 0; - return true; - } - - if (layout.dragItem.card) { - for (uint dst = 0; dst < StacksLen; ++dst) { - if (SDL_PointInRect(&point, &stackRects[dst])) { - if (gameMove(dst, layout.dragItem.card)) break; - } - } - layout.dragItem.card = 0; - return true; - } - - struct Item *item = listFind(&layout.main, &point); - if (!item) return false; - if (!gameAvail(item->card)) return false; - layout.dragItem = *item; - return true; -} - -static SDL_Renderer *render; - -static void renderOutline(SDL_Rect rect, bool out) { - int right = rect.x + rect.w - 1; - int bottom = rect.y + rect.h - 1; - SDL_Point topLeft[3] = { - { rect.x, bottom - 1 }, - { rect.x, rect.y }, - { right - 1, rect.y }, - }; - SDL_Point bottomRight[3] = { - { rect.x + 1, bottom }, - { right, bottom }, - { right, rect.y + 1 }, - }; - SDL_SetRenderDrawColor(render, 0x00, 0x00, 0x00, 0xFF); - SDL_RenderDrawLines(render, out ? bottomRight : topLeft, 3); - SDL_SetRenderDrawColor(render, 0x00, 0xFF, 0x00, 0xFF); - SDL_RenderDrawLines(render, out ? topLeft : bottomRight, 3); -} - -static void renderOutlines(void) { - for (uint i = Foundation1; i <= Cell4; ++i) { - renderOutline(stackRects[i], false); - } -} - -static void renderKing(SDL_Texture *textures[]) { - SDL_Rect box = { KingX, KingY, KingWidth, KingHeight }; - renderOutline(box, true); - if (gameWin()) { - SDL_Rect king = { KingWinX, KingWinY, KingWinWidth, KingWinHeight }; - SDL_RenderCopy(render, textures[Cards_KingWin], NULL, &king); - } else { - SDL_Rect king = { - KingX + KingPadX, KingY + KingPadY, - Cards_KingWidth, Cards_KingHeight, - }; - SDL_RenderCopy(render, textures[kingFace], NULL, &king); - } -} - -static void renderList(SDL_Texture *textures[], const struct List *list) { - for (uint i = 0; i < list->len; ++i) { - SDL_RenderCopy( - render, textures[list->items[i].card], - NULL, &list->items[i].rect - ); - } -} - -static void err(const char *title) { - int error = SDL_ShowSimpleMessageBox( - SDL_MESSAGEBOX_ERROR, title, SDL_GetError(), NULL - ); - if (error) fprintf(stderr, "%s\n", SDL_GetError()); - exit(EXIT_FAILURE); -} - -int main(void) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) err("SDL_Init"); - atexit(SDL_Quit); - - struct Paths paths; - if (assetPaths(&paths) < 0) err("SDL_GetPrefPath"); - - bool kings = false; - struct { - SDL_Surface *cards[Cards_Empty]; - SDL_Surface *kings[Cards_FreeCellCount]; - } surfaces; - - SDL_RWops *rw = assetOpenCards(&paths); - if (!rw) return EXIT_FAILURE; - int error = Cards_LoadCards( - surfaces.cards, Cards_Empty, - rw, Cards_AlphaCorners | Cards_BlackBorders - ); - if (error) err("Cards_LoadCards"); - SDL_RWclose(rw); - - rw = assetOpenFreeCell(&paths); - if (rw) { - kings = true; - int error = Cards_LoadFreeCell( - surfaces.kings, Cards_FreeCellCount, - rw, Cards_ColorKey - ); - if (error) err("Cards_LoadFreeCell"); - SDL_RWclose(rw); - } - - SDL_Window *window; - error = SDL_CreateWindowAndRenderer( - WindowWidth, WindowHeight, SDL_WINDOW_ALLOW_HIGHDPI, - &window, &render - ); - if (error) err("SDL_CreateWindowAndRenderer"); - SDL_SetWindowTitle(window, "FreeCell"); - - SDL_RenderSetIntegerScale(render, SDL_TRUE); - SDL_RenderSetLogicalSize(render, WindowWidth, WindowHeight); - - SDL_Texture *textures[Cards_Empty] = {0}; - SDL_Texture *inverted[Cards_Empty] = {0}; - SDL_Texture *kingTextures[Cards_FreeCellCount] = {0}; - - for (uint i = 0; i < Cards_Empty; ++i) { - if (!surfaces.cards[i]) continue; - - textures[i] = SDL_CreateTextureFromSurface(render, surfaces.cards[i]); - if (!textures[i]) err("SDL_CreateTextureFromSurface"); - - error = Cards_InvertSurface(surfaces.cards[i]); - if (error) err("Cards_InvertSurface"); - - inverted[i] = SDL_CreateTextureFromSurface(render, surfaces.cards[i]); - if (!inverted[i]) err("SDL_CreateTextureFromSurface"); - - SDL_FreeSurface(surfaces.cards[i]); - } - - if (kings) { - for (uint i = 0; i < Cards_FreeCellCount; ++i) { - if (!surfaces.kings[i]) continue; - kingTextures[i] = - SDL_CreateTextureFromSurface(render, surfaces.kings[i]); - if (!kingTextures[i]) err("SDL_CreateTextureFromSurface"); - SDL_FreeSurface(surfaces.kings[i]); - } - } - - srand(time(NULL)); - gameDeal(); - - for (;;) { - updateLayout(); - - SDL_SetRenderDrawColor(render, 0x00, 0xAA, 0x55, 0xFF); - SDL_RenderClear(render); - renderOutlines(); - if (kings) renderKing(kingTextures); - renderList(textures, &layout.main); - renderList(inverted, &layout.drag); - renderList(textures, &reveal); - SDL_RenderPresent(render); - - // TODO: Add more than a frame delay between automatic moves? - if (gameAuto()) continue; - - SDL_Event event; - for (;;) { - SDL_WaitEvent(&event); - if (event.type == SDL_QUIT) { - goto quit; - } else if (event.type == SDL_KEYDOWN) { - if (keyDown(event.key)) break; - } else if (event.type == SDL_KEYUP) { - if (keyUp(event.key)) break; - } else if (event.type == SDL_MOUSEBUTTONDOWN) { - if (mouseButtonDown(event.button)) break; - } else if (event.type == SDL_MOUSEBUTTONUP) { - if (mouseButtonUp(event.button)) break; - } - } - } - -quit: - SDL_Quit(); -} diff --git a/layout.h b/layout.h deleted file mode 100644 index cbff6df..0000000 --- a/layout.h +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef LAYOUT_H -#define LAYOUT_H - -#include <SDL_rect.h> -#include <assert.h> - -#include "stack.h" - -#ifndef LAYOUT_CAP -#define LAYOUT_CAP 64 -#endif - -typedef unsigned uint; - -struct Item { - SDL_Rect rect; - Card card; -}; - -struct List { - uint len; - struct Item items[LAYOUT_CAP]; -}; - -static inline void listClear(struct List *list) { - list->len = 0; -} - -static inline void -listPush(struct List *list, const struct SDL_Rect *rect, Card card) { - assert(list->len < LAYOUT_CAP); - struct Item item = { *rect, card }; - list->items[list->len++] = item; -} - -static inline struct Item *listFind(struct List *list, const SDL_Point *point) { - for (uint i = list->len - 1; i < list->len; --i) { - if (SDL_PointInRect(point, &list->items[i].rect)) { - return &list->items[i]; - } - } - return NULL; -} - -struct Layout { - struct List main; - struct List drag; - struct Item dragItem; -}; - -static inline void layoutClear(struct Layout *layout) { - listClear(&layout->main); - listClear(&layout->drag); -} - -struct Style { - uint increment; - int deltaXBack; - int deltaYBack; - int deltaXFront; - int deltaYFront; -}; - -enum { - SquareIncrement = 24 / 3, - SquareDeltaX = 2, - SquareDeltaY = 1, -}; -static const struct Style Flat = { 1, 0, 0, 0, 0 }; -static const struct Style Square = { - SquareIncrement, SquareDeltaX, SquareDeltaY, SquareDeltaX, SquareDeltaY, -}; - -static inline void -layoutStack( - struct Layout *layout, SDL_Rect *rect, - const struct Stack *stack, const struct Style *style -) { - struct List *list = &layout->main; - for (uint i = 0; i < stack->len; ++i) { - Card card = stack->cards[i]; - if (card == layout->dragItem.card) { - list = &layout->drag; - *rect = layout->dragItem.rect; - } - listPush(list, rect, card); - if ((i + 1) % style->increment == 0) { - rect->x += (card > 0 ? style->deltaXFront : style->deltaXBack); - rect->y += (card > 0 ? style->deltaYFront : style->deltaYBack); - } - } -} - -#endif diff --git a/sol.c b/sol.c deleted file mode 100644 index cf643f2..0000000 --- a/sol.c +++ /dev/null @@ -1,393 +0,0 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <SDL.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> - -#include "asset.h" -#include "cards.h" -#include "layout.h" -#include "stack.h" - -#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) - -enum { - Stock, - Waste1, - Waste3, - Foundation1, - Foundation2, - Foundation3, - Foundation4, - Tableau1, - Tableau2, - Tableau3, - Tableau4, - Tableau5, - Tableau6, - Tableau7, - StacksLen, -}; - -static struct Stack stacks[StacksLen]; - -// TODO: Scoring method, score, time. -static struct { - uint draw; -} game = { - .draw = 3, -}; - -typedef void StackFn(struct Stack *dst, struct Stack *src, Uint8 len); - -static struct { - StackFn *fn; - uint dst; - uint src; - uint len; -} undo; - -static void gameDo(StackFn *fn, uint dst, uint src, uint len) { - undo.fn = fn; - undo.dst = src; - undo.src = dst; - undo.len = len; - fn(&stacks[dst], &stacks[src], len); -} - -static bool gameUndo(void) { - if (!undo.fn) return false; - undo.fn(&stacks[undo.dst], &stacks[undo.src], undo.len); - undo.fn = NULL; - return true; -} - -static void gameDeal(void) { - undo.fn = NULL; - for (uint i = 0; i < StacksLen; ++i) { - stackClear(&stacks[i]); - } - for (Card i = 1; i <= 52; ++i) { - stackPush(&stacks[Stock], -i); - } - stackShuffle(&stacks[Stock]); - for (uint i = Tableau1; i <= Tableau7; ++i) { - stackMoveTo(&stacks[i], &stacks[Stock], i - Tableau1); - stackFlipTo(&stacks[i], &stacks[Stock], 1); - } -} - -static void gameDraw(void) { - stackMoveTo(&stacks[Waste1], &stacks[Waste3], stacks[Waste3].len); - if (stacks[Stock].len) { - if (game.draw > 1) { - gameDo(stackFlipTo, Waste3, Stock, game.draw); - } else { - gameDo(stackFlipTo, Waste1, Stock, 1); - } - } else { - gameDo(stackFlipTo, Stock, Waste1, stacks[Waste1].len); - } -} - -static bool gameFind(uint *stack, uint *index, Card 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(Card card) { - uint stack, index; - if (card < 0) return false; - if (!gameFind(&stack, &index, card)) return false; - - bool top = (card == stackTop(&stacks[stack])); - 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(Card card) { - uint stack, index; - if (!gameFind(&stack, &index, card)) return false; - if (stack < Tableau1 || stack > Tableau7) return false; - if (card != stackTop(&stacks[stack])) return false; - if (card > 0) return false; - gameDo(stackFlipTo, stack, stack, 1); - return true; -} - -static bool gameMove(uint dest, Card card) { - uint source, index; - if (!gameFind(&source, &index, card)) return false; - if (source == dest) return false; - - uint count = stacks[source].len - index; - Card 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; - gameDo(stackMoveTo, dest, 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; - gameDo(stackMoveTo, dest, source, count); - return true; - } - - return false; -} - -enum { - CardWidth = Cards_CardWidth, - CardHeight = Cards_CardHeight, - - StackMarginX = 11, - StackMarginY = 6, - - StockX = StackMarginX, - StockY = StackMarginY, - - WasteX = StockX + CardWidth + StackMarginX, - WasteY = StackMarginY, - - FoundationX = WasteX + 2 * (CardWidth + StackMarginX), - FoundationY = StackMarginY, - - TableauX = StackMarginX, - TableauY = StockY + CardHeight + StackMarginY, - - FanRightDeltaX = 14, - FanDownDeltaYBack = 3, - FanDownDeltaYFront = 15, - - WindowWidth = 7 * CardWidth + 8 * StackMarginX, - WindowHeight = 2 * (StackMarginY + CardHeight) - + 6 * FanDownDeltaYBack - + 12 * FanDownDeltaYFront - + StackMarginY, -}; - -static const struct Style FanRight = { - 1, 0, 0, FanRightDeltaX, SquareDeltaY -}; -static const struct Style FanDown = { - 1, 0, FanDownDeltaYBack, 0, FanDownDeltaYFront -}; - -static struct SDL_Rect stackRects[StacksLen]; -static struct Layout layout; -static uint backTexture = Cards_Back1; - -static void updateLayout(void) { - layoutClear(&layout); - - SDL_Rect stock = { StockX, StockY, CardWidth, CardHeight }; - stackRects[Stock] = stock; - listPush(&layout.main, &stock, Cards_O); - layoutStack(&layout, &stock, &stacks[Stock], &Square); - - SDL_Rect waste = { WasteX, WasteY, CardWidth, CardHeight }; - layoutStack(&layout, &waste, &stacks[Waste1], &Square); - layoutStack(&layout, &waste, &stacks[Waste3], &FanRight); - - SDL_Rect found = { FoundationX, FoundationY, CardWidth, CardHeight }; - for (uint i = Foundation1; i <= Foundation4; ++i) { - stackRects[i] = found; - listPush(&layout.main, &found, Cards_Empty); - SDL_Rect rect = found; - layoutStack(&layout, &rect, &stacks[i], &Square); - found.x += CardWidth + StackMarginX; - } - - SDL_Rect table = { TableauX, TableauY, CardWidth, CardHeight }; - for (uint i = Tableau1; i <= Tableau7; ++i) { - stackRects[i] = table; - stackRects[i].h = WindowHeight; - SDL_Rect rect = table; - layoutStack(&layout, &rect, &stacks[i], &FanDown); - table.x += CardWidth + StackMarginX; - } -} - -static bool keyDown(SDL_KeyboardEvent key) { - switch (key.keysym.sym) { - case SDLK_F2: gameDeal(); return true; - case SDLK_BACKSPACE: return gameUndo(); - case SDLK_b: { - backTexture -= Cards_Back1; - backTexture += 1; - backTexture %= 12; - backTexture += Cards_Back1; - return true; - } - case SDLK_d: { - game.draw = (game.draw == 1 ? 3 : 1); - gameDeal(); - return true; - } - default: return false; - } -} - -static bool mouseButtonDown(SDL_MouseButtonEvent button) { - struct SDL_Point point = { button.x, button.y }; - if (SDL_PointInRect(&point, &stackRects[Stock])) { - gameDraw(); - return true; - } - struct Item *item = listFind(&layout.main, &point); - if (!item) return false; - if (!gameAvail(item->card)) return gameReveal(item->card); - if (button.clicks % 2 == 0) { - 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, &stackRects[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_CardCount]; - -static void renderList(const struct List *list) { - for (uint i = 0; i < list->len; ++i) { - int tex = list->items[i].card; - if (tex < 0) tex = backTexture; - SDL_RenderCopy(render, textures[tex], NULL, &list->items[i].rect); - } -} - -static void err(const char *title) { - int error = SDL_ShowSimpleMessageBox( - SDL_MESSAGEBOX_ERROR, title, SDL_GetError(), NULL - ); - if (error) fprintf(stderr, "%s\n", SDL_GetError()); - exit(EXIT_FAILURE); -} - -int main(void) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) err("SDL_Init"); - atexit(SDL_Quit); - - struct Paths paths; - if (assetPaths(&paths) < 0) err("SDL_GetPrefPath"); - - SDL_RWops *rw = assetOpenCards(&paths); - if (!rw) return EXIT_FAILURE; - - SDL_Surface *surfaces[Cards_CardCount]; - int error = Cards_LoadCards( - surfaces, Cards_CardCount, - rw, Cards_ColorKey | Cards_AlphaCorners | Cards_BlackBorders - ); - if (error) err("Cards_LoadCards"); - SDL_RWclose(rw); - - SDL_Window *window; - error = SDL_CreateWindowAndRenderer( - WindowWidth, WindowHeight, SDL_WINDOW_ALLOW_HIGHDPI, - &window, &render - ); - if (error) err("SDL_CreateWindowAndRenderer"); - SDL_SetWindowTitle(window, "Solitaire"); - - SDL_RenderSetIntegerScale(render, SDL_TRUE); - SDL_RenderSetLogicalSize(render, WindowWidth, WindowHeight); - - for (uint i = 0; i < Cards_CardCount; ++i) { - textures[i] = NULL; - if (!surfaces[i]) continue; - textures[i] = SDL_CreateTextureFromSurface(render, surfaces[i]); - if (!textures[i]) err("SDL_CreateTextureFromSurface"); - SDL_FreeSurface(surfaces[i]); - } - - srand(time(NULL)); - backTexture = Cards_Back1 + randUniform(12); - gameDeal(); - - for (;;) { - updateLayout(); - - SDL_SetRenderDrawColor(render, 0x00, 0xAA, 0x55, 0xFF); - SDL_RenderClear(render); - 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_KEYDOWN) { - if (keyDown(event.key)) break; - } 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: - SDL_Quit(); -} diff --git a/stack.h b/stack.h deleted file mode 100644 index f5140df..0000000 --- a/stack.h +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef STACK_H -#define STACK_H - -#include <SDL_stdinc.h> -#include <assert.h> -#include <stdlib.h> - -#include "cards.h" - -#ifndef STACK_CAP -#define STACK_CAP 52 -#endif - -typedef Sint8 Card; - -static inline int cardSuit(Card 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 inline int cardColor(Card card) { - return cardSuit(card) == Cards_Diamond || cardSuit(card) == Cards_Heart; -} - -static inline int cardRank(Card card) { - return abs(card) - cardSuit(card); -} - -struct Stack { - Uint8 len; - Card cards[STACK_CAP]; -}; - -static inline void stackClear(struct Stack *stack) { - stack->len = 0; -} - -static inline void stackPush(struct Stack *stack, Card card) { - assert(stack->len < STACK_CAP); - stack->cards[stack->len++] = card; -} - -static inline Card stackPop(struct Stack *stack) { - if (!stack->len) return 0; - return stack->cards[--stack->len]; -} - -static inline Card stackTop(const struct Stack *stack) { - if (!stack->len) return 0; - return stack->cards[stack->len - 1]; -} - -static inline void stackFlipTo(struct Stack *dst, struct Stack *src, Uint8 n) { - if (n > src->len) n = src->len; - for (Uint8 i = 0; i < n; ++i) { - stackPush(dst, -stackPop(src)); - } -} - -static inline void stackMoveTo(struct Stack *dst, struct Stack *src, Uint8 n) { - if (n > src->len) n = src->len; - for (Uint8 i = 0; i < n; ++i) { - stackPush(dst, src->cards[src->len - n + i]); - } - src->len -= n; -} - -static inline int randUniform(int bound) { - for (;;) { - int r = rand(); - if (r >= RAND_MAX % bound) return r % bound; - } -} - -static inline void stackShuffle(struct Stack *stack) { - for (Uint8 i = stack->len - 1; i > 0; --i) { - Uint8 j = randUniform(i + 1); - Card x = stack->cards[i]; - stack->cards[i] = stack->cards[j]; - stack->cards[j] = x; - } -} - -#endif |