Implemented actions

This commit is contained in:
Lukas Wölfer
2019-04-08 19:54:16 +02:00
parent a4d98e415c
commit 208c2c83a4
3 changed files with 144 additions and 37 deletions

View File

@@ -1,6 +1,6 @@
"""Contains board class"""
import enum
from typing import Union, Tuple, List
from typing import Union, List, Dict, Optional, NewType
from dataclasses import dataclass
@@ -34,19 +34,14 @@ class Position(enum.Enum):
Goal = enum.auto()
class BunkerStatus(enum.Enum):
"""States a bunker can be in, if it not holding a card"""
Empty = enum.auto()
Dragon = enum.auto()
KilledDragon = NewType('KilledDragon', SpecialCard)
@dataclass
class Board:
"""Solitaire board"""
field: List[List[Card]] = []
bunker: Tuple[Union[BunkerStatus, Card],
Union[BunkerStatus, Card],
Union[BunkerStatus, Card]] = (BunkerStatus.Empty,
BunkerStatus.Empty, BunkerStatus.Empty,)
goal: List[Tuple[NumberCard.Suit, int]] = []
field: List[List[Card]] = [[]] * 8
bunker: List[Union[KilledDragon, Optional[Card]]] = [None] * 3
goal: Dict[NumberCard.Suit, int] = {NumberCard.Suit.Red: 0,
NumberCard.Suit.Green: 0,
NumberCard.Suit.Black: 0}
flowerGone: bool = False

View File

@@ -5,14 +5,72 @@ import board
@dataclass
class MoveAction:
"""Moving a card from one stack to another"""
card: board.Card
source_position: board.Position
class GoalAction:
"""Move card from field to goal"""
card: board.NumberCard
source_id: int
source_position: board.Position
def apply(self, action_board: board.Board) -> None:
"""Do action"""
if self.source_position == board.Position.Field:
assert action_board.field[self.source_id][-1] == self.card
assert action_board.goal[self.card.suit] + 1 == self.card.number
action_board.field[self.source_id].pop()
action_board.goal[self.card.suit] += 1
elif self.source_position == board.Position.Bunker:
assert action_board.bunker[self.source_id] == self.card
assert action_board.goal[self.card.suit] + 1 == self.card.number
action_board.bunker[self.source_id] = None
action_board.goal[self.card.suit] += 1
else:
raise RuntimeError("Unknown position")
def undo(self, action_board: board.Board) -> None:
"""Undo action"""
assert action_board.goal[self.card.suit] == self.card.number
if self.source_position == board.Position.Field:
action_board.field[self.source_id].append(self.card)
elif self.source_position == board.Position.Bunker:
assert action_board.bunker[self.source_id] is None
action_board.bunker[self.source_id] = self.card
else:
raise RuntimeError("Unknown position")
action_board.goal[self.card.suit] -= 1
@dataclass
class RestoreAction:
"""Move card from bunker to field"""
card: board.Card
source_id: int
destination_position: board.Position
destination_id: int
def apply(self, action_board: board.Board) -> None:
"""Do action"""
assert action_board.bunker[self.source_id] == self.card
action_board.bunker[self.source_id] = None
@dataclass
class StoreAction:
"""Move card from field to bunker"""
card: board.Card
source_id: int
destination_id: int
@dataclass
class MoveAction:
"""Moving a card from one field stack to another"""
card: board.Card
source_id: int
destination_id: int
def apply(self, action_board: board.Board) -> None:
"""Do action"""
@dataclass
class DragonKillAction:
@@ -21,11 +79,57 @@ class DragonKillAction:
source_stacks: List[Tuple[board.Position, int]]
destination_bunker_id: int
def apply(self, action_board: board.Board) -> None:
"""Do action"""
assert (action_board.bunker[self.destination_bunker_id] is None or
action_board.bunker[self.destination_bunker_id] == self.dragon)
assert len(self.source_stacks) == 4
for position, index in self.source_stacks:
if position == board.Position.Field:
assert action_board.field[index]
assert action_board.field[index][-1] == self.dragon
action_board.field[index].pop()
elif position == board.Position.Bunker:
assert action_board.bunker[index] == self.dragon
action_board.bunker[index] = None
else:
raise RuntimeError("Can only kill dragons in field and bunker")
action_board.bunker[self.destination_bunker_id] = board.KilledDragon(
self.dragon)
def undo(self, action_board: board.Board) -> None:
"""Undo action"""
assert action_board.bunker[self.destination_bunker_id] == board.KilledDragon(
self.dragon)
assert len(self.source_stacks) == 4
for position, index in self.source_stacks:
if position == board.Position.Field:
action_board.field[index].append(self.dragon)
elif position == board.Position.Bunker:
action_board.bunker[index] = self.dragon
else:
raise RuntimeError("Can only kill dragons in field and bunker")
action_board.bunker[self.destination_bunker_id] = None
@dataclass
class HuaKillAction:
"""Remove the flower card"""
source_field_id: int
def apply(self, action_board: board.Board) -> None:
"""Do action"""
assert not action_board.flowerGone
assert action_board.field[self.source_field_id] == board.SpecialCard.Hua
action_board.field[self.source_field_id].pop()
action_board.flowerGone = True
Action = Union[MoveAction, DragonKillAction, HuaKillAction]
def undo(self, action_board: board.Board) -> None:
"""Undo action"""
assert action_board.flowerGone
action_board.field[self.source_field_id].append(board.SpecialCard.Hua)
action_board.flowerGone = False
Action = Union[MoveAction, DragonKillAction,
HuaKillAction, StoreAction, RestoreAction, GoalAction]

