226 lines
6.3 KiB
Python
226 lines
6.3 KiB
Python
from pywerwolf import models
|
|
import types
|
|
import typing as t
|
|
import uuid
|
|
import collections
|
|
from dataclasses import dataclass
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
|
|
|
|
class GameState(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def __repr__(self):
|
|
return self.name
|
|
|
|
|
|
GameState.initial = GameState("Initial")
|
|
GameState.waiting_for_players = GameState("Waiting for Players")
|
|
GameState.start_game = GameState("Start Game")
|
|
GameState.night_phase = GameState("Night Phase")
|
|
GameState.day_phase = GameState("Day Phase")
|
|
GameState.award = GameState("Award")
|
|
|
|
|
|
class Input:
|
|
pass
|
|
|
|
class TimeOut(Input):
|
|
pass
|
|
|
|
class GameMasterStartGame(Input):
|
|
pass
|
|
|
|
|
|
class Condition:
|
|
def condition(self, input) -> bool:
|
|
raise RuntimeError("not implemented")
|
|
|
|
|
|
class Transition:
|
|
def transition(self, input):
|
|
raise RuntimeError("transition not implemented")
|
|
|
|
|
|
@dataclass
|
|
class TransitionObject:
|
|
name: str
|
|
inputType: type
|
|
transition: Transition
|
|
condition: t.List[Condition]
|
|
next: GameState
|
|
|
|
|
|
class StateMachine:
|
|
"""Generic Statemachine
|
|
|
|
Executes actions on transitions
|
|
"""
|
|
|
|
transitionTable: t.Dict[GameState, TransitionObject]
|
|
state: GameState
|
|
data: object
|
|
|
|
def __init__(
|
|
self,
|
|
initialState: GameState,
|
|
tranTable: t.Dict[GameState, TransitionObject],
|
|
data: object,
|
|
):
|
|
self.state = initialState
|
|
self.transitionTable = tranTable
|
|
self.data = data
|
|
_logger.info("Created Statemachine")
|
|
|
|
def nextState(self, input):
|
|
"""Transfer into the next state
|
|
|
|
Depending on the
|
|
- input
|
|
- the current state
|
|
- the condition
|
|
"""
|
|
for transition in self.transitionTable[self.state]:
|
|
_logger.debug("Checking Transition: %s", transition.name)
|
|
if transition.inputType is not None and not isinstance(
|
|
input, transition.inputType
|
|
):
|
|
_logger.debug("Input type is required and not given. Expected: %s got %s", transition.inputType, type(input))
|
|
continue
|
|
|
|
if transition.condition is not None:
|
|
def check_condition(x):
|
|
_logger.debug("Check condition %s", x.__doc__)
|
|
if not x(input):
|
|
_logger.debug("Condition %s not met", x.__doc__)
|
|
return True
|
|
_logger.debug("Condition %s met", x.__doc__)
|
|
return False
|
|
|
|
if any(map(check_condition, transition.condition)):
|
|
continue
|
|
|
|
if transition.transition is not None:
|
|
t = transition.transition
|
|
t(input)
|
|
|
|
self.state = transition.next
|
|
_logger.info(f"Transition done to: {self.state}")
|
|
break
|
|
else:
|
|
_logger.warning("No transition performed for input")
|
|
|
|
|
|
class Player(object):
|
|
def __init__(self, name="Someone"):
|
|
self.ready = False
|
|
self.name = name
|
|
|
|
|
|
class Game(object):
|
|
statemachine: StateMachine
|
|
players: t.List[Player]
|
|
|
|
def __init__(self, game_id: t.Union[str, uuid.UUID] = None):
|
|
self.players = []
|
|
self.states = {
|
|
GameState.initial: [
|
|
TransitionObject(
|
|
name = "Initial 2 Waiting",
|
|
inputType=None,
|
|
condition=None,
|
|
transition=self.create_game_id,
|
|
next=GameState.waiting_for_players,
|
|
)
|
|
],
|
|
GameState.waiting_for_players: [
|
|
TransitionObject(
|
|
name = "Waiting 2 MainPhase",
|
|
inputType=GameMasterStartGame,
|
|
condition=[self.players_ready],
|
|
transition=self.assign_roles,
|
|
next=GameState.start_game,
|
|
)
|
|
],
|
|
GameState.start_game: [
|
|
TransitionObject(
|
|
name = "StartGame 2 MainPhaseNight",
|
|
inputType=GameMasterStartGame,
|
|
condition=[self.players_ready, self.no_cupin],
|
|
transition=self.assign_roles,
|
|
next=GameState.night_phase,
|
|
),
|
|
TransitionObject(
|
|
name = "StartGame 2 CupinSelect",
|
|
inputType=GameMasterStartGame,
|
|
condition=[self.has_cupin],
|
|
transition=self.assign_roles,
|
|
next=GameState.night_phase,
|
|
)
|
|
],
|
|
}
|
|
|
|
if game_id is None:
|
|
self.create_new_game_state()
|
|
else:
|
|
self.load_game_state(game_id)
|
|
|
|
def create_new_game_state(self):
|
|
_logger.info("Creating a new game")
|
|
self.statemachine = StateMachine(GameState.initial, self.states, self)
|
|
self.statemachine.nextState(None)
|
|
|
|
def players_ready(self, input) -> bool:
|
|
"""Players Ready?"""
|
|
_logger.debug("[%s] check if players ready", self.game_id)
|
|
return all(map(lambda x: x.ready, self.players))
|
|
|
|
def remove_players_ready_flag(self):
|
|
_logger.debug("[%s] remove players ready flag", self.game_id)
|
|
for p in self.players:
|
|
p.ready = False
|
|
|
|
def assign_roles(self, _):
|
|
pass
|
|
|
|
def start_game(self):
|
|
self.statemachine.nextState(GameMasterStartGame())
|
|
|
|
def add_player(self):
|
|
self.players.append(Player())
|
|
|
|
def create_game_id(self, *args):
|
|
self.game_id = uuid.uuid4()
|
|
_logger.info("[%s] created game", self.game_id)
|
|
|
|
def load_game_state(self, game_id: t.Union[str, uuid.UUID]):
|
|
_logger.info("Loading game")
|
|
pass
|
|
|
|
def valid_game(self):
|
|
# check if game has been created or loaded
|
|
return True
|
|
|
|
|
|
@property
|
|
def player_alive(self):
|
|
"""Generator for players still alive"""
|
|
return filter(lambda x: x.alive, self.state.players)
|
|
|
|
@property
|
|
def werewolfes(self):
|
|
"""Generator for players that are werewolfes"""
|
|
return filter(models.Player.isWerwolf, self.player_alive)
|
|
|
|
@property
|
|
def villagers(self):
|
|
"""Generator for players that are villagers"""
|
|
return filter(not (models.Player.isWerwolf), self.player_alive)
|