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