Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c7966755f | ||
|
|
0730d8180b |
@@ -6,11 +6,13 @@ from typing import Optional
|
||||
class ArrowDirection(StrEnum):
|
||||
LEFT = auto()
|
||||
RIGHT = auto()
|
||||
HIDDEN = auto()
|
||||
|
||||
|
||||
class StepState(Enum):
|
||||
WAITING_FOR_SETTER = auto()
|
||||
WAITING_FOR_GUESSER = auto()
|
||||
WAITING_FOR_CLOSURE = auto()
|
||||
SESSION_FINISHED = auto()
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from ..domain import ArrowDirection, Player
|
||||
from ..domain import ArrowDirection, Player, PlayerRole
|
||||
from ..network import NetworkController, NetworkMessage, NetworkMessageKind
|
||||
|
||||
|
||||
@@ -13,16 +13,58 @@ class SessionInput:
|
||||
)
|
||||
|
||||
def player_move(self, direction: ArrowDirection) -> None:
|
||||
payload = {
|
||||
"player": {
|
||||
"name": self._local_player.name,
|
||||
"role": self._local_player.role,
|
||||
},
|
||||
"arrow_direction": direction,
|
||||
}
|
||||
self._network.send(
|
||||
NetworkMessage(NetworkMessageKind.SESSION_PLAYER_MOVED, payload)
|
||||
)
|
||||
match self._local_player.role:
|
||||
case PlayerRole.SETTER:
|
||||
local_message = NetworkMessage(
|
||||
NetworkMessageKind.SESSION_PLAYER_MOVED,
|
||||
{
|
||||
"player": {
|
||||
"name": self._local_player.name,
|
||||
"role": self._local_player.role,
|
||||
},
|
||||
"arrow_direction": direction,
|
||||
},
|
||||
)
|
||||
|
||||
network_message = NetworkMessage(
|
||||
NetworkMessageKind.SESSION_PLAYER_MOVED,
|
||||
{
|
||||
"player": {
|
||||
"name": self._local_player.name,
|
||||
"role": self._local_player.role,
|
||||
},
|
||||
"arrow_direction": ArrowDirection.HIDDEN,
|
||||
},
|
||||
local_message.id,
|
||||
)
|
||||
|
||||
self._network.send_with_looback(local_message, network_message)
|
||||
|
||||
case PlayerRole.GUESSER:
|
||||
self._network.send(
|
||||
NetworkMessage(
|
||||
NetworkMessageKind.SESSION_PLAYER_MOVED,
|
||||
{
|
||||
"player": {
|
||||
"name": self._local_player.name,
|
||||
"role": self._local_player.role,
|
||||
},
|
||||
"arrow_direction": direction,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
self._network.send(
|
||||
NetworkMessage(
|
||||
NetworkMessageKind.SESSION_SECRET_REVEAL_REQUESTED,
|
||||
{
|
||||
"player": {
|
||||
"name": self._local_player.name,
|
||||
"role": self._local_player.role,
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
def finish_session(self) -> None:
|
||||
pass
|
||||
|
||||
@@ -12,6 +12,7 @@ MessageHandler = Callable[[NetworkMessage], None]
|
||||
class NetworkController:
|
||||
def __init__(self, host: str, port: int, username: str, password: str):
|
||||
self._handlers: set[MessageHandler] = set()
|
||||
self._pending_loopbacks: dict[str, NetworkMessage] = {}
|
||||
|
||||
self._topic = "mind_reader"
|
||||
self._client = mqtt.Client(CallbackAPIVersion.VERSION2)
|
||||
@@ -40,12 +41,28 @@ class NetworkController:
|
||||
match kind:
|
||||
case (
|
||||
NetworkMessageKind.SESSION_PLAYER_MOVED
|
||||
| NetworkMessageKind.SESSION_SECRET_REVEAL_REQUESTED
|
||||
| NetworkMessageKind.SESSION_SECRET_REVEALED
|
||||
| NetworkMessageKind.SESSION_SESSION_FINISHED
|
||||
| NetworkMessageKind.SESSION_SESSION_STARTED
|
||||
):
|
||||
topic = f"{self._topic}/session"
|
||||
self._client.publish(topic, payload)
|
||||
|
||||
def send_with_looback(
|
||||
self,
|
||||
local_message: NetworkMessage,
|
||||
network_message: NetworkMessage,
|
||||
) -> None:
|
||||
if local_message.id != network_message.id:
|
||||
raise ValueError(
|
||||
f"Mismatched message IDs in send_with_loopback: "
|
||||
f"local_message.id='{local_message.id}' vs network_message.id='{network_message.id}'"
|
||||
)
|
||||
|
||||
self._pending_loopbacks[local_message.id] = local_message
|
||||
self.send(network_message)
|
||||
|
||||
def _handle(self, network_message: NetworkMessage) -> None:
|
||||
for handler in self._handlers:
|
||||
handler(network_message)
|
||||
@@ -54,5 +71,10 @@ class NetworkController:
|
||||
self, client: mqtt.Client, userdata: None, message: mqtt.MQTTMessage
|
||||
) -> None:
|
||||
json_str = message.payload.decode("utf-8")
|
||||
network_message = NetworkMessage.from_json(json_str)
|
||||
self._handle(network_message)
|
||||
incoming_message = NetworkMessage.from_json(json_str)
|
||||
|
||||
if incoming_message.id in self._pending_loopbacks:
|
||||
local_message = self._pending_loopbacks.pop(incoming_message.id)
|
||||
self._handle(local_message)
|
||||
else:
|
||||
self._handle(incoming_message)
|
||||
|
||||
@@ -7,6 +7,8 @@ class NetworkMessageKind(StrEnum):
|
||||
# ---- Session ----
|
||||
SESSION_SESSION_STARTED = "session_started"
|
||||
SESSION_PLAYER_MOVED = "player_moved"
|
||||
SESSION_SECRET_REVEAL_REQUESTED = "secret_reveal_requested"
|
||||
SESSION_SECRET_REVEALED = "secret_revealed"
|
||||
SESSION_SESSION_FINISHED = "session_finished"
|
||||
|
||||
# ---- System -----
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from ..domain import ArrowDirection, Player, PlayerRole
|
||||
from ..state import SessionState
|
||||
from .controller import NetworkController
|
||||
from .enums import NetworkMessageKind
|
||||
from .message import NetworkMessage
|
||||
|
||||
|
||||
class SessionNetworkMessageHandler:
|
||||
def __init__(self, state: SessionState):
|
||||
def __init__(self, network: NetworkController, state: SessionState):
|
||||
self._network = network
|
||||
self._state = state
|
||||
|
||||
def __call__(self, message: NetworkMessage):
|
||||
@@ -23,5 +25,23 @@ class SessionNetworkMessageHandler:
|
||||
case PlayerRole.GUESSER:
|
||||
self._state.guesser_move(direction)
|
||||
|
||||
case NetworkMessageKind.SESSION_SECRET_REVEAL_REQUESTED:
|
||||
self._network.send(
|
||||
NetworkMessage(
|
||||
NetworkMessageKind.SESSION_SECRET_REVEALED,
|
||||
{
|
||||
"player": {
|
||||
"name": self._state.local_player.name,
|
||||
"role": self._state.local_player.role,
|
||||
},
|
||||
"arrow_direction": self._state.reveal_setter_move(),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
case NetworkMessageKind.SESSION_SECRET_REVEALED:
|
||||
direction = ArrowDirection(message.payload["arrow_direction"])
|
||||
self._state.step_close(direction)
|
||||
|
||||
case NetworkMessageKind.SESSION_SESSION_FINISHED:
|
||||
pass
|
||||
|
||||
@@ -32,27 +32,26 @@ class SessionState:
|
||||
return len(self._guesser_moves)
|
||||
|
||||
def _get_setter_card_state(self, index: int) -> CardState:
|
||||
current_index = len(self._setter_moves)
|
||||
completed = self._completed_move_count
|
||||
|
||||
if index > current_index:
|
||||
if index > completed:
|
||||
return CardState.SETTER_NOT_SET
|
||||
|
||||
elif index == current_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
|
||||
elif index == completed:
|
||||
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, index: int) -> CardState:
|
||||
completed_move_count = self._completed_move_count
|
||||
completed = self._completed_move_count
|
||||
|
||||
if index > completed_move_count:
|
||||
if index > completed:
|
||||
return CardState.GUESSER_DISABLED
|
||||
|
||||
elif index == completed_move_count:
|
||||
elif index == completed:
|
||||
if self._step_state == StepState.WAITING_FOR_GUESSER:
|
||||
return CardState.GUESSER_WAITING
|
||||
else:
|
||||
@@ -113,12 +112,21 @@ class SessionState:
|
||||
return False
|
||||
|
||||
self._guesser_moves.append(direction)
|
||||
self._step_state = StepState.WAITING_FOR_CLOSURE
|
||||
return True
|
||||
|
||||
def step_close(self, direction: ArrowDirection) -> bool:
|
||||
if self._step_state != StepState.WAITING_FOR_CLOSURE:
|
||||
return False
|
||||
|
||||
if self._completed_move_count < self._sequence_length:
|
||||
self._step_state = StepState.WAITING_FOR_SETTER
|
||||
else:
|
||||
self._step_state = StepState.SESSION_FINISHED
|
||||
|
||||
if self._local_player.role == PlayerRole.GUESSER:
|
||||
self._setter_moves[-1] = direction
|
||||
|
||||
index = len(self._guesser_moves) - 1
|
||||
|
||||
pub.sendMessage(
|
||||
@@ -158,6 +166,13 @@ class SessionState:
|
||||
|
||||
return True
|
||||
|
||||
def reveal_setter_move(self) -> ArrowDirection:
|
||||
if self._local_player.role == PlayerRole.SETTER:
|
||||
if self._step_state == StepState.WAITING_FOR_CLOSURE:
|
||||
return self._setter_moves[-1]
|
||||
|
||||
return ArrowDirection.HIDDEN
|
||||
|
||||
def local_move(self, direction: ArrowDirection) -> bool:
|
||||
if not self.can_local_move():
|
||||
return False
|
||||
@@ -172,6 +187,8 @@ class SessionState:
|
||||
return self._local_player.role == PlayerRole.SETTER
|
||||
case StepState.WAITING_FOR_GUESSER:
|
||||
return self._local_player.role == PlayerRole.GUESSER
|
||||
case StepState.WAITING_FOR_CLOSURE:
|
||||
return False
|
||||
case StepState.SESSION_FINISHED:
|
||||
return False
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ class GuesserCardColour:
|
||||
class ArrowSymbol:
|
||||
LEFT: str = "←"
|
||||
RIGHT: str = "→"
|
||||
HIDDEN: str = "?"
|
||||
|
||||
|
||||
class Card(wx.Panel):
|
||||
@@ -61,7 +62,7 @@ class Card(wx.Panel):
|
||||
|
||||
case CardState.SETTER_HIDDEN:
|
||||
colour = SetterCardColour.HIDDEN
|
||||
show_arrow = False
|
||||
show_arrow = True
|
||||
|
||||
case CardState.SETTER_REVEALED:
|
||||
colour = SetterCardColour.REVEALED
|
||||
@@ -91,6 +92,9 @@ class Card(wx.Panel):
|
||||
case ArrowDirection.RIGHT:
|
||||
symbol = ArrowSymbol.RIGHT
|
||||
|
||||
case ArrowDirection.HIDDEN:
|
||||
symbol = ArrowSymbol.HIDDEN
|
||||
|
||||
self.label.SetLabel(symbol)
|
||||
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user