From 91953ef64d02a4bbffcbf449cae815ed66de93e5 Mon Sep 17 00:00:00 2001 From: Fabro Date: Thu, 4 Jun 2026 18:53:21 +0000 Subject: [PATCH] fabro(01KT9YT14FHDYA4VTV9DSFG0FY): impl_logic (succeeded) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fabro-Run: 01KT9YT14FHDYA4VTV9DSFG0FY Fabro-Completed: 9 Fabro-Checkpoint: 916c46b57a6bcf612fe0db0729a9f2675d63105c ⚒️ Generated with [Fabro](https://fabro.sh) --- card-game-app/src/card_game_tui/domain.py | 48 +++++++++++++++++++++++ card-game-app/tests/test_domain.py | 38 ++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/card-game-app/src/card_game_tui/domain.py b/card-game-app/src/card_game_tui/domain.py index fe733cef3..f1dd717f2 100644 --- a/card-game-app/src/card_game_tui/domain.py +++ b/card-game-app/src/card_game_tui/domain.py @@ -376,3 +376,51 @@ class GameState: def check_win(self) -> bool: """Returns True if all 52 cards are in the Foundations.""" return all(len(self.foundations[suit]) == 13 for suit in Suit) + + def check_loss(self) -> bool: + """ + Returns True if the game is lost (i.e. not won and no legal moves can be made). + """ + if self.check_win(): + return False + + # Check all possible moves from freecells + for i in range(4): + if self.freecells[i] is not None: + from_pos = Position(LocationType.FREECELL, i) + # To tableau + for j in range(8): + if self.validate_move(from_pos, Position(LocationType.TABLEAU, j), 1): + return False + # To other freecells + for j in range(4): + if i != j and self.validate_move(from_pos, Position(LocationType.FREECELL, j), 1): + return False + # To foundations + for j in range(4): + if self.validate_move(from_pos, Position(LocationType.FOUNDATION, j), 1): + return False + + # Check all possible moves from tableaus + for i in range(8): + col_len = len(self.tableaus[i]) + if col_len > 0: + from_pos = Position(LocationType.TABLEAU, i) + # Check different move counts + for count in range(1, col_len + 1): + # To other tableaus + for j in range(8): + if i != j and self.validate_move(from_pos, Position(LocationType.TABLEAU, j), count): + return False + # To freecells + if count == 1: + for j in range(4): + if self.validate_move(from_pos, Position(LocationType.FREECELL, j), 1): + return False + # To foundations + if count == 1: + for j in range(4): + if self.validate_move(from_pos, Position(LocationType.FOUNDATION, j), 1): + return False + + return True diff --git a/card-game-app/tests/test_domain.py b/card-game-app/tests/test_domain.py index 0fa4360ed..c507ddc8a 100644 --- a/card-game-app/tests/test_domain.py +++ b/card-game-app/tests/test_domain.py @@ -347,3 +347,41 @@ def test_win_detection(): # Remove one card state.foundations[Suit.HEARTS].pop() assert state.check_win() is False + +def test_check_loss_not_lost(): + # A standard freshly-dealt game should not be in a loss state (usually has legal moves) + state = GameState() + assert state.check_loss() is False + +def test_check_loss_won(): + # A won game is not lost + state = GameState() + for suit in Suit: + state.foundations[suit] = [Card(suit, rank) for rank in Rank] + assert state.check_loss() is False + +def test_check_loss_is_lost(): + state = GameState() + # Clear tableaus and freecells, and foundations + state.freecells = [None] * 4 + state.tableaus = [[] for _ in range(8)] + state.foundations = {suit: [] for suit in Suit} + + # Lock everything: + # Fill all 4 freecells with Kings of Diamonds + state.freecells = [Card(Suit.DIAMONDS, Rank.KING)] * 4 + + # Fill all 8 tableaus with at least one King of Diamonds + for i in range(8): + state.tableaus[i] = [Card(Suit.DIAMONDS, Rank.KING)] + + # Now there are: + # - No empty tableaus + # - No empty freecells + # - No Aces on top of tableaus or in freecells (all are Kings of Diamonds) + # So: + # - Cannot move to freecells (all occupied) + # - Cannot move to foundations (only Kings, no Aces) + # - Cannot move to tableaus (all top cards are King of Diamonds, can't build King on King, and no empty columns) + # This state should be a lost/locked state! + assert state.check_loss() is True