summary refs log tree commit diff
path: root/freecell.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--freecell.c484
1 files changed, 0 insertions, 484 deletions
diff --git a/freecell.c b/freecell.c
deleted file mode 100644
index b7f6378..0000000
--- a/freecell.c
+++ /dev/null
@@ -1,484 +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"
-
-enum {
-	Foundation1,
-	Foundation2,
-	Foundation3,
-	Foundation4,
-	Cell1,
-	Cell2,
-	Cell3,
-	Cell4,
-	Tableau1,
-	Tableau2,
-	Tableau3,
-	Tableau4,
-	Tableau5,
-	Tableau6,
-	Tableau7,
-	Tableau8,
-	StacksLen,
-};
-
-static struct Stack stacks[StacksLen];
-
-static struct {
-	bool avail;
-	uint dst;
-	uint src;
-} undo;
-
-static uint kingFace = Cards_KingRight;
-
-static void gameDeal(void) {
-	for (uint i = 0; i < StacksLen; ++i) {
-		stackClear(&stacks[i]);
-	}
-	struct Stack deck = {0};
-	for (Card i = 1; i <= 52; ++i) {
-		stackPush(&deck, i);
-	}
-	stackShuffle(&deck);
-	for (uint i = Tableau1; i <= Tableau8; ++i) {
-		stackMoveTo(&stacks[i], &deck, (i < Tableau5 ? 7 : 6));
-	}
-	undo.avail = false;
-	kingFace = Cards_KingRight;
-}
-
-static bool gameWin(void) {
-	for (uint i = Foundation1; i <= Foundation4; ++i) {
-		if (stacks[i].len != 13) return false;
-	}
-	return true;
-}
-
-static bool gameFind(uint *stack, Card card) {
-	for (*stack = 0; *stack < StacksLen; ++*stack) {
-		for (uint i = 0; i < stacks[*stack].len; ++i) {
-			if (stacks[*stack].cards[i] == card) return true;
-		}
-	}
-	return false;
-}
-
-static bool gameAvail(Card card) {
-	uint stack;
-	assert(gameFind(&stack, card));
-	if (stack <= Foundation4) return false;
-	return card == stackTop(&stacks[stack]);
-}
-
-static bool gameMove(uint dst, Card card) {
-	uint src;
-	assert(gameFind(&src, card));
-	Card top = stackTop(&stacks[dst]);
-
-	if (src == dst) return false;
-	if (dst >= Cell1 && dst <= Cell4) {
-		if (stacks[dst].len) return false;
-		kingFace = Cards_KingLeft;
-	}
-	if (dst >= Foundation1 && dst <= Foundation4) {
-		if (!top && cardRank(card) != Cards_A) return false;
-		if (top && cardSuit(card) != cardSuit(top)) return false;
-		if (top && cardRank(card) != cardRank(top) + 1) return false;
-		kingFace = Cards_KingRight;
-	}
-	if (dst >= Tableau1 && dst <= Tableau8) {
-		if (top && cardColor(card) == cardColor(top)) return false;
-		if (top && cardRank(card) != cardRank(top) - 1) return false;
-	}
-
-	undo.dst = src;
-	undo.src = dst;
-	undo.avail = true;
-	stackPush(&stacks[dst], stackPop(&stacks[src]));
-	return true;
-}
-
-static bool gameUndo(void) {
-	if (!undo.avail) return false;
-	stackPush(&stacks[undo.dst], stackPop(&stacks[undo.src]));
-	undo.avail = false;
-	return true;
-}
-
-static bool gameAuto(void) {
-	Card min[2] = { Cards_K, Cards_K };
-	for (uint i = Cell1; i <= Tableau8; ++i) {
-		for (uint j = 0; j < stacks[i].len; ++j) {
-			Card card = stacks[i].cards[j];
-			if (cardRank(card) < min[cardColor(card)]) {
-				min[cardColor(card)] = cardRank(card);
-			}
-		}
-	}
-	for (uint i = Cell1; i <= Tableau8; ++i) {
-		Card card = stackTop(&stacks[i]);
-		if (!card) continue;
-		if (cardRank(card) > Cards_2) {
-			if (min[!cardColor(card)] < cardRank(card)) continue;
-		}
-		for (uint dst = Foundation1; dst <= Foundation4; ++dst) {
-			if (gameMove(dst, card)) return true;
-		}
-	}
-	return false;
-}
-
-enum {
-	CardWidth = Cards_CardWidth,
-	CardHeight = Cards_CardHeight,
-
-	CellX = 0,
-	CellY = 0,
-
-	KingMarginX = 13,
-	KingX = CellX + 4 * CardWidth + KingMarginX,
-	KingY = 18,
-	KingPadX = 3,
-	KingPadY = 3,
-	KingWidth = Cards_KingWidth + 2 * KingPadX,
-	KingHeight = Cards_KingHeight + 2 * KingPadY,
-
-	FoundationX = KingX + KingWidth + KingMarginX,
-	FoundationY = CellY,
-
-	StackMarginX = 7,
-	StackMarginY = 10,
-
-	TableauX = StackMarginX,
-	TableauY = CellY + CardHeight + StackMarginY,
-	FanDownDeltaY = 17,
-
-	KingWinMarginX = 10,
-	KingWinMarginY = 10,
-	KingWinX = KingWinMarginX,
-	KingWinY = CellY + CardHeight + KingWinMarginY,
-	KingWinWidth = 320,
-	KingWinHeight = 320,
-
-	WindowWidth = 8 * CardWidth + 9 * StackMarginX + 1,
-	WindowHeight = KingWinY + KingWinHeight + KingWinMarginY,
-};
-
-static const struct Style FanDown = { 1, 0, 0, 0, FanDownDeltaY };
-
-static struct SDL_Rect stackRects[StacksLen];
-static struct Layout layout;
-static struct List reveal;
-
-static void updateLayout(void) {
-	layoutClear(&layout);
-
-	SDL_Rect cell = { CellX, CellY, CardWidth, CardHeight };
-	for (uint i = Cell1; i <= Cell4; ++i) {
-		stackRects[i] = cell;
-		layoutStack(&layout, &cell, &stacks[i], &Flat);
-		cell.x += CardWidth;
-	}
-
-	SDL_Rect found = { FoundationX, FoundationY, CardWidth, CardHeight };
-	for (uint i = Foundation1; i <= Foundation4; ++i) {
-		stackRects[i] = found;
-		layoutStack(&layout, &found, &stacks[i], &Flat);
-		found.x += CardWidth;
-	}
-
-	SDL_Rect table = { TableauX, TableauY, CardWidth, CardHeight };
-	for (uint i = Tableau1; i <= Tableau8; ++i) {
-		stackRects[i] = table;
-		stackRects[i].h = WindowHeight;
-		SDL_Rect rect = table;
-		layoutStack(&layout, &rect, &stacks[i], &FanDown);
-		table.x += CardWidth + StackMarginX;
-	}
-}
-
-static void revealRank(Card rank) {
-	for (uint i = 0; i < layout.main.len; ++i) {
-		struct Item item = layout.main.items[i];
-		if (cardRank(item.card) != rank) continue;
-		listPush(&reveal, &item.rect, item.card);
-	}
-}
-
-static bool keyDown(SDL_KeyboardEvent key) {
-	switch (key.keysym.sym) {
-		case SDLK_F2: gameDeal(); return true;
-		case SDLK_BACKSPACE: return gameUndo();
-	}
-	if (key.repeat) return false;
-	switch (key.keysym.sym) {
-		break; case SDLK_a: revealRank(Cards_A);
-		break; case SDLK_2: revealRank(Cards_2);
-		break; case SDLK_3: revealRank(Cards_3);
-		break; case SDLK_4: revealRank(Cards_4);
-		break; case SDLK_5: revealRank(Cards_5);
-		break; case SDLK_6: revealRank(Cards_6);
-		break; case SDLK_7: revealRank(Cards_7);
-		break; case SDLK_8: revealRank(Cards_8);
-		break; case SDLK_9: revealRank(Cards_9);
-		break; case SDLK_1: revealRank(Cards_10);
-		break; case SDLK_0: revealRank(Cards_10);
-		break; case SDLK_j: revealRank(Cards_J);
-		break; case SDLK_q: revealRank(Cards_Q);
-		break; case SDLK_k: revealRank(Cards_K);
-		break; default: return false;
-	}
-	return true;
-}
-
-static bool keyUp(SDL_KeyboardEvent key) {
-	(void)key;
-	if (!reveal.len) return false;
-	listClear(&reveal);
-	return true;
-}
-
-static bool mouseButtonDown(SDL_MouseButtonEvent button) {
-	if (button.button != SDL_BUTTON_RIGHT) return false;
-	struct SDL_Point point = { button.x, button.y };
-	struct Item *item = listFind(&layout.main, &point);
-	if (!item) return false;
-	listPush(&reveal, &item->rect, item->card);
-	return true;
-}
-
-static bool mouseButtonUp(SDL_MouseButtonEvent button) {
-	struct SDL_Point point = { button.x, button.y };
-
-	if (button.button == SDL_BUTTON_RIGHT) {
-		if (!reveal.len) return false;
-		listClear(&reveal);
-		return true;
-	}
-
-	if (button.clicks % 2 == 0) {
-		Card card = layout.dragItem.card;
-		if (!card) {
-			struct Item *item = listFind(&layout.main, &point);
-			if (!item) return false;
-			card = item->card;
-		}
-		if (!gameAvail(card)) return false;
-		for (uint dst = Cell1; dst <= Cell4; ++dst) {
-			if (gameMove(dst, card)) break;
-		}
-		layout.dragItem.card = 0;
-		return true;
-	}
-
-	if (layout.dragItem.card) {
-		for (uint dst = 0; dst < StacksLen; ++dst) {
-			if (SDL_PointInRect(&point, &stackRects[dst])) {
-				if (gameMove(dst, layout.dragItem.card)) break;
-			}
-		}
-		layout.dragItem.card = 0;
-		return true;
-	}
-
-	struct Item *item = listFind(&layout.main, &point);
-	if (!item) return false;
-	if (!gameAvail(item->card)) return false;
-	layout.dragItem = *item;
-	return true;
-}
-
-static SDL_Renderer *render;
-
-static void renderOutline(SDL_Rect rect, bool out) {
-	int right = rect.x + rect.w - 1;
-	int bottom = rect.y + rect.h - 1;
-	SDL_Point topLeft[3] = {
-		{ rect.x, bottom - 1 },
-		{ rect.x, rect.y },
-		{ right - 1, rect.y },
-	};
-	SDL_Point bottomRight[3] = {
-		{ rect.x + 1, bottom },
-		{ right, bottom },
-		{ right, rect.y + 1 },
-	};
-	SDL_SetRenderDrawColor(render, 0x00, 0x00, 0x00, 0xFF);
-	SDL_RenderDrawLines(render, out ? bottomRight : topLeft, 3);
-	SDL_SetRenderDrawColor(render, 0x00, 0xFF, 0x00, 0xFF);
-	SDL_RenderDrawLines(render, out ? topLeft : bottomRight, 3);
-}
-
-static void renderOutlines(void) {
-	for (uint i = Foundation1; i <= Cell4; ++i) {
-		renderOutline(stackRects[i], false);
-	}
-}
-
-static void renderKing(SDL_Texture *textures[]) {
-	SDL_Rect box = { KingX, KingY, KingWidth, KingHeight };
-	renderOutline(box, true);
-	if (gameWin()) {
-		SDL_Rect king = { KingWinX, KingWinY, KingWinWidth, KingWinHeight };
-		SDL_RenderCopy(render, textures[Cards_KingWin], NULL, &king);
-	} else {
-		SDL_Rect king = {
-			KingX + KingPadX, KingY + KingPadY,
-			Cards_KingWidth, Cards_KingHeight,
-		};
-		SDL_RenderCopy(render, textures[kingFace], NULL, &king);
-	}
-}
-
-static void renderList(SDL_Texture *textures[], const struct List *list) {
-	for (uint i = 0; i < list->len; ++i) {
-		SDL_RenderCopy(
-			render, textures[list->items[i].card],
-			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");
-
-	bool kings = false;
-	struct {
-		SDL_Surface *cards[Cards_Empty];
-		SDL_Surface *kings[Cards_FreeCellCount];
-	} surfaces;
-
-	SDL_RWops *rw = assetOpenCards(&paths);
-	if (!rw) return EXIT_FAILURE;
-	int error = Cards_LoadCards(
-		surfaces.cards, Cards_Empty,
-		rw, Cards_AlphaCorners | Cards_BlackBorders
-	);
-	if (error) err("Cards_LoadCards");
-	SDL_RWclose(rw);
-
-	rw = assetOpenFreeCell(&paths);
-	if (rw) {
-		kings = true;
-		int error = Cards_LoadFreeCell(
-			surfaces.kings, Cards_FreeCellCount,
-			rw, Cards_ColorKey
-		);
-		if (error) err("Cards_LoadFreeCell");
-		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, "FreeCell");
-
-	SDL_RenderSetIntegerScale(render, SDL_TRUE);
-	SDL_RenderSetLogicalSize(render, WindowWidth, WindowHeight);
-
-	SDL_Texture *textures[Cards_Empty] = {0};
-	SDL_Texture *inverted[Cards_Empty] = {0};
-	SDL_Texture *kingTextures[Cards_FreeCellCount] = {0};
-
-	for (uint i = 0; i < Cards_Empty; ++i) {
-		if (!surfaces.cards[i]) continue;
-
-		textures[i] = SDL_CreateTextureFromSurface(render, surfaces.cards[i]);
-		if (!textures[i]) err("SDL_CreateTextureFromSurface");
-
-		error = Cards_InvertSurface(surfaces.cards[i]);
-		if (error) err("Cards_InvertSurface");
-
-		inverted[i] = SDL_CreateTextureFromSurface(render, surfaces.cards[i]);
-		if (!inverted[i]) err("SDL_CreateTextureFromSurface");
-
-		SDL_FreeSurface(surfaces.cards[i]);
-	}
-
-	if (kings) {
-		for (uint i = 0; i < Cards_FreeCellCount; ++i) {
-			if (!surfaces.kings[i]) continue;
-			kingTextures[i] =
-				SDL_CreateTextureFromSurface(render, surfaces.kings[i]);
-			if (!kingTextures[i]) err("SDL_CreateTextureFromSurface");
-			SDL_FreeSurface(surfaces.kings[i]);
-		}
-	}
-
-	srand(time(NULL));
-	gameDeal();
-
-	for (;;) {
-		updateLayout();
-
-		SDL_SetRenderDrawColor(render, 0x00, 0xAA, 0x55, 0xFF);
-		SDL_RenderClear(render);
-		renderOutlines();
-		if (kings) renderKing(kingTextures);
-		renderList(textures, &layout.main);
-		renderList(inverted, &layout.drag);
-		renderList(textures, &reveal);
-		SDL_RenderPresent(render);
-
-		// TODO: Add more than a frame delay between automatic moves?
-		if (gameAuto()) continue;
-
-		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_KEYUP) {
-				if (keyUp(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;
-			}
-		}
-	}
-
-quit:
-	SDL_Quit();
-}