diff options
Diffstat (limited to '')
-rw-r--r-- | sol.c | 393 |
1 files changed, 0 insertions, 393 deletions
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(); -} |