diff options
Diffstat (limited to '')
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | sol.c | 254 | ||||
-rw-r--r-- | stack.h | 74 |
4 files changed, 336 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore index 986bf84..b755818 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ CARDS.DLL SOL.EXE demo dump +sol diff --git a/Makefile b/Makefile index e6c8651..59dea6a 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,8 @@ LDLIBS = -lSDL2 -include config.mk -BINS = demo dump -OBJS = cards.o demo.o dump.o +BINS = demo dump sol +OBJS = cards.o demo.o dump.o sol.o all: $(BINS) @@ -18,7 +18,12 @@ demo: cards.o demo.o dump: cards.o dump.o $(CC) $(LDFLAGS) cards.o dump.o $(LDLIBS) -o dump +sol: cards.o sol.o + $(CC) $(LDFLAGS) cards.o sol.o $(LDLIBS) -o sol + $(OBJS): cards.h +sol.o: stack.h + clean: rm -f $(BINS) $(OBJS) 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(); +} diff --git a/stack.h b/stack.h new file mode 100644 index 0000000..46b9091 --- /dev/null +++ b/stack.h @@ -0,0 +1,74 @@ +/* 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> + +struct Stack { + Uint8 len; + Sint8 cards[52]; +}; + +static inline void stackClear(struct Stack *stack) { + stack->len = 0; +} + +static inline void stackPush(struct Stack *stack, Sint8 card) { + assert(stack->len < 52); + stack->cards[stack->len++] = card; +} + +static inline Sint8 stackPop(struct Stack *stack) { + if (!stack->len) return 0; + return stack->cards[--stack->len]; +} + +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 void stackDeck(struct Stack *stack) { + stackClear(stack); + for (Sint8 i = 1; i <= 52; ++i) { + stackPush(stack, -i); + } +} + +// FIXME: Use a portable random. +static inline void stackShuffle(struct Stack *stack) { + for (Uint8 i = stack->len - 1; i > 0; --i) { + Uint8 j = arc4random_uniform(i + 1); + Sint8 x = stack->cards[i]; + stack->cards[i] = stack->cards[j]; + stack->cards[j] = x; + } +} + +#endif |