from fastapi import APIRouter, Depends, HTTPException, Response from starlette import status from hopper.api.dependencies import get_game_engine from hopper.api.dto import ( BoardDto, DestinationDto, ErrorResponseDto, GameInfoDto, MovePlayerResponseDto, PingResponse, PlayerDto, PlayerInfoResponseDto, PositionDto, StartGameRequestDto, StartGameResponseDto, ) from hopper.engine import GameEngine from hopper.enums import Direction, PlayerMoveResult from hopper.errors import Collision, PositionOutOfBounds router = APIRouter() @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=BoardDto.from_model(engine.board), destination=DestinationDto( position=PositionDto.from_model(engine.board.destination.position) ), ) @router.post("/game", response_model=StartGameResponseDto) async def start_game( body: StartGameRequestDto, engine: GameEngine = Depends(get_game_engine), ) -> StartGameResponseDto: new_player = engine.start_game(player_name=body.player_name) return StartGameResponseDto( board=BoardDto.from_model(engine.board), player=PlayerDto.from_model(new_player), destination=DestinationDto( position=PositionDto.from_model(engine.board.destination.position) ), ) @router.get( "/player/{uuid}", response_model=PlayerInfoResponseDto, status_code=status.HTTP_201_CREATED, ) async def get_player_info( uuid: str, engine: GameEngine = Depends(get_game_engine), ) -> MovePlayerResponseDto: player = engine.players.find(uuid) 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_404_NOT_FOUND, detail="Player kicked out due to inactivity", ) return PlayerInfoResponseDto(player=PlayerDto.from_model(player)) @router.post( "/player/{uuid}/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 uuid not valid, probably due to inactivity", }, status.HTTP_409_CONFLICT: { "model": ErrorResponseDto, "description": " Position out of bounds or collision with an object", }, }, ) async def move_player( uuid: str, direction: Direction, response: Response, engine: GameEngine = Depends(get_game_engine), ) -> MovePlayerResponseDto: player = engine.players.find(uuid) 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_404_NOT_FOUND, detail="Player kicked out due to inactivity", ) try: move_result = 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" ) if move_result == PlayerMoveResult.DESTINATION_REACHED: response.status_code = status.HTTP_200_OK return MovePlayerResponseDto(player=PlayerDto.from_model(player))