botWebWars/backend/app/game.py

100 lines
3.3 KiB
Python
Raw Normal View History

import asyncio
import random
import uuid
from typing import Dict, List, Optional, Tuple
from app.config import settings
from app.models import BoardConfig, BoardState, Player, PlayerCreate
class GameEngine:
def __init__(self):
self._lock = asyncio.Lock()
self.players: Dict[str, Player] = {}
self.config = BoardConfig(
min_x=settings.GRID_MIN_X,
max_x=settings.GRID_MAX_X,
min_y=settings.GRID_MIN_Y,
max_y=settings.GRID_MAX_Y,
grid_cells_x=settings.GRID_MAX_X - settings.GRID_MIN_X,
grid_cells_y=settings.GRID_MAX_Y - settings.GRID_MIN_Y,
)
def _get_occupied_coordinates(self) -> set[Tuple[int, int]]:
return {(p.x, p.y) for p in self.players.values()}
def _find_random_free_position(self) -> Tuple[int, int]:
occupied = self._get_occupied_coordinates()
total_possible = (self.config.max_x - self.config.min_x + 1) * (
self.config.max_y - self.config.min_y + 1
)
if len(occupied) >= total_possible:
# All spots taken, fallback to random any spot
return (
random.randint(self.config.min_x, self.config.max_x),
random.randint(self.config.min_y, self.config.max_y),
)
# Fast random sampling
for _ in range(100):
rx = random.randint(self.config.min_x, self.config.max_x)
ry = random.randint(self.config.min_y, self.config.max_y)
if (rx, ry) not in occupied:
return (rx, ry)
# Fallback exhaustive search if dense
all_coords = [
(x, y)
for x in range(self.config.min_x, self.config.max_x + 1)
for y in range(self.config.min_y, self.config.max_y + 1)
if (x, y) not in occupied
]
return random.choice(all_coords)
async def register_player(self, player_in: PlayerCreate) -> Player:
async with self._lock:
player_id = f"bot_{uuid.uuid4().hex[:8]}"
x, y = self._find_random_free_position()
player = Player(
id=player_id,
name=player_in.name,
color=player_in.color,
x=x,
y=y,
)
self.players[player_id] = player
return player
async def get_player(self, player_id: str) -> Optional[Player]:
async with self._lock:
return self.players.get(player_id)
async def get_all_players(self) -> List[Player]:
async with self._lock:
return list(self.players.values())
async def remove_player(self, player_id: str) -> bool:
async with self._lock:
if player_id in self.players:
del self.players[player_id]
return True
return False
async def reset(self) -> None:
async with self._lock:
self.players.clear()
async def get_board_state(self) -> BoardState:
async with self._lock:
players_list = list(self.players.values())
return BoardState(
config=self.config,
player_count=len(players_list),
players=players_list,
)
# Global game engine instance
game_engine = GameEngine()