Files
fairhopper-sdk/python/fh_sdk.py

166 lines
3.8 KiB
Python

from enum import Enum
from typing import List, Optional
import requests
from pydantic import BaseModel
class BaseError(Exception):
detail: str = "BaseError"
def __str__(self) -> str:
return self.detail
class PositionError(BaseError):
detail = "Invalid position"
class PlayerInactiveError(BaseError):
detail = "Player inactive"
class PlayerNotFoundError(BaseError):
detail = "Player not found"
class GameLockedError(BaseError):
detail = "Game locked. Product selection in progress."
class ValidationError(BaseError):
detail = "Validation error"
class Direction(str, Enum):
LEFT = "left"
RIGHT = "right"
UP = "up"
DOWN = "down"
class PlayerState(str, Enum):
CREATED = "CREATED"
MOVING = "MOVING"
ON_DESTINATION = "ON_DESTINATION"
INACTIVE = "INACTIVE"
class Board(BaseModel):
width: int
height: int
class Position(BaseModel):
x: int
y: int
class Destination(BaseModel):
position: Position
class Player(BaseModel):
id: str
name: str
active: bool
position: Position
move_count: int
move_attempt_count: int
state: PlayerState
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: str) -> None:
self.host = host
def format_url(self, path: str) -> str:
return f"{self.host}{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)
if r.status_code == 422:
raise ValidationError()
else:
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, player_id: str) -> PlayerInfoResponse:
r = requests.get(self.format_url(f"/player/{player_id}"))
if r.status_code == 403:
raise PlayerInactiveError()
elif r.status_code == 404:
raise PlayerNotFoundError()
elif r.status_code == 422:
raise ValidationError()
else:
r.raise_for_status()
return PlayerInfoResponse(**r.json())
def move_left(self, player_id: str) -> PlayerInfoResponse:
return self.move(player_id, Direction.LEFT)
def move_right(self, player_id: str) -> PlayerInfoResponse:
return self.move(player_id, Direction.RIGHT)
def move_up(self, player_id: str) -> PlayerInfoResponse:
return self.move(player_id, Direction.UP)
def move_down(self, player_id: str) -> PlayerInfoResponse:
return self.move(player_id, Direction.DOWN)
def move(self, player_id: str, direction: Direction) -> PlayerInfoResponse:
path = f"/player/{player_id}/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()
elif r.status_code == 422:
raise ValidationError()
elif r.status_code == 423:
raise GameLockedError()
else:
r.raise_for_status()
return PlayerInfoResponse(**r.json())