Files
pywerewolf/pywerwolf/gamelogic.py
2021-03-14 14:07:04 +01:00

219 lines
6.0 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],
transition=self.assign_roles,
next=GameState.start_game,
)
],
}
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)