View File

@@ -16,7 +16,7 @@ def possible_dragonkill_actions(
"""Enumerate all possible dragon kills"""
possible_dragons = [board.SpecialCard.Zhong,
board.SpecialCard.Fa, board.SpecialCard.Bai]
if not any(x == board.BunkerStatus.Empty for x in search_board.bunker):
if not any(x is None for x in search_board.bunker):
new_possible_dragons = []
for dragon in possible_dragons:
if any(x == dragon for x in search_board.bunker):
@@ -35,7 +35,7 @@ def possible_dragonkill_actions(
destination_bunker_id = bunker_dragons[0]
else:
destination_bunker_id = [
i for i, x in enumerate(search_board.bunker) if x == board.BunkerStatus.Empty][0]
i for i, x in enumerate(search_board.bunker) if x is None][0]
source_stacks = [(board.Position.Bunker, i) for i in bunker_dragons]
source_stacks.extend([(board.Position.Field, i)
@@ -45,10 +45,10 @@ def possible_dragonkill_actions(
destination_bunker_id=destination_bunker_id)
def possible_bunkerize_actions(search_board: board.Board) -> Iterator[board_actions.MoveAction]:
def possible_bunkerize_actions(search_board: board.Board) -> Iterator[board_actions.StoreAction]:
"""Enumerates all possible card moves from the field to the bunker"""
open_bunker_list = [i for i, x in enumerate(
search_board.bunker) if x == board.BunkerStatus.Empty]
search_board.bunker) if x is None]
if not open_bunker_list:
return
@@ -57,14 +57,13 @@ def possible_bunkerize_actions(search_board: board.Board) -> Iterator[board_acti
for index, stack in enumerate(search_board.field):
if not stack:
continue
yield board_actions.MoveAction(card=stack[-1],
source_position=board.Position.Field,
source_id=index,
destination_position=board.Position.Bunker,
destination_id=open_bunker)
yield board_actions.StoreAction(card=stack[-1],
source_id=index,
destination_id=open_bunker)
def possible_debunkerize_actions(search_board: board.Board) -> Iterator[board_actions.MoveAction]:
def possible_debunkerize_actions(
search_board: board.Board) -> Iterator[board_actions.RestoreAction]:
"""Enumerates all possible card moves from the bunker to the field"""
bunker_number_cards = [(i, x) for i, x in enumerate(
search_board.bunker) if isinstance(x, board.NumberCard)]
@@ -78,16 +77,26 @@ def possible_debunkerize_actions(search_board: board.Board) -> Iterator[board_ac
continue
if other_stack[-1].number != card.number + 1:
continue
yield board_actions.MoveAction(card=card,
source_position=board.Position.Bunker,
source_id=index,
destination_position=board.Position.Field,
destination_id=other_index)
yield board_actions.RestoreAction(card=card,
source_id=index,
destination_id=other_index)
def possible_goal_move_actions(search_board: board.Board) -> Iterator[board_actions.MoveAction]:
def possible_goal_move_actions(search_board: board.Board) -> Iterator[board_actions.GoalAction]:
"""Enumerates all possible moves from anywhere to the goal"""
#TODO
field_cards = [(board.Position.Field, index, stack[-1]) for index, stack in enumerate(
search_board.field) if stack if isinstance(stack[-1], board.NumberCard)]
bunker_cards = [(board.Position.Bunker, index, stack)
for index, stack in enumerate(search_board.bunker)
if isinstance(stack, board.NumberCard)]
top_cards = field_cards + bunker_cards
for suit, number in search_board.goal.items():
for source, index, stack in top_cards:
if not (stack.suit == suit and stack.number == number + 1):
continue
yield board_actions.GoalAction(card=stack, source_id=index, source_position=source)
break
def possible_field_move_actions(search_board: board.Board) -> Iterator[board_actions.MoveAction]:
@@ -107,9 +116,7 @@ def possible_field_move_actions(search_board: board.Board) -> Iterator[board_act
if other_stack[-1].number != stack[-1].number + 1:
continue
yield board_actions.MoveAction(card=stack[-1],
source_position=board.Position.Field,
source_id=index,
destination_position=board.Position.Field,
destination_id=other_index)
@@ -117,6 +124,7 @@ def possible_actions(search_board: board.Board) -> Iterator[board_actions.Action
"""Enumerate all possible actions on the current search_board"""
yield from possible_huakill_action(search_board)
yield from possible_dragonkill_actions(search_board)
yield from possible_goal_move_actions(search_board)
yield from possible_debunkerize_actions(search_board)
yield from possible_field_move_actions(search_board)
yield from possible_bunkerize_actions(search_board)