botWebWars/backend/app/api/routes.py

267 lines
7.1 KiB
Python
Raw Normal View History

2026-09-05 21:20:39 +00:00
from typing import List, Optional
from fastapi import APIRouter, HTTPException, Query, status
from app.api.websocket import manager
from app.game import game_engine
2026-09-05 21:20:39 +00:00
from app.models import (
ApiResponse,
AvailableMovesResponse,
BoardState,
MoveCheckResult,
MoveRequest,
MoveResponse,
Player,
PlayerCreate,
TurnInfo,
)
router = APIRouter()
@router.get("/health", response_model=ApiResponse, tags=["System"])
async def health_check():
return ApiResponse(
success=True,
message="botWebWars backend service is healthy",
data={"service": "FastAPI", "status": "running"},
)
@router.post(
"/players",
response_model=Player,
status_code=status.HTTP_201_CREATED,
summary="Register a new player on the board",
tags=["Players"],
)
@router.post(
"/register",
response_model=Player,
status_code=status.HTTP_201_CREATED,
include_in_schema=False,
)
async def register_player(player_in: PlayerCreate):
player = await game_engine.register_player(player_in)
2026-09-05 21:20:39 +00:00
board_state = await game_engine.get_board_state()
# Broadcast to all connected WebSocket clients
await manager.broadcast({
"event": "player_joined",
"player": player.model_dump(),
2026-09-05 21:20:39 +00:00
"player_count": board_state.player_count,
"turn": board_state.turn.model_dump(),
})
return player
@router.get(
"/players",
response_model=List[Player],
summary="List all registered players",
tags=["Players"],
)
async def list_players():
return await game_engine.get_all_players()
@router.get(
"/players/{player_id}",
response_model=Player,
summary="Get details of a specific player",
tags=["Players"],
)
async def get_player(player_id: str):
player = await game_engine.get_player(player_id)
if not player:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Player '{player_id}' not found",
)
return player
@router.delete(
"/players/{player_id}",
response_model=ApiResponse,
summary="Remove a player from the board",
tags=["Players"],
)
async def remove_player(player_id: str):
removed = await game_engine.remove_player(player_id)
if not removed:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Player '{player_id}' not found",
)
2026-09-05 21:20:39 +00:00
board_state = await game_engine.get_board_state()
await manager.broadcast({
"event": "player_left",
"player_id": player_id,
2026-09-05 21:20:39 +00:00
"player_count": board_state.player_count,
"turn": board_state.turn.model_dump(),
})
return ApiResponse(
success=True,
message=f"Player '{player_id}' successfully removed",
)
2026-09-05 21:20:39 +00:00
# ==========================================
# Movement & Turn Endpoints
# ==========================================
@router.get(
"/players/{player_id}/available-moves",
response_model=AvailableMovesResponse,
summary="Check availability of all 8 movement directions (cardinal & diagonal)",
tags=["Movement"],
)
async def get_available_moves(player_id: str):
try:
return await game_engine.get_available_moves(player_id)
except KeyError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Player '{player_id}' not found",
)
@router.get(
"/players/{player_id}/check-move",
response_model=MoveCheckResult,
summary="Check if a specific direction is available for movement",
tags=["Movement"],
)
async def check_single_move(
player_id: str,
direction: str = Query(
...,
description="Direction to check: UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT (or N, S, E, W, NW, NE, SW, SE)",
),
):
try:
return await game_engine.check_single_move(player_id, direction)
except KeyError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Player '{player_id}' not found",
)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
@router.post(
"/players/{player_id}/move",
response_model=MoveResponse,
summary="Move player in a cardinal or diagonal direction during their turn",
tags=["Movement"],
)
async def move_player(player_id: str, move_req: MoveRequest):
try:
dx, dy, dir_name = move_req.get_delta()
result = await game_engine.move_player(player_id, dx, dy, dir_name)
# Broadcast move event to all connected WebSocket clients
await manager.broadcast({
"event": "player_moved",
"player": result.player.model_dump(),
"direction": result.direction,
"previous_position": result.previous_position,
"new_position": result.new_position,
"turn": result.turn.model_dump(),
})
return result
except KeyError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Player '{player_id}' not found",
)
except PermissionError as e:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=str(e),
)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
@router.post(
"/players/{player_id}/pass",
response_model=TurnInfo,
summary="Pass the current turn to the next player",
tags=["Movement"],
)
async def pass_turn(player_id: str):
try:
next_turn = await game_engine.pass_turn(player_id)
await manager.broadcast({
"event": "turn_passed",
"passed_by": player_id,
"turn": next_turn.model_dump(),
})
return next_turn
except KeyError:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Player '{player_id}' not found",
)
except PermissionError as e:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=str(e),
)
@router.get(
"/turn",
response_model=TurnInfo,
summary="Get current turn details and round information",
tags=["Turn"],
)
async def get_turn():
board = await game_engine.get_board_state()
return board.turn
# ==========================================
# Board Endpoints
# ==========================================
@router.get(
"/board",
response_model=BoardState,
summary="Get full board configuration and player positions",
tags=["Board"],
)
async def get_board():
return await game_engine.get_board_state()
@router.post(
"/board/reset",
response_model=ApiResponse,
summary="Reset the board and clear all players",
tags=["Board"],
)
async def reset_board():
await game_engine.reset()
2026-09-05 21:20:39 +00:00
board = await game_engine.get_board_state()
await manager.broadcast({
"event": "board_reset",
"player_count": 0,
2026-09-05 21:20:39 +00:00
"turn": board.turn.model_dump(),
})
return ApiResponse(
success=True,
message="Board reset successfully. All players cleared.",
)