202 lines
4.7 KiB
Python
202 lines
4.7 KiB
Python
from typing import List, Optional
|
|
from pydantic import BaseModel
|
|
from enum import Enum
|
|
|
|
import requests
|
|
|
|
|
|
class BaseError(Exception):
|
|
...
|
|
|
|
|
|
class PositionError(BaseError):
|
|
...
|
|
|
|
|
|
class PlayerInactiveError(BaseError):
|
|
...
|
|
|
|
|
|
class PlayerNotFoundError(BaseError):
|
|
...
|
|
|
|
|
|
class ValidationError(BaseError):
|
|
...
|
|
|
|
|
|
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 Product(BaseModel):
|
|
name: str
|
|
id: str
|
|
description: Optional[str] = None
|
|
|
|
|
|
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 ProductListResponse(BaseModel):
|
|
products: List[Product]
|
|
|
|
|
|
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()
|
|
else:
|
|
r.raise_for_status()
|
|
|
|
return PlayerInfoResponse(**r.json())
|
|
|
|
def get_products(self) -> List[Product]:
|
|
r = requests.get(self.format_url("/products"))
|
|
response_data = ProductListResponse(**r.json())
|
|
return response_data.products
|
|
|
|
def get_product(self, product_id: str) -> Product:
|
|
r = requests.get(self.format_url(f"/products/{product_id}"))
|
|
|
|
if r.status_code == 422:
|
|
raise ValidationError()
|
|
else:
|
|
r.raise_for_status()
|
|
|
|
return Product(**r.json())
|
|
|
|
def purchase_product(self, player_id: str, product_id: str) -> Product:
|
|
url = self.format_url(f"/player/{player_id}/product/purchase")
|
|
payload = {
|
|
"product_id": product_id,
|
|
}
|
|
r = requests.post(url, json=payload)
|
|
|
|
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()
|
|
else:
|
|
r.raise_for_status()
|
|
|
|
return Product(**r.json())
|