2026-09-05 21:20:39 +00:00
|
|
|
from typing import List, Optional
|
|
|
|
|
from fastapi import APIRouter, HTTPException, Query, status
|
2026-09-05 21:10:18 +00:00
|
|
|
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,
|
2026-09-05 21:55:07 +00:00
|
|
|
BattleRequest,
|
|
|
|
|
BattleResult,
|
2026-09-05 21:20:39 +00:00
|
|
|
BoardState,
|
2026-09-05 22:09:18 +00:00
|
|
|
BotMemoryResponse,
|
|
|
|
|
BotRadarResponse,
|
2026-09-05 21:20:39 +00:00
|
|
|
MoveCheckResult,
|
|
|
|
|
MoveRequest,
|
|
|
|
|
MoveResponse,
|
2026-09-05 21:55:07 +00:00
|
|
|
Party,
|
|
|
|
|
PartyDefeatResult,
|
|
|
|
|
PartyDirectForm,
|
|
|
|
|
PartyInvite,
|
|
|
|
|
PartyInviteCreate,
|
|
|
|
|
PartyInviteResponse,
|
2026-09-05 21:20:39 +00:00
|
|
|
Player,
|
|
|
|
|
PlayerCreate,
|
|
|
|
|
TurnInfo,
|
|
|
|
|
)
|
2026-09-05 21:10:18 +00:00
|
|
|
|
|
|
|
|
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"},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-09-05 21:55:07 +00:00
|
|
|
# ==========================================
|
|
|
|
|
# Player Registration & Management
|
|
|
|
|
# ==========================================
|
|
|
|
|
|
2026-09-05 21:10:18 +00:00
|
|
|
@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()
|
2026-09-05 21:55:07 +00:00
|
|
|
|
2026-09-05 21:10:18 +00:00
|
|
|
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(),
|
2026-09-05 21:10:18 +00:00
|
|
|
})
|
2026-09-05 21:55:07 +00:00
|
|
|
|
2026-09-05 21:10:18 +00:00
|
|
|
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:55:07 +00:00
|
|
|
|
2026-09-05 21:20:39 +00:00
|
|
|
board_state = await game_engine.get_board_state()
|
2026-09-05 21:10:18 +00:00
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "player_left",
|
|
|
|
|
"player_id": player_id,
|
2026-09-05 21:20:39 +00:00
|
|
|
"player_count": board_state.player_count,
|
2026-09-05 21:55:07 +00:00
|
|
|
"parties": [p.model_dump() for p in board_state.parties],
|
2026-09-05 21:20:39 +00:00
|
|
|
"turn": board_state.turn.model_dump(),
|
2026-09-05 21:10:18 +00:00
|
|
|
})
|
2026-09-05 21:55:07 +00:00
|
|
|
|
2026-09-05 21:10:18 +00:00
|
|
|
return ApiResponse(
|
|
|
|
|
success=True,
|
|
|
|
|
message=f"Player '{player_id}' successfully removed",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-09-05 22:09:18 +00:00
|
|
|
# ==========================================
|
|
|
|
|
# Bot Memory & Radar Endpoints
|
|
|
|
|
# ==========================================
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
"/players/{player_id}/memory",
|
|
|
|
|
response_model=BotMemoryResponse,
|
|
|
|
|
summary="Retrieve a bot's current location and visited locations memory",
|
|
|
|
|
tags=["Bot Intelligence"],
|
|
|
|
|
)
|
|
|
|
|
async def get_bot_memory(
|
|
|
|
|
player_id: str,
|
|
|
|
|
check_x: Optional[int] = Query(None, description="Check if bot has visited this X coordinate"),
|
|
|
|
|
check_y: Optional[int] = Query(None, description="Check if bot has visited this Y coordinate"),
|
|
|
|
|
):
|
|
|
|
|
try:
|
|
|
|
|
return await game_engine.get_bot_memory(player_id, check_x, check_y)
|
|
|
|
|
except KeyError:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
detail=f"Player '{player_id}' not found",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
"/players/{player_id}/radar",
|
|
|
|
|
response_model=BotRadarResponse,
|
|
|
|
|
summary="Scan surroundings for other bots, distance, allies/enemies, and recommended direction",
|
|
|
|
|
tags=["Bot Intelligence"],
|
|
|
|
|
)
|
|
|
|
|
async def get_bot_radar(player_id: str):
|
|
|
|
|
try:
|
|
|
|
|
return await game_engine.get_bot_radar(player_id)
|
|
|
|
|
except KeyError:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
detail=f"Player '{player_id}' not found",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-09-05 21:55:07 +00:00
|
|
|
# ==========================================
|
|
|
|
|
# Party & Squad Endpoints
|
|
|
|
|
# ==========================================
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
"/parties",
|
|
|
|
|
response_model=List[Party],
|
|
|
|
|
summary="List all active parties",
|
|
|
|
|
tags=["Parties"],
|
|
|
|
|
)
|
|
|
|
|
async def list_parties():
|
|
|
|
|
return await game_engine.get_all_parties()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
"/parties/{party_id}",
|
|
|
|
|
response_model=Party,
|
|
|
|
|
summary="Get details of a specific party",
|
|
|
|
|
tags=["Parties"],
|
|
|
|
|
)
|
|
|
|
|
async def get_party(party_id: str):
|
|
|
|
|
party = await game_engine.get_party(party_id)
|
|
|
|
|
if not party:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
detail=f"Party '{party_id}' not found",
|
|
|
|
|
)
|
|
|
|
|
return party
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/parties",
|
|
|
|
|
response_model=Party,
|
|
|
|
|
status_code=status.HTTP_201_CREATED,
|
|
|
|
|
summary="Directly form a party with adjacent linked members and an agreed leader",
|
|
|
|
|
tags=["Parties"],
|
|
|
|
|
)
|
|
|
|
|
async def form_party(form_req: PartyDirectForm):
|
|
|
|
|
try:
|
|
|
|
|
party = await game_engine.form_party(
|
|
|
|
|
member_ids=form_req.member_ids,
|
|
|
|
|
leader_id=form_req.leader_id,
|
|
|
|
|
name=form_req.name,
|
|
|
|
|
)
|
|
|
|
|
board_state = await game_engine.get_board_state()
|
|
|
|
|
|
|
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "party_formed",
|
|
|
|
|
"party": party.model_dump(),
|
|
|
|
|
"players": [p.model_dump() for p in board_state.players],
|
|
|
|
|
"turn": board_state.turn.model_dump(),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return party
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/parties/invite",
|
|
|
|
|
response_model=PartyInvite,
|
|
|
|
|
summary="Invite an adjacent bot to join/form a party with an agreed leader",
|
|
|
|
|
tags=["Parties"],
|
|
|
|
|
)
|
|
|
|
|
async def invite_to_party(invite_req: PartyInviteCreate):
|
|
|
|
|
try:
|
|
|
|
|
invite = await game_engine.invite_to_party(
|
|
|
|
|
inviter_id=invite_req.inviter_id,
|
|
|
|
|
invitee_id=invite_req.invitee_id,
|
|
|
|
|
proposed_leader_id=invite_req.proposed_leader_id,
|
|
|
|
|
party_name=invite_req.party_name,
|
|
|
|
|
)
|
|
|
|
|
return invite
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/parties/invites/{invite_id}/respond",
|
|
|
|
|
response_model=Optional[Party],
|
|
|
|
|
summary="Accept or reject a party invitation",
|
|
|
|
|
tags=["Parties"],
|
|
|
|
|
)
|
|
|
|
|
async def respond_to_invite(invite_id: str, resp: PartyInviteResponse):
|
|
|
|
|
try:
|
|
|
|
|
party = await game_engine.respond_to_invite(invite_id, resp.accept)
|
|
|
|
|
if party:
|
|
|
|
|
board_state = await game_engine.get_board_state()
|
|
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "party_updated",
|
|
|
|
|
"party": party.model_dump(),
|
|
|
|
|
"players": [p.model_dump() for p in board_state.players],
|
|
|
|
|
"turn": board_state.turn.model_dump(),
|
|
|
|
|
})
|
|
|
|
|
return party
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/parties/{party_id}/defeat",
|
|
|
|
|
response_model=PartyDefeatResult,
|
|
|
|
|
summary="Trigger defeat for a party: leader killed, respawns, score -1, remainder assigns new leader",
|
|
|
|
|
tags=["Parties"],
|
|
|
|
|
)
|
|
|
|
|
async def defeat_party(party_id: str):
|
|
|
|
|
try:
|
|
|
|
|
result = await game_engine.defeat_party(party_id)
|
|
|
|
|
board_state = await game_engine.get_board_state()
|
|
|
|
|
|
|
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "party_defeated",
|
|
|
|
|
"defeat_details": result.model_dump(),
|
|
|
|
|
"players": [p.model_dump() for p in board_state.players],
|
|
|
|
|
"parties": [p.model_dump() for p in board_state.parties],
|
|
|
|
|
"turn": board_state.turn.model_dump(),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/battles/fight",
|
|
|
|
|
response_model=BattleResult,
|
2026-09-05 22:09:18 +00:00
|
|
|
summary="Initiate a 3-bout D20 battle between two adjacent parties/bots",
|
2026-09-05 21:55:07 +00:00
|
|
|
tags=["Battles"],
|
|
|
|
|
)
|
|
|
|
|
async def fight_battle(battle_req: BattleRequest):
|
|
|
|
|
try:
|
|
|
|
|
result = await game_engine.battle(battle_req.challenger_id, battle_req.defender_id)
|
|
|
|
|
board_state = await game_engine.get_board_state()
|
|
|
|
|
|
|
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "battle_resolved",
|
|
|
|
|
"battle": result.model_dump(),
|
|
|
|
|
"players": [p.model_dump() for p in board_state.players],
|
|
|
|
|
"parties": [p.model_dump() for p in board_state.parties],
|
|
|
|
|
"turn": board_state.turn.model_dump(),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
2026-09-05 21:20:39 +00:00
|
|
|
# ==========================================
|
|
|
|
|
# Movement & Turn Endpoints
|
|
|
|
|
# ==========================================
|
|
|
|
|
|
|
|
|
|
@router.get(
|
|
|
|
|
"/players/{player_id}/available-moves",
|
|
|
|
|
response_model=AvailableMovesResponse,
|
2026-09-05 21:55:07 +00:00
|
|
|
summary="Check availability of all 8 movement directions for a bot or their party",
|
2026-09-05 21:20:39 +00:00
|
|
|
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(
|
|
|
|
|
...,
|
2026-09-05 21:55:07 +00:00
|
|
|
description="Direction to check: UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT",
|
2026-09-05 21:20:39 +00:00
|
|
|
),
|
|
|
|
|
):
|
|
|
|
|
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,
|
2026-09-05 21:55:07 +00:00
|
|
|
summary="Move player (or entire party if leader) in a cardinal or diagonal direction during turn",
|
2026-09-05 21:20:39 +00:00
|
|
|
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)
|
|
|
|
|
|
2026-09-05 21:55:07 +00:00
|
|
|
board_state = await game_engine.get_board_state()
|
2026-09-05 21:20:39 +00:00
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "player_moved",
|
|
|
|
|
"player": result.player.model_dump(),
|
|
|
|
|
"direction": result.direction,
|
2026-09-05 21:55:07 +00:00
|
|
|
"party_moved": result.party_moved,
|
|
|
|
|
"affected_players": [p.model_dump() for p in result.affected_players],
|
2026-09-05 21:20:39 +00:00
|
|
|
"previous_position": result.previous_position,
|
|
|
|
|
"new_position": result.new_position,
|
2026-09-05 21:55:07 +00:00
|
|
|
"players": [p.model_dump() for p in board_state.players],
|
2026-09-05 21:20:39 +00:00
|
|
|
"turn": result.turn.model_dump(),
|
|
|
|
|
})
|
|
|
|
|
|
2026-09-05 22:09:18 +00:00
|
|
|
if result.battle_triggered and result.battle_result:
|
|
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "battle_resolved",
|
|
|
|
|
"battle": result.battle_result.model_dump(),
|
|
|
|
|
"players": [p.model_dump() for p in board_state.players],
|
|
|
|
|
"parties": [p.model_dump() for p in board_state.parties],
|
|
|
|
|
"turn": board_state.turn.model_dump(),
|
|
|
|
|
})
|
|
|
|
|
|
2026-09-05 21:20:39 +00:00
|
|
|
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
|
|
|
|
|
# ==========================================
|
|
|
|
|
|
2026-09-05 21:10:18 +00:00
|
|
|
@router.get(
|
|
|
|
|
"/board",
|
|
|
|
|
response_model=BoardState,
|
2026-09-05 21:55:07 +00:00
|
|
|
summary="Get full board configuration, parties, and player positions",
|
2026-09-05 21:10:18 +00:00
|
|
|
tags=["Board"],
|
|
|
|
|
)
|
|
|
|
|
async def get_board():
|
|
|
|
|
return await game_engine.get_board_state()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/board/reset",
|
|
|
|
|
response_model=ApiResponse,
|
2026-09-05 21:55:07 +00:00
|
|
|
summary="Reset the board and clear all players and parties",
|
2026-09-05 21:10:18 +00:00
|
|
|
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()
|
2026-09-05 21:10:18 +00:00
|
|
|
await manager.broadcast({
|
|
|
|
|
"event": "board_reset",
|
|
|
|
|
"player_count": 0,
|
2026-09-05 21:20:39 +00:00
|
|
|
"turn": board.turn.model_dump(),
|
2026-09-05 21:10:18 +00:00
|
|
|
})
|
|
|
|
|
return ApiResponse(
|
|
|
|
|
success=True,
|
|
|
|
|
message="Board reset successfully. All players cleared.",
|
|
|
|
|
)
|