diff options
Diffstat (limited to 'sol.c')
-rw-r--r-- | sol.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/sol.c b/sol.c new file mode 100644 index 0000000..3a289b9 --- /dev/null +++ b/sol.c @@ -0,0 +1,254 @@ +/* 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 <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "cards.h" +#include "stack.h" + +typedef unsigned uint; + +enum { + Stock, + Waste1, + Waste3, + Foundation1, + Foundation2, + Foundation3, + Foundation4, + Tableau1, + Tableau2, + Tableau3, + Tableau4, + Tableau5, + Tableau6, + Tableau7, + StacksLen, +}; + +static struct Stack stacks[StacksLen]; + +static void restock(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); + } +} + +static void draw(void) { + stackMoveTo(&stacks[Waste1], &stacks[Waste3], stacks[Waste3].len); + if (stacks[Stock].len) { + stackFlipTo(&stacks[Waste3], &stacks[Stock], 3); + } else { + stackFlipTo(&stacks[Stock], &stacks[Waste1], stacks[Waste1].len); + } +} + +enum { + StackMarginX = 11, + StackMarginY = 6, + + StockX = StackMarginX, + StockY = StackMarginY, + + WasteX = StockX + Cards_Width + StackMarginX, + WasteY = StackMarginY, + + FoundationX = WasteX + 2 * (Cards_Width + StackMarginX), + FoundationY = StackMarginY, + + TableauX = StackMarginX, + TableauY = StockY + Cards_Height + StackMarginY, + + StackDeltaX = 2, + StackDeltaY = 1, + + Waste3DeltaX = 14, + Waste3DeltaY = StackDeltaY, + + TableauDeltaX = 0, + TableauDeltaYBack = 3, + TableauDeltaYFront = 15, + + WindowWidth = 7 * Cards_Width + 8 * StackMarginX, + WindowHeight = 2 * (StackMarginY + Cards_Height) + + 6 * TableauDeltaYBack + + 12 * TableauDeltaYFront + + StackMarginY, +}; + +static SDL_Renderer *render; +static SDL_Texture *textures[Cards_Count]; + +static uint cardBack = Cards_Back1; + +static void renderCard(const SDL_Rect *rect, Sint8 card) { + uint i = (card > 0 ? card : cardBack); + SDL_RenderCopy(render, textures[i], NULL, rect); +} + +static void renderStack(SDL_Rect *rect, const struct Stack *stack, uint depth) { + for (uint i = 0; i < stack->len; ++i) { + renderCard(rect, stack->cards[i]); + if (i > 0 && i % (depth / 3) == 0) { + rect->x += StackDeltaX; + rect->y += StackDeltaY; + } + } +} + +static void renderStock(void) { + SDL_Rect rect = { StockX, StockY, Cards_Width, Cards_Height }; + SDL_RenderCopy(render, textures[Cards_O], NULL, &rect); + renderStack(&rect, &stacks[Stock], 24); +} + +static void renderWaste(void) { + SDL_Rect rect = { WasteX, WasteY, Cards_Width, Cards_Height }; + renderStack(&rect, &stacks[Waste1], 24); + for (uint i = 0; i < stacks[Waste3].len; ++i) { + renderCard(&rect, stacks[Waste3].cards[i]); + rect.x += Waste3DeltaX; + rect.y += Waste3DeltaY; + } +} + +static void renderFoundations(void) { + SDL_Rect base = { FoundationX, FoundationY, Cards_Width, Cards_Height }; + for (uint i = Foundation1; i <= Foundation4; ++i) { + SDL_Rect rect = base; + SDL_RenderCopy(render, textures[Cards_Empty], NULL, &rect); + renderStack(&rect, &stacks[i], 13); + base.x += Cards_Width + StackMarginX; + } +} + +static void renderTableau(void) { + SDL_Rect base = { TableauX, TableauY, Cards_Width, Cards_Height }; + for (uint i = Tableau1; i <= Tableau7; ++i) { + SDL_Rect rect = base; + for (uint j = 0; j < stacks[i].len; ++j) { + Sint8 card = stacks[i].cards[j]; + renderCard(&rect, card); + rect.x += TableauDeltaX; + rect.y += (card > 0 ? TableauDeltaYFront : TableauDeltaYBack); + } + base.x += Cards_Width + StackMarginX; + } +} + +static void err(const char *prefix) { + fprintf(stderr, "%s: %s\n", prefix, SDL_GetError()); + exit(EXIT_FAILURE); +} + +int main(void) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) err("SDL_Init"); + atexit(SDL_Quit); + + SDL_Window *window; + int error = SDL_CreateWindowAndRenderer( + WindowWidth, WindowHeight, SDL_WINDOW_ALLOW_HIGHDPI, + &window, &render + ); + if (error) err("SDL_CreateWindowAndRenderer"); + + SDL_SetWindowTitle(window, "Solitaire"); + + int width, height; + SDL_GetRendererOutputSize(render, &width, &height); + if (width > WindowWidth && height > WindowHeight) { + SDL_RenderSetIntegerScale(render, SDL_TRUE); + SDL_RenderSetScale( + render, + (float)width / WindowWidth, + (float)height / WindowHeight + ); + } + + // TODO: Path search/option. + SDL_RWops *rw = SDL_RWFromFile("CARDS.DLL", "rb"); + if (!rw) err("CARDS.DLL"); + + struct Cards *cards = Cards_Load( + rw, Cards_ColorKey | Cards_AlphaCorners | Cards_BlackBorders + ); + if (!cards) err("Cards_Load"); + SDL_RWclose(rw); + + for (uint i = 0; i < Cards_Count; ++i) { + textures[i] = NULL; + if (!cards->surfaces[i]) continue; + textures[i] = SDL_CreateTextureFromSurface(render, cards->surfaces[i]); + if (!textures[i]) err("SDL_CreateTextureFromSurface"); + } + 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: { + cardBack = Cards_Back1 + (cardBack - Cards_Back1 + 1) % 12; + } + } + } + + SDL_SetRenderDrawColor(render, 0x00, 0xAA, 0x55, 0xFF); + SDL_RenderClear(render); + + renderStock(); + renderWaste(); + renderFoundations(); + renderTableau(); + + SDL_RenderPresent(render); + } + +quit: + SDL_Quit(); +} |