Files
fairhopper/hopper/api/views.py
2023-05-10 15:49:08 +02:00

151 lines
4.3 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,
MovePlayerResponseDto,
PingResponse,
PlayerInfoResponseDto,
StartGameRequestDto,
StartGameResponseDto,
)
from hopper.engine import GameEngine
from hopper.enums import Direction, PlayerMoveResult
from hopper.errors import (
Collision,
GameLockForMovement,
PositionOutOfBounds,
)
from hopper.models.player import Player
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)