summary refs log tree commit diff
path: root/freecell.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--freecell.c176
1 files changed, 76 insertions, 100 deletions
diff --git a/freecell.c b/freecell.c
index 332be63..eae8413 100644
--- a/freecell.c
+++ b/freecell.c
@@ -56,42 +56,40 @@ struct Move {
 	uint src;
 };
 
-enum { QueueLen = 32 };
+enum { QueueLen = 16 };
 static struct {
 	struct Move moves[QueueLen];
-	uint r, w;
+	uint r, w, u;
 } queue;
 
-static uint undo;
-
-static void gameEnqueue(uint dst, uint src) {
+static void enqueue(uint dst, uint src) {
 	queue.moves[queue.w % QueueLen].dst = dst;
 	queue.moves[queue.w % QueueLen].src = src;
 	queue.w++;
 }
 
-static void gameDequeue(void) {
+static void dequeue(void) {
 	struct Move move = queue.moves[queue.r++ % QueueLen];
 	stackPush(&stacks[move.dst], stackPop(&stacks[move.src]));
-	if (move.dst >= Foundation1 && move.dst <= Foundation4) {
+	if (move.dst <= Foundation4) {
 		kingIndex = Cards_KingRight;
-	} else if (move.dst >= Cell1 && move.dst <= Cell4) {
+	} else if (move.dst <= Cell4) {
 		kingIndex = Cards_KingLeft;
 	}
 }
 
-static bool gameUndo(void) {
-	uint len = queue.w - undo;
+static bool undo(void) {
+	uint len = queue.w - queue.u;
 	if (!len || len > QueueLen) return false;
 	for (uint i = len - 1; i < len; --i) {
-		struct Move move = queue.moves[(undo + i) % QueueLen];
+		struct Move move = queue.moves[(queue.u + i) % QueueLen];
 		stackPush(&stacks[move.src], stackPop(&stacks[move.dst]));
 	}
-	queue.r = queue.w = undo;
+	queue.r = queue.w = queue.u;
 	return true;
 }
 
-static void gameDeal(void) {
+static void deal(void) {
 	for (uint i = 0; i < StacksLen; ++i) {
 		stackClear(&stacks[i]);
 	}
@@ -103,14 +101,14 @@ static void gameDeal(void) {
 	kingIndex = Cards_KingRight;
 }
 
-static bool gameWin(void) {
+static bool win(void) {
 	for (uint i = Foundation1; i <= Foundation4; ++i) {
 		if (stacks[i].len != 13) return false;
 	}
 	return true;
 }
 
-static bool gameValid(uint dst, Card card) {
+static bool valid(uint dst, Card card) {
 	Card top = stackTop(&stacks[dst]);
 	if (dst >= Foundation1 && dst <= Foundation4) {
 		if (!top) return cardRank(card) == Cards_A;
@@ -125,7 +123,7 @@ static bool gameValid(uint dst, Card card) {
 	return false;
 }
 
-static void gameAuto(void) {
+static void autoEnqueue(void) {
 	Card min[] = { Cards_K, Cards_K };
 	for (uint i = Cell1; i <= Tableau8; ++i) {
 		for (uint j = 0; j < stacks[i].len; ++j) {
@@ -143,23 +141,22 @@ static void gameAuto(void) {
 			if (min[!cardColor(card)] < cardRank(card)) continue;
 		}
 		for (uint dst = Foundation1; dst <= Foundation4; ++dst) {
-			if (gameValid(dst, card)) {
-				gameEnqueue(dst, src);
+			if (valid(dst, card)) {
+				enqueue(dst, src);
 				return;
 			}
 		}
 	}
 }
 
-static uint gameFree(uint list[]) {
-	uint len = 0;
-	for (uint i = Cell1; i <= Tableau8; ++i) {
-		if (!stacks[i].len) list[len++] = i;
+static void moveSingle(uint dst, uint src) {
+	if (valid(dst, stackTop(&stacks[src]))) {
+		queue.u = queue.w;
+		enqueue(dst, src);
 	}
-	return len;
 }
 
-static uint gameDepth(uint src) {
+static uint moveDepth(uint src) {
 	struct Stack stack = stacks[src];
 	if (stack.len < 2) return stack.len;
 	uint n = 1;
@@ -170,34 +167,36 @@ static uint gameDepth(uint src) {
 	return n;
 }
 
-static void gameMoveSingle(uint dst, uint src) {
-	if (gameValid(dst, stackTop(&stacks[src]))) {
-		undo = queue.w;
-		gameEnqueue(dst, src);
+static uint freeCells(uint list[], uint dst) {
+	uint len = 0;
+	for (uint i = Cell1; i <= Tableau8; ++i) {
+		if (i == dst) continue;
+		if (!stacks[i].len) list[len++] = i;
 	}
+	return len;
 }
 
-static void gameMoveColumn(uint dst, uint src) {
+static void moveColumn(uint dst, uint src) {
 	uint depth;
-	for (depth = gameDepth(src); depth; --depth) {
-		if (gameValid(dst, stacks[src].cards[stacks[src].len - depth])) break;
+	for (depth = moveDepth(src); depth; --depth) {
+		if (valid(dst, stacks[src].cards[stacks[src].len - depth])) break;
 	}
 	if (depth < 2 || dst <= Cell4) {
-		gameMoveSingle(dst, src);
+		moveSingle(dst, src);
 		return;
 	}
 
 	uint list[StacksLen];
-	uint free = gameFree(list);
+	uint free = freeCells(list, dst);
 	if (free < depth - 1) return;
 
-	undo = queue.w;
+	queue.u = queue.w;
 	for (uint i = 0; i < depth - 1; ++i) {
-		gameEnqueue(list[i], src);
+		enqueue(list[i], src);
 	}
-	gameEnqueue(dst, src);
+	enqueue(dst, src);
 	for (uint i = depth - 2; i < depth - 1; --i) {
-		gameEnqueue(dst, list[i]);
+		enqueue(dst, list[i]);
 	}
 }
 
@@ -280,37 +279,15 @@ static void err(const char *title) {
 	exit(EXIT_FAILURE);
 }
 
-static Card hiliteRank;
-static SDL_Point revealPoint;
-static uint sourceStack = StacksLen;
-
-enum Choice {
-	Cancel,
-	Column,
-	Single,
-};
-
-static enum Choice chooseMove(void) {
+static bool playAgain(void) {
 	SDL_MessageBoxButtonData buttons[] = {
-		{
-			.buttonid = Column,
-			.text = "Move column",
-			.flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
-		},
-		{
-			.buttonid = Single,
-			.text = "Move single card",
-		},
-		{
-			.buttonid = Cancel,
-			.text = "Cancel",
-			.flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
-		},
+		{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, true, "Yes" },
+		{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, false, "No" },
 	};
 	SDL_MessageBoxData data = {
 		.window = window,
-		.title = "Move to Empty Column...",
-		.message = "",
+		.title = "Game Over",
+		.message = "Congratulations, you win!\n\nDo you want to play again?",
 		.buttons = buttons,
 		.numbuttons = SDL_arraysize(buttons),
 	};
@@ -319,23 +296,22 @@ static enum Choice chooseMove(void) {
 	return choice;
 }
 
-static bool chooseAgain(void) {
+enum Choice {
+	Cancel,
+	Column,
+	Single,
+};
+
+static enum Choice chooseMove(void) {
 	SDL_MessageBoxButtonData buttons[] = {
-		{
-			.buttonid = true,
-			.text = "Yes",
-			.flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
-		},
-		{
-			.buttonid = false,
-			.text = "No",
-			.flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
-		},
+		{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, Column, "Move column" },
+		{ 0, Single, "Move single card" },
+		{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, Cancel, "Cancel" },
 	};
 	SDL_MessageBoxData data = {
 		.window = window,
-		.title = "Game Over",
-		.message = "Congratulations, you win!\n\nDo you want to play again?",
+		.title = "Move to Empty Column...",
+		.message = "",
 		.buttons = buttons,
 		.numbuttons = SDL_arraysize(buttons),
 	};
@@ -344,10 +320,14 @@ static bool chooseAgain(void) {
 	return choice;
 }
 
+static Card hiliteRank;
+static SDL_Point revealPoint;
+static uint fromStack = StacksLen;
+
 static bool keyDown(SDL_KeyboardEvent key) {
 	switch (key.keysym.sym) {
-		case SDLK_F2: gameDeal(); return true;
-		case SDLK_BACKSPACE: return gameUndo();
+		case SDLK_F2: deal(); return true;
+		case SDLK_BACKSPACE: return undo();
 	}
 	if (key.repeat) return false;
 	switch (key.keysym.sym) {
@@ -389,12 +369,9 @@ static bool mouseButtonDown(SDL_MouseButtonEvent button) {
 }
 
 static bool mouseButtonUp(SDL_MouseButtonEvent button) {
-	if (gameWin()) {
-		if (chooseAgain()) {
-			gameDeal();
-			return true;
-		}
-		return false;
+	if (win() && playAgain()) {
+		deal();
+		return true;
 	}
 
 	if (button.button == SDL_BUTTON_RIGHT && revealPoint.x) {
@@ -411,25 +388,24 @@ static bool mouseButtonUp(SDL_MouseButtonEvent button) {
 		}
 	}
 
-	if (sourceStack < StacksLen && stack < StacksLen) {
-		if (gameDepth(sourceStack) > 1 && stack > Cell4 && !stacks[stack].len) {
+	if (fromStack < StacksLen && stack < StacksLen) {
+		if (moveDepth(fromStack) > 1 && stack > Cell4 && !stacks[stack].len) {
 			switch (chooseMove()) {
-				break; case Single: gameMoveSingle(stack, sourceStack);
-				break; case Column: gameMoveColumn(stack, sourceStack);
+				break; case Single: moveSingle(stack, fromStack);
+				break; case Column: moveColumn(stack, fromStack);
 				break; case Cancel: break;
 			}
 		} else {
-			gameMoveColumn(stack, sourceStack);
+			moveColumn(stack, fromStack);
 		}
 	}
 
-	if (sourceStack < StacksLen) {
-		sourceStack = StacksLen;
+	if (fromStack < StacksLen) {
+		fromStack = StacksLen;
 		return true;
 	}
-
 	if (stack < StacksLen && stack > Foundation4 && stacks[stack].len) {
-		sourceStack = stack;
+		fromStack = stack;
 		return true;
 	}
 
@@ -471,7 +447,7 @@ static void renderOutlines(void) {
 static void renderKing(void) {
 	SDL_Rect box = { KingX, KingY, KingWidth, KingHeight };
 	renderOutline(box, true);
-	if (gameWin()) {
+	if (win()) {
 		SDL_Rect king = { KingWinX, KingWinY, KingWinWidth, KingWinHeight };
 		SDL_RenderCopy(render, tex.kings[Cards_KingWin], NULL, &king);
 	} else {
@@ -498,7 +474,7 @@ static void renderStack(uint stack) {
 			revealCard = card;
 			revealRect = rect;
 		}
-		bool hilite = (stack == sourceStack && i == stacks[stack].len - 1);
+		bool hilite = (stack == fromStack && i == stacks[stack].len - 1);
 		renderCard(rect, card, hilite || cardRank(card) == hiliteRank);
 		rect.y += StackDeltaY;
 	}
@@ -511,7 +487,7 @@ static void renderStacks(void) {
 	for (uint i = Foundation1; i <= Cell4; ++i) {
 		Card card = stackTop(&stacks[i]);
 		if (!card) continue;
-		bool hilite = (i == sourceStack || cardRank(card) == hiliteRank);
+		bool hilite = (i == fromStack || cardRank(card) == hiliteRank);
 		renderCard(rects[i], card, hilite);
 	}
 	for (uint i = Tableau1; i <= Tableau8; ++i) {
@@ -587,7 +563,7 @@ int main(void) {
 	}
 
 	srand(time(NULL));
-	gameDeal();
+	deal();
 	
 	initRects();
 	for (;;) {
@@ -599,8 +575,8 @@ int main(void) {
 		SDL_RenderPresent(render);
 
 		if (queue.r < queue.w) {
-			gameDequeue();
-			if (queue.r == queue.w) gameAuto();
+			dequeue();
+			if (queue.r == queue.w) autoEnqueue();
 			if (queue.r < queue.w) SDL_Delay(50);
 			continue;
 		}