mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-12 23:02:41 +00:00
fabro(01KTA1FC5W5W0BHTQSV865A1H6): impl_logic (succeeded)
Fabro-Run: 01KTA1FC5W5W0BHTQSV865A1H6
Fabro-Completed: 9
Fabro-Checkpoint: 516516a770
⚒️ Generated with [Fabro](https://fabro.sh)
This commit is contained in:
parent
094a552a5c
commit
e73e5322ac
2 changed files with 182 additions and 0 deletions
|
|
@ -78,3 +78,89 @@ def test_validate_move_sequence():
|
|||
assert not valid
|
||||
assert "Insufficient empty FreeCells" in reason
|
||||
|
||||
def test_validate_move_to_freecell():
|
||||
state = GameState()
|
||||
state.free_cells = [None] * 4
|
||||
state.tableau[0] = [Card(Rank.ACE, Suit.SPADES)]
|
||||
|
||||
# Valid move of single card to empty FreeCell
|
||||
move_valid = Move('C', 0, 'F', 0, 1)
|
||||
valid, reason = validate_move(state, move_valid)
|
||||
assert valid
|
||||
|
||||
# Cannot move a sequence to a FreeCell
|
||||
state.tableau[0] = [Card(Rank.TWO, Suit.HEARTS), Card(Rank.ACE, Suit.SPADES)]
|
||||
move_seq = Move('C', 0, 'F', 1, 2)
|
||||
valid, reason = validate_move(state, move_seq)
|
||||
assert not valid
|
||||
assert "Cannot move a sequence to a FreeCell" in reason
|
||||
|
||||
# Cannot move to an occupied FreeCell
|
||||
state.free_cells[2] = Card(Rank.KING, Suit.CLUBS)
|
||||
move_occ = Move('C', 0, 'F', 2, 1)
|
||||
valid, reason = validate_move(state, move_occ)
|
||||
assert not valid
|
||||
assert "Target FreeCell is occupied" in reason
|
||||
|
||||
def test_validate_move_to_foundation():
|
||||
state = GameState()
|
||||
state.free_cells = [None] * 4
|
||||
state.foundations = {suit: [] for suit in Suit}
|
||||
state.tableau[0] = [Card(Rank.ACE, Suit.SPADES)]
|
||||
state.tableau[1] = [Card(Rank.TWO, Suit.SPADES)]
|
||||
|
||||
# Moving Ace of Spades to empty Foundation is valid
|
||||
move_ace = Move('C', 0, 'A', 0, 1)
|
||||
valid, reason = validate_move(state, move_ace)
|
||||
assert valid
|
||||
|
||||
# Moving Two of Spades to empty Foundation is invalid
|
||||
move_two_invalid = Move('C', 1, 'A', 0, 1)
|
||||
valid, reason = validate_move(state, move_two_invalid)
|
||||
assert not valid
|
||||
assert "Foundations must start with an Ace" in reason
|
||||
|
||||
# Place Ace of Spades in foundation first
|
||||
state.foundations[Suit.SPADES] = [Card(Rank.ACE, Suit.SPADES)]
|
||||
# Now, moving Two of Spades to foundation is valid
|
||||
move_two_valid = Move('C', 1, 'A', 0, 1)
|
||||
valid, reason = validate_move(state, move_two_valid)
|
||||
assert valid
|
||||
|
||||
# Moving a non-consecutive card (e.g. Four of Spades) is invalid
|
||||
state.tableau[2] = [Card(Rank.FOUR, Suit.SPADES)]
|
||||
move_four_invalid = Move('C', 2, 'A', 0, 1)
|
||||
valid, reason = validate_move(state, move_four_invalid)
|
||||
assert not valid
|
||||
assert "Must be next rank up" in reason
|
||||
|
||||
# Cannot move a sequence to a Foundation
|
||||
state.tableau[3] = [Card(Rank.THREE, Suit.HEARTS), Card(Rank.TWO, Suit.SPADES)]
|
||||
move_seq = Move('C', 3, 'A', 0, 2)
|
||||
valid, reason = validate_move(state, move_seq)
|
||||
assert not valid
|
||||
assert "Cannot move a sequence to a Foundation" in reason
|
||||
|
||||
def test_validate_move_sequence_invalid_alternating_color():
|
||||
state = GameState()
|
||||
state.free_cells = [None] * 4
|
||||
state.tableau[0] = [Card(Rank.JACK, Suit.HEARTS), Card(Rank.TEN, Suit.DIAMONDS)]
|
||||
|
||||
# Hearts and Diamonds are both RED. Sequence J♥, 10♦ is invalid!
|
||||
move = Move('C', 0, 'C', 1, 2)
|
||||
valid, reason = validate_move(state, move)
|
||||
assert not valid
|
||||
assert "alternating color descending sequence" in reason
|
||||
|
||||
def test_validate_move_sequence_invalid_ranks():
|
||||
state = GameState()
|
||||
state.free_cells = [None] * 4
|
||||
state.tableau[0] = [Card(Rank.JACK, Suit.HEARTS), Card(Rank.NINE, Suit.SPADES)]
|
||||
|
||||
# Jack (11) and Nine (9) is invalid sequence (should be Jack and Ten)
|
||||
move = Move('C', 0, 'C', 1, 2)
|
||||
valid, reason = validate_move(state, move)
|
||||
assert not valid
|
||||
assert "alternating color descending sequence" in reason
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -127,3 +127,99 @@ def test_is_lost():
|
|||
# Thus, no legal moves are possible!
|
||||
assert state.is_lost()
|
||||
|
||||
def test_execute_invalid_move_no_history():
|
||||
state = GameState()
|
||||
state.tableau = [[] for _ in range(8)]
|
||||
state.free_cells = [None] * 4
|
||||
state.tableau[0] = [Card(Rank.KING, Suit.HEARTS)]
|
||||
state.tableau[1] = [Card(Rank.TEN, Suit.DIAMONDS)]
|
||||
|
||||
# Attempt invalid move (cannot place 10♦ on K♥)
|
||||
move = Move('C', 1, 'C', 0, 1)
|
||||
success, reason = state.execute_move(move)
|
||||
assert not success
|
||||
assert not state.history # History should remain empty
|
||||
|
||||
def test_multiple_undo_redo():
|
||||
state = GameState()
|
||||
state.tableau = [[] for _ in range(8)]
|
||||
state.free_cells = [None] * 4
|
||||
state.foundations = {suit: [] for suit in Suit}
|
||||
|
||||
# Set up some cards
|
||||
state.tableau[0] = [Card(Rank.ACE, Suit.SPADES)]
|
||||
state.tableau[1] = [Card(Rank.TWO, Suit.SPADES)]
|
||||
|
||||
# 1. Move Ace of Spades to foundation
|
||||
success, _ = state.execute_move(Move('C', 0, 'A', 0, 1))
|
||||
assert success
|
||||
assert len(state.foundations[Suit.SPADES]) == 1
|
||||
|
||||
# 2. Move Two of Spades to foundation (will auto-home, but we move manually)
|
||||
success, _ = state.execute_move(Move('C', 1, 'A', 0, 1))
|
||||
assert success
|
||||
assert len(state.foundations[Suit.SPADES]) == 2
|
||||
|
||||
# Check state before undo
|
||||
assert not state.tableau[0]
|
||||
assert not state.tableau[1]
|
||||
|
||||
# Undo 2nd move
|
||||
assert state.undo()
|
||||
assert len(state.foundations[Suit.SPADES]) == 1
|
||||
assert state.tableau[1] == [Card(Rank.TWO, Suit.SPADES)]
|
||||
|
||||
# Undo 1st move
|
||||
assert state.undo()
|
||||
assert len(state.foundations[Suit.SPADES]) == 0
|
||||
assert state.tableau[0] == [Card(Rank.ACE, Suit.SPADES)]
|
||||
|
||||
# No more history
|
||||
assert not state.undo()
|
||||
|
||||
# Redo 1st move
|
||||
assert state.redo()
|
||||
assert len(state.foundations[Suit.SPADES]) == 1
|
||||
assert not state.tableau[0]
|
||||
|
||||
# Redo 2nd move
|
||||
assert state.redo()
|
||||
assert len(state.foundations[Suit.SPADES]) == 2
|
||||
assert not state.tableau[1]
|
||||
|
||||
# No more redo history
|
||||
assert not state.redo()
|
||||
|
||||
def test_is_lost_false_if_freecell_can_move_to_tableau():
|
||||
state = GameState()
|
||||
# 8 columns filled with 5♠ -> no moves between columns
|
||||
state.tableau = [[Card(Rank.FIVE, Suit.SPADES)] for _ in range(8)]
|
||||
# All FreeCells but one occupied by King of Hearts. One has Six of Hearts
|
||||
state.free_cells = [
|
||||
Card(Rank.KING, Suit.HEARTS),
|
||||
Card(Rank.KING, Suit.HEARTS),
|
||||
Card(Rank.KING, Suit.HEARTS),
|
||||
Card(Rank.FOUR, Suit.HEARTS) # Four of Hearts can go on Five of Spades!
|
||||
]
|
||||
state.foundations = {suit: [] for suit in Suit}
|
||||
|
||||
# Not lost because 4♥ in FreeCell can move onto 5♠ on any Tableau column
|
||||
assert not state.is_lost()
|
||||
|
||||
def test_is_lost_false_if_freecell_can_move_to_foundation():
|
||||
state = GameState()
|
||||
# 8 columns filled with 5♠ -> no moves between columns
|
||||
state.tableau = [[Card(Rank.FIVE, Suit.SPADES)] for _ in range(8)]
|
||||
# FreeCells occupied, one contains Ace of Spades
|
||||
state.free_cells = [
|
||||
Card(Rank.KING, Suit.HEARTS),
|
||||
Card(Rank.KING, Suit.HEARTS),
|
||||
Card(Rank.KING, Suit.HEARTS),
|
||||
Card(Rank.ACE, Suit.SPADES) # Ace can go to foundation
|
||||
]
|
||||
state.foundations = {suit: [] for suit in Suit}
|
||||
|
||||
# Not lost because Ace can go to empty Spades Foundation
|
||||
assert not state.is_lost()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue