211 lines
6.2 KiB
Python
211 lines
6.2 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Response
|
|
from starlette import status
|
|
|
|
from hopper.api.dependencies import get_game_engine
|
|
from hopper.api.dto import (
|
|
DestinationDto,
|
|
ErrorResponseDto,
|
|
GameInfoDto,
|
|
GetProductsResponse,
|
|
MovePlayerResponseDto,
|
|
PingResponse,
|
|
PlayerInfoResponseDto,
|
|
ProductDto,
|
|
PurchaseProductDto,
|
|
StartGameRequestDto,
|
|
StartGameResponseDto,
|
|
)
|
|
from hopper.engine import GameEngine
|
|
from hopper.enums import Direction, PlayerMoveResult
|
|
from hopper.errors import (
|
|
Collision,
|
|
GameLockForMovement,
|
|
PositionOutOfBounds,
|
|
PurchaseForbiddenForPlayer,
|
|
)
|
|
from hopper.models.player import Player
|
|
from settings import settings
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def get_player(id: str, engine: GameEngine = Depends(get_game_engine)) -> Player:
|
|
player = engine.players.find(id)
|
|
if player is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND, detail="Player not found"
|
|
)
|
|
if not player.active:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Player kicked out due to inactivity",
|
|
)
|
|
return player
|
|
|
|
|
|
@router.get("/ping", response_model=PingResponse)
|
|
async def ping() -> PingResponse:
|
|
return PingResponse(
|
|
message="Pong!",
|
|
)
|
|
|
|
|
|
@router.get("/game", response_model=GameInfoDto)
|
|
async def get_game_info(
|
|
engine: GameEngine = Depends(get_game_engine),
|
|
) -> GameInfoDto:
|
|
return GameInfoDto(
|
|
board=engine.board,
|
|
destination=DestinationDto(
|
|
position=engine.board.destination.position,
|
|
),
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/game", response_model=StartGameResponseDto, status_code=status.HTTP_201_CREATED
|
|
)
|
|
async def start_game(
|
|
body: StartGameRequestDto,
|
|
engine: GameEngine = Depends(get_game_engine),
|
|
) -> StartGameResponseDto:
|
|
new_player = await engine.start_game_for_player(player_name=body.player_name)
|
|
|
|
return StartGameResponseDto(
|
|
board=engine.board,
|
|
player=new_player,
|
|
destination=DestinationDto(
|
|
position=engine.board.destination.position,
|
|
),
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/player/{id}",
|
|
response_model=PlayerInfoResponseDto,
|
|
responses={
|
|
status.HTTP_403_FORBIDDEN: {
|
|
"model": ErrorResponseDto,
|
|
"description": "Player inactive",
|
|
},
|
|
status.HTTP_404_NOT_FOUND: {
|
|
"model": ErrorResponseDto,
|
|
"description": "Player with id not found, probably kicked out",
|
|
},
|
|
},
|
|
)
|
|
async def get_player_info(
|
|
player: Player = Depends(get_player),
|
|
) -> MovePlayerResponseDto:
|
|
return PlayerInfoResponseDto(player=player)
|
|
|
|
|
|
@router.post(
|
|
"/player/{id}/move/{direction}",
|
|
response_model=MovePlayerResponseDto,
|
|
status_code=status.HTTP_201_CREATED,
|
|
responses={
|
|
status.HTTP_200_OK: {
|
|
"model": MovePlayerResponseDto,
|
|
"description": "Destination reached!",
|
|
},
|
|
status.HTTP_403_FORBIDDEN: {
|
|
"model": ErrorResponseDto,
|
|
"description": "Player inactive",
|
|
},
|
|
status.HTTP_404_NOT_FOUND: {
|
|
"model": ErrorResponseDto,
|
|
"description": "Player with id not found, probably kicked out",
|
|
},
|
|
status.HTTP_409_CONFLICT: {
|
|
"model": ErrorResponseDto,
|
|
"description": "Position out of bounds or collision with an object",
|
|
},
|
|
status.HTTP_423_LOCKED: {
|
|
"model": ErrorResponseDto,
|
|
"description": "Player reached destination. Can't move anymore.",
|
|
},
|
|
},
|
|
)
|
|
async def move_player(
|
|
direction: Direction,
|
|
response: Response,
|
|
engine: GameEngine = Depends(get_game_engine),
|
|
player: Player = Depends(get_player),
|
|
) -> MovePlayerResponseDto:
|
|
try:
|
|
move_result = await engine.move_player(player, direction)
|
|
except PositionOutOfBounds:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_409_CONFLICT, detail="Position out of bounds"
|
|
)
|
|
except Collision:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_409_CONFLICT, detail="Collision with an object"
|
|
)
|
|
except GameLockForMovement:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_423_LOCKED,
|
|
detail="Player reached destination. Can't move anymore.",
|
|
)
|
|
|
|
if move_result == PlayerMoveResult.DESTINATION_REACHED:
|
|
response.status_code = status.HTTP_200_OK
|
|
|
|
return MovePlayerResponseDto(player=player)
|
|
|
|
|
|
@router.get("/products", response_model=GetProductsResponse)
|
|
async def get_products() -> GetProductsResponse:
|
|
return GetProductsResponse(
|
|
products=settings.products,
|
|
)
|
|
|
|
|
|
@router.get("/products/{id}", response_model=ProductDto)
|
|
async def get_product(id: str) -> ProductDto:
|
|
for product in settings.products:
|
|
if product.id == id:
|
|
return ProductDto.from_orm(product)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND, detail="Product not found"
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/player/{id}/product/purchase",
|
|
response_model=ProductDto,
|
|
responses={
|
|
status.HTTP_200_OK: {
|
|
"model": ProductDto,
|
|
"description": "Product purchased",
|
|
},
|
|
status.HTTP_403_FORBIDDEN: {
|
|
"model": ErrorResponseDto,
|
|
"description": "Purchase forbidden for this player",
|
|
},
|
|
status.HTTP_404_NOT_FOUND: {
|
|
"model": ErrorResponseDto,
|
|
"description": " Player with id not found, probably kicked out",
|
|
},
|
|
},
|
|
)
|
|
async def purchase_product(
|
|
body: PurchaseProductDto,
|
|
engine: GameEngine = Depends(get_game_engine),
|
|
player: Player = Depends(get_player),
|
|
) -> ProductDto:
|
|
for product in settings.products:
|
|
if product.id == body.product_id:
|
|
try:
|
|
await engine.purchase_product(player=player, product=product)
|
|
return ProductDto.from_orm(product)
|
|
except PurchaseForbiddenForPlayer:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Purchase forbidden for this player",
|
|
)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND, detail="Product not found"
|
|
)
|