from pydantic import BaseModel from enum import Enum import requests class BaseError(Exception): ... class PositionError(BaseError): ... class PlayerInactiveError(BaseError): ... class PlayerNotFoundError(BaseError): ... class Direction(str, Enum): LEFT = "left" RIGHT = "right" UP = "up" DOWN = "down" class Board(BaseModel): width: int height: int class Position(BaseModel): x: int y: int class Destination(BaseModel): position: Position class Player(BaseModel): uuid: str name: str active: bool position: Position move_count: int move_attempt_count: int class PingResponse(BaseModel): message: str class StartGameResponse(BaseModel): board: Board destination: Destination player: Player class PlayerInfoResponse(BaseModel): player: Player class GameInfoResponse(BaseModel): board: Board destination: Destination class FairHopper: def __init__(self, host, port) -> None: self.host = host self.port = port def format_url(self, path: str) -> str: return f"{self.host}:{self.port}{path}" def ping(self) -> PingResponse: r = requests.get(self.format_url("/ping")) r.raise_for_status() return PingResponse(**r.json()) def start_game(self, player_name: str) -> StartGameResponse: payload = { "player_name": player_name, } r = requests.post(self.format_url("/game"), json=payload) r.raise_for_status() return StartGameResponse(**r.json()) def get_game_info(self) -> GameInfoResponse: r = requests.get(self.format_url(f"/game")) r.raise_for_status() return GameInfoResponse(**r.json()) def get_player_info(self, uuid: str) -> PlayerInfoResponse: r = requests.get(self.format_url(f"/player/{uuid}")) if r.status_code == 403: raise PlayerInactiveError() elif r.status_code == 404: raise PlayerNotFoundError() else: r.raise_for_status() return PlayerInfoResponse(**r.json()) def move_left(self, uuid: str) -> PlayerInfoResponse: return self.move(uuid, Direction.LEFT) def move_right(self, uuid: str) -> PlayerInfoResponse: return self.move(uuid, Direction.RIGHT) def move_up(self, uuid: str) -> PlayerInfoResponse: return self.move(uuid, Direction.UP) def move_down(self, uuid: str) -> PlayerInfoResponse: return self.move(uuid, Direction.DOWN) def move(self, uuid: str, direction: Direction) -> PlayerInfoResponse: path = f"/player/{uuid}/move/{direction}" r = requests.post(self.format_url(path)) if r.status_code == 403: raise PlayerInactiveError() elif r.status_code == 404: raise PlayerNotFoundError() elif r.status_code == 409: raise PositionError() else: r.raise_for_status() return PlayerInfoResponse(**r.json())