about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-03-20 00:56:33 -0400
committerJune McEnroe <june@causal.agency>2019-03-20 00:56:33 -0400
commitd0e804ede2f1e86c3c23b129dd8533aa3971a878 (patch)
tree4850c828ea1394c2ef942de8a703b8e8bffde3cb
parentOnly check errors from create calls (diff)
downloadcards-d0e804ede2f1e86c3c23b129dd8533aa3971a878.tar.gz
cards-d0e804ede2f1e86c3c23b129dd8533aa3971a878.zip
Add WIP sol.c
-rw-r--r--.gitignore1
-rw-r--r--Makefile9
-rw-r--r--sol.c254
-rw-r--r--stack.h74
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