from __future__ import annotations from dataclasses import dataclass from typing import Optional, Union @dataclass class HelloServer: description: str @dataclass class NewGame: player1: str player2: str @dataclass class MoveBroadcast: square: Optional[int] value: int # piece index or 16/17 @dataclass class GameOver: reason: str # "VICTORY" | "DRAW" | "DISCONNECT" winner: Optional[str] @dataclass class UnknownCommand: raw: str ServerMessage = Union[HelloServer, NewGame, MoveBroadcast, GameOver, UnknownCommand] def parse_server_line(line: str) -> ServerMessage: parts = line.strip().split("~") if not parts: return UnknownCommand(raw=line) cmd = parts[0] if cmd == "HELLO" and len(parts) == 2: return HelloServer(description=parts[1]) if cmd == "NEWGAME" and len(parts) == 3: return NewGame(player1=parts[1], player2=parts[2]) if cmd == "MOVE": if len(parts) == 2: try: value = int(parts[1]) except ValueError: return UnknownCommand(raw=line) return MoveBroadcast(square=None, value=value) if len(parts) == 3: try: square = int(parts[1]) value = int(parts[2]) except ValueError: return UnknownCommand(raw=line) return MoveBroadcast(square=square, value=value) if cmd == "GAMEOVER": if len(parts) == 2: return GameOver(reason=parts[1], winner=None) if len(parts) == 3: return GameOver(reason=parts[1], winner=parts[2]) return UnknownCommand(raw=line) def format_hello(description: str) -> str: return f"HELLO~{description}" def format_queue() -> str: return "QUEUE" def format_move_initial(piece: int) -> str: return f"MOVE~{piece}" def format_move_normal(square: int, m_value: int) -> str: return f"MOVE~{square}~{m_value}"