Compare commits

2 Commits
Author SHA1 Message Date
vladimir 6a66a02071 feat: wip SessionState, SessionView 2026-08-12 04:30:17 +03:00
vladimir da169b49fc refactor(ui): wip session_view.py 2026-08-12 01:21:54 +03:00
+274 -45
View File
@@ -1,11 +1,10 @@
import sys import sys
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum, auto from enum import Enum, auto
from typing import Optional from typing import Callable, Optional
import wx import wx
from wx.lib.pubsub import pub
from mind_reader.core import SessionState
def enable_high_dpi_support(): def enable_high_dpi_support():
@@ -56,6 +55,172 @@ class SessionStats:
return round(accuracy, 1) return round(accuracy, 1)
class StepState(Enum):
WAITING_FOR_SETTER = auto()
WAITING_FOR_GUESSER = auto()
SESSION_FINISHED = auto()
class PlayerRole(Enum):
SETTER = auto()
GUESSER = auto()
@dataclass(frozen=True)
class MoveResult:
step_index: int
setter_direction: Optional[ArrowDirection] = None
setter_card_state: Optional[CardState] = None
guesser_direction: Optional[ArrowDirection] = None
guesser_card_state: Optional[CardState] = None
@dataclass
class Player:
name: str
role: PlayerRole
class SessionState:
def __init__(
self,
local_player: Player,
remote_player: Player,
sequence_length: int = 20,
):
self._sequence_length = sequence_length
self._local_player = local_player
self._remote_player = remote_player
self._completed_step_count: int = 0
self._step_state: StepState = StepState.WAITING_FOR_SETTER
self._setter_moves: list[ArrowDirection] = []
self._guesser_moves: list[ArrowDirection] = []
self._stats: Optional[SessionStats] = None
def setter_move(self, direction: ArrowDirection) -> bool:
if self._step_state != StepState.WAITING_FOR_SETTER:
return False
self._setter_moves.append(direction)
self._step_state = StepState.WAITING_FOR_GUESSER
step_index = len(self._setter_moves) - 1
result = MoveResult(
step_index=step_index,
setter_direction=direction,
setter_card_state=self.get_setter_card_state(step_index),
)
pub.sendMessage("session.setter_moved", result=result)
pub.sendMessage(
"session.step_state_changed",
step_state=self._step_state,
)
return True
def guesser_move(self, direction: ArrowDirection) -> bool:
if self._step_state != StepState.WAITING_FOR_GUESSER:
return False
self._guesser_moves.append(direction)
self._completed_step_count += 1
step_index = len(self._guesser_moves) - 1
result = MoveResult(
step_index=step_index,
setter_direction=self._setter_moves[-1],
setter_card_state=self.get_setter_card_state(step_index),
guesser_direction=self._guesser_moves[-1],
guesser_card_state=self.get_guesser_card_state(step_index),
)
pub.sendMessage("session.guesser_moved", result=result)
if self._completed_step_count < self._sequence_length:
self._step_state = StepState.WAITING_FOR_SETTER
pub.sendMessage(
"session.step_state_changed",
step_state=self._step_state,
)
else:
self._step_state = StepState.SESSION_FINISHED
pub.sendMessage("session.session_finished")
return True
def local_move(self, direction: ArrowDirection) -> bool:
if not self.can_local_move():
return False
if self._local_player.role == PlayerRole.SETTER:
return self.setter_move(direction)
else:
return self.guesser_move(direction)
def can_local_move(self) -> bool:
match self._step_state:
case StepState.WAITING_FOR_SETTER:
return self._local_player.role == PlayerRole.SETTER
case StepState.WAITING_FOR_GUESSER:
return self._local_player.role == PlayerRole.GUESSER
case StepState.SESSION_FINISHED:
return False
def get_setter_card_state(self, step_index: int) -> CardState:
setter_step_index = len(self._setter_moves) + 1
if step_index > setter_step_index:
return CardState.SETTER_NOT_SET
if step_index == setter_step_index:
if self._step_state == StepState.WAITING_FOR_GUESSER:
if self._local_player.role == PlayerRole.SETTER:
return CardState.SETTER_REVEALED
else:
return CardState.SETTER_HIDDEN
return CardState.SETTER_REVEALED
def get_guesser_card_state(self, step_index: int) -> CardState:
if step_index > self._completed_step_count:
return CardState.GUESSER_DISABLED
elif step_index == self._completed_step_count:
return CardState.GUESSER_WAITING
else:
guesser_move = self._guesser_moves[step_index]
setter_move = self._setter_moves[step_index]
if guesser_move == setter_move:
return CardState.GUESSER_CORRECT
else:
return CardState.GUESSER_WRONG
@property
def setter_name(self) -> str:
if self._local_player.role == PlayerRole.SETTER:
return self._local_player.name
else:
return self._remote_player.name
@property
def guesser_name(self) -> str:
if self._local_player.role == PlayerRole.GUESSER:
return self._local_player.name
else:
return self._remote_player.name
@property
def sequence_lenght(self) -> int:
return self._sequence_length
@dataclass(frozen=True) @dataclass(frozen=True)
class SetterCardColour: class SetterCardColour:
NOT_SET = wx.Colour(82, 97, 107) NOT_SET = wx.Colour(82, 97, 107)
@@ -158,17 +323,15 @@ class Card(wx.Panel):
class SessionPanel(wx.Panel): class SessionPanel(wx.Panel):
def __init__( def __init__(self, parent, state: SessionState):
self,
parent,
setter_name: str,
guesser_name: str,
sequence_length: int = 20,
):
super().__init__(parent) super().__init__(parent)
self._setter_name = setter_name
self._guesser_name = guesser_name self._state = state
self._sequence_length = sequence_length
self._setter_name = self._state.setter_name
self._guesser_name = self._state.guesser_name
self._sequence_length = self._state.sequence_lenght
self._setter_sequence: list[Card] = [] self._setter_sequence: list[Card] = []
self._guesser_sequence: list[Card] = [] self._guesser_sequence: list[Card] = []
@@ -177,34 +340,36 @@ class SessionPanel(wx.Panel):
self._setup() self._setup()
self._fit_parent_frame() self._fit_parent_frame()
pub.subscribe(self._on_setter_moved, "session.setter_moved")
pub.subscribe(self._on_guesser_moved, "session.guesser_moved")
pub.subscribe(self._on_step_state_changed, "session.step_state_changed")
pub.subscribe(self._on_session_finished, "session.session_finished")
def _setup(self) -> None: def _setup(self) -> None:
main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer = wx.BoxSizer(wx.VERTICAL)
setter_title = wx.StaticText( setter_sizer = self._create_sequence_section(
self, label=f"Загадывает: {self._setter_name}" title=f"Загадывает: {self._setter_name}",
sequence=self._setter_sequence,
sequence_length=self._sequence_length,
initial_card_state=CardState.SETTER_NOT_SET,
) )
setter_sizer = wx.BoxSizer(wx.HORIZONTAL)
for _ in range(self._sequence_length):
card = Card(self, CardState.SETTER_NOT_SET)
setter_sizer.Add(card, 0, wx.ALL, 4)
self._setter_sequence.append(card)
guesser_title = wx.StaticText( guesser_sizer = self._create_sequence_section(
self, label=f"Отгадывает: {self._guesser_name}" title=f"Угадывает: {self._guesser_name}",
sequence=self._guesser_sequence,
sequence_length=self._sequence_length,
initial_card_state=CardState.GUESSER_DISABLED,
) )
guesser_sizer = wx.BoxSizer(wx.HORIZONTAL)
for _ in range(self._sequence_length):
card = Card(self, CardState.GUESSER_DISABLED)
guesser_sizer.Add(card, 0, wx.ALL, 4)
self._guesser_sequence.append(card)
controls_sizer = self._create_controls_sizer() controls_sizer = self._create_controls_sizer(
on_left=self._on_left_click,
on_right=self._on_right_click,
)
secondary_sizer = wx.BoxSizer(wx.VERTICAL) secondary_sizer = wx.BoxSizer(wx.VERTICAL)
secondary_sizer.Add(setter_title, 0, wx.LEFT | wx.TOP, 9) secondary_sizer.Add(setter_sizer, 0, wx.EXPAND)
secondary_sizer.Add(setter_sizer, 0, wx.ALL, 5) secondary_sizer.Add(guesser_sizer, 0, wx.EXPAND)
secondary_sizer.Add(guesser_title, 0, wx.LEFT | wx.TOP, 9)
secondary_sizer.Add(guesser_sizer, 0, wx.ALL, 5)
secondary_sizer.Add( secondary_sizer.Add(
controls_sizer, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 25 controls_sizer, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 25
) )
@@ -213,7 +378,32 @@ class SessionPanel(wx.Panel):
self.SetSizer(main_sizer) self.SetSizer(main_sizer)
def _create_controls_sizer(self) -> wx.Sizer: def _create_sequence_section(
self,
title: str,
sequence: list[Card],
sequence_length: int,
initial_card_state: CardState,
) -> wx.Sizer:
section_sizer = wx.BoxSizer(wx.VERTICAL)
title_label = wx.StaticText(self, label=title)
cards_sizer = wx.BoxSizer(wx.HORIZONTAL)
for _ in range(sequence_length):
card = Card(self, initial_card_state)
cards_sizer.Add(card, 0, wx.ALL, 4)
sequence.append(card)
section_sizer.Add(title_label, 0, wx.LEFT | wx.TOP, 9)
section_sizer.Add(cards_sizer, 0, wx.ALL, 5)
return section_sizer
def _create_controls_sizer(
self,
on_left: Callable[[wx.CommandEvent], None],
on_right: Callable[[wx.CommandEvent], None],
) -> wx.Sizer:
left = wx.Button(self, label=ArrowSymbol.LEFT, size=wx.Size(145, 55)) left = wx.Button(self, label=ArrowSymbol.LEFT, size=wx.Size(145, 55))
right = wx.Button(self, label=ArrowSymbol.RIGHT, size=wx.Size(145, 55)) right = wx.Button(self, label=ArrowSymbol.RIGHT, size=wx.Size(145, 55))
@@ -223,6 +413,9 @@ class SessionPanel(wx.Panel):
left.SetFont(font) left.SetFont(font)
right.SetFont(font) right.SetFont(font)
left.Bind(wx.EVT_BUTTON, on_left)
right.Bind(wx.EVT_BUTTON, on_right)
controls_sizer = wx.BoxSizer(wx.HORIZONTAL) controls_sizer = wx.BoxSizer(wx.HORIZONTAL)
controls_sizer.Add(left, 0, wx.RIGHT, 10) controls_sizer.Add(left, 0, wx.RIGHT, 10)
controls_sizer.Add(right, 0, wx.LEFT, 10) controls_sizer.Add(right, 0, wx.LEFT, 10)
@@ -261,6 +454,41 @@ class SessionPanel(wx.Panel):
top_frame.SetMinSize(min_size) top_frame.SetMinSize(min_size)
top_frame.Fit() top_frame.Fit()
def _on_left_click(self, event: wx.CommandEvent) -> None:
self._handle_arrow_input(ArrowDirection.LEFT)
def _on_right_click(self, event: wx.CommandEvent) -> None:
self._handle_arrow_input(ArrowDirection.RIGHT)
def _handle_arrow_input(self, direction: ArrowDirection) -> None:
pass
def _on_setter_moved(self, result: MoveResult):
self.update_setter_card(
index=result.step_index,
state=result.setter_card_state or CardState.SETTER_NOT_SET,
direction=result.setter_direction,
)
def _on_guesser_moved(self, result: MoveResult):
self.update_setter_card(
index=result.step_index,
state=result.setter_card_state or CardState.SETTER_NOT_SET,
direction=result.setter_direction,
)
self.update_guesser_card(
index=result.step_index,
state=result.guesser_card_state or CardState.GUESSER_DISABLED,
direction=result.guesser_direction,
)
def _on_step_state_changed(self, step_state: StepState):
pass
def _on_session_finished(self):
pass
def update_setter_card( def update_setter_card(
self, self,
index: int, index: int,
@@ -279,14 +507,14 @@ class SessionPanel(wx.Panel):
def update_statusbar( def update_statusbar(
self, self,
statusbar_text: Optional[str], message: Optional[str],
stats: SessionStats, stats: SessionStats,
): ):
if not self._statusbar: if not self._statusbar:
return return
if statusbar_text is not None: if message is not None:
self._statusbar.SetStatusText(statusbar_text, 0) self._statusbar.SetStatusText(message, 0)
progress = f" Прогресс {stats.setted}/{stats.total} " progress = f" Прогресс {stats.setted}/{stats.total} "
self._statusbar.SetStatusText(progress, 1) self._statusbar.SetStatusText(progress, 1)
@@ -307,22 +535,23 @@ class SessionPanel(wx.Panel):
class TestFrame(wx.Frame): class TestFrame(wx.Frame):
def __init__(self): def __init__(self):
super().__init__(None, title="TestFrame") super().__init__(None, title="TestFrame")
main_panel = SessionPanel(self, "Бывалый", "Балбес")
local = Player("Бывалый", PlayerRole.GUESSER)
remote = Player("Балбес", PlayerRole.SETTER)
session = SessionState(local, remote)
main_panel = SessionPanel(self, session)
main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(main_panel, 1, wx.EXPAND) main_sizer.Add(main_panel, 1, wx.EXPAND)
self.SetSizer(main_sizer) self.SetSizer(main_sizer)
main_panel.update_setter_card(
0, CardState.SETTER_REVEALED, ArrowDirection.LEFT
)
main_panel.update_guesser_card(
0, CardState.GUESSER_WRONG, ArrowDirection.RIGHT
)
s = SessionStats(20, 1, 0, 1, 5.1) s = SessionStats(20, 1, 0, 1, 5.1)
main_panel.update_statusbar(None, s) main_panel.update_statusbar(None, s)
session.setter_move(ArrowDirection.LEFT)
session.guesser_move(ArrowDirection.LEFT)
session.setter_move(ArrowDirection.LEFT)
if __name__ == "__main__": if __name__ == "__main__":
enable_high_dpi_support() enable_high_dpi_support()