summary refs log tree commit diff
path: root/sol.c
diff options
context:
space:
mode:
Diffstat (limited to 'sol.c')
-rw-r--r--sol.c254
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();
+}