gamelogic
This commit is contained in:
@@ -33,11 +33,11 @@ MainPhaseDay --> MainPhaseNight: Some Werewolfes left
|
||||
```plantuml
|
||||
@startuml(id=TheNight)
|
||||
hide empty description
|
||||
[*] --> NightPhaseCupin: Game has Cupin
|
||||
[*] --> NightPhaseMain: no Cupin
|
||||
[*] --> NightPhaseCupin:
|
||||
|
||||
NightPhaseCupin --> InformLovedOnes
|
||||
InformLovedOnes --> NightPhaseMain
|
||||
NightPhaseCupin --> InformLovedOnes: Cupin selected
|
||||
InformLovedOnes --> NightPhaseMain: ack
|
||||
NightPhaseCupin --> NightPhaseMain : Cupin already selected or No Cupin
|
||||
' state NightPhaseCupinEnd <<join>>
|
||||
' NightPhaseCupin --> WaitToContinueCupin: !Cupin
|
||||
' NightPhaseCupin --> SelectLovedOnes: Cupin
|
||||
|
||||
@@ -1,48 +1,197 @@
|
||||
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):
|
||||
state: models.GameState
|
||||
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,
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
def check_victory(self):
|
||||
"""check for a winner of the current game"""
|
||||
if not self.valid_game():
|
||||
return None
|
||||
|
||||
if self.state.currentPhase == models.GamePhase.Award:
|
||||
return True
|
||||
|
||||
if sum(1 for _ in self.werewolfes) > 0:
|
||||
if sum(1 for _ in self.villagers) <= 0:
|
||||
return models.RoleGroup.Werewolfs
|
||||
pass
|
||||
else:
|
||||
return models.RoleGroup.Villagers
|
||||
|
||||
if sum(1 for _ in self.player_alive) == 2:
|
||||
living = list(self.player_alive)
|
||||
if living[0].lovedOne == living[1]:
|
||||
return models.RoleGroup.LovedOnes
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def player_alive(self):
|
||||
|
||||
@@ -46,11 +46,11 @@ class Roles(enum.Enum):
|
||||
Leaderwolf = enum.auto()
|
||||
|
||||
@classmethod
|
||||
def isWerwolf(cls, role: Roles):
|
||||
def isWerwolf(cls, role: "Roles"):
|
||||
return role in [cls.Werewolf, cls.Leaderwolf]
|
||||
|
||||
@classmethod
|
||||
def getGroup(cls, role: Roles):
|
||||
def getGroup(cls, role: "Roles"):
|
||||
return RoleGroup.Werewolfs if role in [cls.Werewolf, cls.Leaderwolf] else RoleGroup.Villagers
|
||||
|
||||
class Rules(object):
|
||||
@@ -98,6 +98,7 @@ class Player(object):
|
||||
diedInRound: int
|
||||
accusedBy: "Player"
|
||||
|
||||
|
||||
@property
|
||||
def isWerwolf(self):
|
||||
return Roles.isWerwolf(self.role)
|
||||
|
||||
@@ -5,4 +5,18 @@ import pywerwolf.gamelogic as gl
|
||||
|
||||
def test_game_init():
|
||||
g = gl.Game()
|
||||
assert g.game_id is not None
|
||||
|
||||
def test_game_start_next_transition():
|
||||
g = gl.Game()
|
||||
g.start_game()
|
||||
assert g.game_id is not None
|
||||
assert g.statemachine.state == gl.GameState.start_game is not None
|
||||
|
||||
def test_game_start_with_player_not_ready():
|
||||
g = gl.Game()
|
||||
g.add_player()
|
||||
g.start_game()
|
||||
assert g.game_id is not None
|
||||
assert g.statemachine.state == gl.GameState.waiting_for_players is not None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user