from ..lobby.domain import Player, PlayerRole from ..session.domain import ArrowDirection, CardState, SessionPhase from ..session.events import ( GuesserMoveEvent, NextStepRequestedEvent, PlayerHelloEvent, RevealSecretEvent, SessionEvent, SetterMoveEvent, ) class SessionStateMachine: def __init__( self, setter: Player, guessers: set[Player], sequence_length: int = 20 ): self._setter = setter self._guessers = guessers self._pending_players: set[Player] = set(guessers) self._pending_players.add(setter) self._sequence_length = sequence_length self._setter_moves: list[ArrowDirection] = [] self._guessers_moves: dict[Player, list[ArrowDirection]] = { player: [] for player in guessers } self._phase: SessionPhase = SessionPhase.INITIALIZING def process(self, event: SessionEvent) -> bool: match (self._phase, event): case (SessionPhase.INITIALIZING, PlayerHelloEvent(player)): self._pending_players.remove(player) if not self._pending_players: self._phase = SessionPhase.WAITING_FOR_SETTER self._pending_players = {self._setter} return True case ( SessionPhase.WAITING_FOR_SETTER, SetterMoveEvent(player, direction), ): self._pending_players.remove(player) if not self._pending_players: self._setter_moves.append(direction) self._phase = SessionPhase.WAITING_FOR_GUESSER self._pending_players = set(self._guessers) return True case ( SessionPhase.WAITING_FOR_GUESSER, GuesserMoveEvent(player, direction), ): self._guessers_moves[player].append(direction) self._pending_players.remove(player) if not self._pending_players: self._phase = SessionPhase.WAITING_FOR_REVEAL self._pending_players = {self._setter} return True case ( SessionPhase.WAITING_FOR_REVEAL, RevealSecretEvent(player, direction), ): self._pending_players.remove(player) if not self._pending_players: self._phase = SessionPhase.CHECKOUT setter_move_index = len(self._setter_moves) - 1 self._setter_moves[setter_move_index] = direction self._pending_players = set(self._guessers) return True case (SessionPhase.CHECKOUT, NextStepRequestedEvent(player)): self._pending_players.remove(player) if not self._pending_players: if len(self._setter_moves) >= self._sequence_length: self._phase = SessionPhase.FINISHED self._pending_players.clear() else: self._phase = SessionPhase.WAITING_FOR_SETTER self._pending_players = {self._setter} return True case _: return False def get_card_state(self, player: Player, index: int) -> CardState: match player.role: case PlayerRole.SETTER: return self._get_setter_card_state(index) case PlayerRole.GUESSER: return self._get_guesser_card_state(player, index) def _get_setter_card_state(self, index: int) -> CardState: current_index = self.current_index if index > current_index: return CardState.SETTER_NOT_SET elif index == current_index: match self._phase: case SessionPhase.INITIALIZING: return CardState.SETTER_NOT_SET case SessionPhase.WAITING_FOR_SETTER: return CardState.SETTER_WAITING case ( SessionPhase.WAITING_FOR_GUESSER | SessionPhase.WAITING_FOR_REVEAL ): if self._setter_moves[-1] == ArrowDirection.HIDDEN: return CardState.SETTER_HIDDEN else: return CardState.SETTER_REVEALED case SessionPhase.CHECKOUT | SessionPhase.FINISHED: return CardState.SETTER_REVEALED else: return CardState.SETTER_REVEALED def _get_guesser_card_state(self, player: Player, index: int) -> CardState: def _check_direction( self: SessionStateMachine, player: Player, index: int ) -> CardState: if self._setter_moves[index] == self._guessers_moves[player][index]: return CardState.GUESSER_CORRECT else: return CardState.GUESSER_WRONG current_index = self.current_index if index > current_index: return CardState.GUESSER_DISABLED elif index == current_index: match self._phase: case ( SessionPhase.INITIALIZING | SessionPhase.WAITING_FOR_SETTER ): return CardState.GUESSER_DISABLED case ( SessionPhase.WAITING_FOR_GUESSER | SessionPhase.WAITING_FOR_REVEAL ): return CardState.GUESSER_WAITING case SessionPhase.CHECKOUT | SessionPhase.FINISHED: return _check_direction(self, player, index) else: return _check_direction(self, player, index) def get_setter_move(self, index: int) -> ArrowDirection | None: try: return self._setter_moves[index] except IndexError: return None def get_guesser_move( self, player: Player, index: int ) -> ArrowDirection | None: try: return self._guessers_moves[player][index] except IndexError: return None @property def current_index(self) -> int: setter_step_number = len(self._setter_moves) if setter_step_number: if self._phase == SessionPhase.WAITING_FOR_SETTER: return setter_step_number else: return setter_step_number - 1 return 0