From 90ab53624029a8a4f127970ff13dff205cea8f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20W=C3=B6lfer?= Date: Sat, 20 Apr 2019 02:17:08 +0200 Subject: [PATCH] Optimization --- shenzhen_solitaire/board_possibilities.py | 50 ++++++----- shenzhen_solitaire/solver.py | 15 ++-- test/test_chain.py | 104 ++++++++++++---------- test/test_solver.py | 6 +- 4 files changed, 93 insertions(+), 82 deletions(-) diff --git a/shenzhen_solitaire/board_possibilities.py b/shenzhen_solitaire/board_possibilities.py index 79c523d..d5a65f7 100644 --- a/shenzhen_solitaire/board_possibilities.py +++ b/shenzhen_solitaire/board_possibilities.py @@ -1,5 +1,5 @@ """Contains function to iterate different kinds of possible actions""" -from typing import Iterator, List +from typing import Iterator, List, Tuple from . import board from . import board_actions @@ -168,29 +168,33 @@ def possible_field_move_actions( """Enumerate all possible move actions from one field stack to another field stack""" first_empty_field_id = -1 - my_id = search_board.state_identifier - for index, stack in enumerate(_get_cardstacks(search_board)): - if not stack: - continue - # TODO: sort all substacks by length - for substack in (stack[i:] for i in range(len(stack))): - for other_index, other_stack in enumerate(search_board.field): - if index == other_index: + cardstacks = [(index, stack) + for index, stack in enumerate(_get_cardstacks(search_board))] + cardstacks = [x for x in cardstacks if x[1]] + cardstacks = sorted(cardstacks, key=lambda x: len(x[1])) + substacks: List[Tuple[int, List[board.Card]]] = [] + + for index, stack in cardstacks: + substacks.extend((index, substack) + for substack in (stack[i:] + for i in range(len(stack)))) + + for index, substack in substacks: + for other_index, other_stack in enumerate(search_board.field): + if index == other_index: + continue + if other_stack: + if not _can_stack(other_stack[-1], substack[0]): continue - if other_stack: - if not _can_stack(other_stack[-1], substack[0]): - continue - elif first_empty_field_id == -1: - first_empty_field_id = other_index - elif other_index != first_empty_field_id: - continue - elif len(substack) == len(search_board.field[index]): - continue - assert search_board.state_identifier == my_id - yield board_actions.MoveAction( - cards=substack, source_id=index, destination_id=other_index - ) - assert search_board.state_identifier == my_id + elif len(substack) == len(search_board.field[index]): + continue + elif first_empty_field_id == -1: + first_empty_field_id = other_index + elif other_index != first_empty_field_id: + continue + yield board_actions.MoveAction( + cards=substack, source_id=index, destination_id=other_index + ) def possible_actions( diff --git a/shenzhen_solitaire/solver.py b/shenzhen_solitaire/solver.py index 3f78ffa..9cd0a51 100644 --- a/shenzhen_solitaire/solver.py +++ b/shenzhen_solitaire/solver.py @@ -54,30 +54,31 @@ class SolitaireSolver: self.stack = ActionStack() self.stack.push(self.search_board) - def solve(self) -> List[board_actions.Action]: - max_goal = 0 - action = None + def solve(self) -> Iterator[List[board_actions.Action]]: while self.stack: + assert (self.search_board.state_identifier == self.stack.state_stack[-1]) action = self.stack.get() + if not action: self.stack.pop() self.stack.action_stack[-1].undo(self.search_board) assert (self.search_board.state_identifier in self.state_set) continue + action.apply(self.search_board) + if self.search_board.solved(): - return self.stack.action_stack + yield self.stack.action_stack + if self.search_board.state_identifier in self.state_set: action.undo(self.search_board) assert self.search_board.state_identifier in self.state_set continue + self.state_set.add(self.search_board.state_identifier) self.stack.push(self.search_board) - if sum(self.search_board.goal.values()) > max_goal: - max_goal = sum(self.search_board.goal.values()) - print(self.search_board.goal) return self.stack.action_stack diff --git a/test/test_chain.py b/test/test_chain.py index b012e8e..a4b1be5 100644 --- a/test/test_chain.py +++ b/test/test_chain.py @@ -2,7 +2,8 @@ import unittest from .context import shenzhen_solitaire -from shenzhen_solitaire.board import NumberCard, SpecialCard, Board +from shenzhen_solitaire.board import NumberCard, SpecialCard, Board, Position +from shenzhen_solitaire.board_actions import MoveAction, BunkerizeAction, GoalAction, HuaKillAction from shenzhen_solitaire import board_possibilities from .boards import my_board @@ -15,52 +16,57 @@ class ChainTestClass(unittest.TestCase): self.assertTrue(my_board.check_correct()) sequence = [ - 0, - 4, - 0, - 1, - 0, - 0, - 8, - 0, - 1, - 3, - 0, - 9, - 0, - 2, - 0, - 1, - 1, - 1, - 2, - 0, - 2, - 1, - 6, - 12, - 0, - 0, - 1, - 0, - 0, - 17, - 11, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0] - for action_index in sequence: + MoveAction( + cards=[ + NumberCard( + suit=NumberCard.Suit.Red, + number=7), + NumberCard( + suit=NumberCard.Suit.Green, + number=6)], + source_id=3, + destination_id=7), + BunkerizeAction( + card=NumberCard( + suit=NumberCard.Suit.Red, + number=6), + bunker_id=0, + field_id=2, + to_bunker=True), + GoalAction( + card=NumberCard( + suit=NumberCard.Suit.Green, + number=1), + source_id=2, + source_position=Position.Field), + MoveAction( + cards=[ + NumberCard( + suit=NumberCard.Suit.Red, + number=4)], + source_id=2, + destination_id=5), + GoalAction( + card=NumberCard( + suit=NumberCard.Suit.Red, + number=1), + source_id=2, + source_position=Position.Field), + HuaKillAction(source_field_id=2), + MoveAction( + cards=[ + NumberCard( + suit=NumberCard.Suit.Black, number=9), + NumberCard(suit=NumberCard.Suit.Red, number=8)], + source_id=6, + destination_id=2), + GoalAction( + card=NumberCard( + suit=NumberCard.Suit.Green, number=2), + source_id=6, + source_position=Position.Field) + ] + for action in sequence: step = list(board_possibilities.possible_actions(my_board)) - step[action_index].apply(my_board) + self.assertIn(action, step) + action.apply(my_board) diff --git a/test/test_solver.py b/test/test_solver.py index 0f955d1..1445c24 100644 --- a/test/test_solver.py +++ b/test/test_solver.py @@ -5,9 +5,9 @@ from .boards import my_board def main() -> None: A = solver.SolitaireSolver(my_board) - B = A.solve() - print(*B, sep='\n') - print(len(B)) + for _, B in zip(range(1), A.solve()): + print(*B, sep='\n') + print(len(B)) if __name__ == "__main__": main()