import asyncio import random from typing import Optional from .models import Room, RoomState, Horse, HorseState from .config import Config class RaceEngine: def __init__(self, config: Config): self.config = config self.active_tasks: dict[str, asyncio.Task] = {} async def start_race(self, room: Room) -> asyncio.Task: """Start race progression loop.""" task = asyncio.create_task(self._race_loop(room)) self.active_tasks[room.scope] = task return task async def _race_loop(self, room: Room): """Main race progression loop.""" room.state = RoomState.RUNNING while room.state == RoomState.RUNNING: await asyncio.sleep(self.config.RACE_TICK_INTERVAL) room.tick_count += 1 for horse in room.horses.values(): if horse.state == HorseState.RACING: distance = max(0, random.gauss(10, 3)) horse.position += distance finished_horses = [ h for h in room.horses.values() if h.position >= self.config.RACE_DISTANCE ] if finished_horses: champion = self._determine_champion(finished_horses) room.champion_name = champion.name room.state = RoomState.FINISHED break def _determine_champion(self, horses: list[Horse]) -> Horse: """Determine champion from tied horses.""" if len(horses) == 1: return horses[0] while len(horses) > 1: distances = [max(0, random.gauss(10, 3)) for _ in horses] max_distance = max(distances) horses = [h for h, d in zip(horses, distances) if d == max_distance] return horses[0] def stop_race(self, scope: str): """Stop race and cancel task.""" if scope in self.active_tasks: task = self.active_tasks[scope] if not task.done(): task.cancel() del self.active_tasks[scope]