import json import asyncio import random import math from typing import Set, Dict, Optional from fastapi import WebSocket from datetime import datetime class ConnectionManager: def __init__(self): self.active_connections: Dict[int, Set[WebSocket]] = {} self._lock = asyncio.Lock() async def connect(self, websocket: WebSocket, user_id: int): await websocket.accept() async with self._lock: if user_id not in self.active_connections: self.active_connections[user_id] = set() self.active_connections[user_id].add(websocket) async def disconnect(self, websocket: WebSocket, user_id: int): async with self._lock: if user_id in self.active_connections: self.active_connections[user_id].discard(websocket) if not self.active_connections[user_id]: del self.active_connections[user_id] async def send_personal(self, user_id: int, message: dict): async with self._lock: if user_id in self.active_connections: dead = set() for ws in self.active_connections[user_id]: try: await ws.send_json(message) except Exception: dead.add(ws) self.active_connections[user_id] -= dead if not self.active_connections[user_id]: del self.active_connections[user_id] async def broadcast(self, message: dict): async with self._lock: dead: Dict[int, Set[WebSocket]] = {} for uid, sockets in self.active_connections.items(): for ws in sockets: try: await ws.send_json(message) except Exception: dead.setdefault(uid, set()).add(ws) for uid, sockets in dead.items(): self.active_connections[uid] -= sockets if not self.active_connections[uid]: del self.active_connections[uid] async def broadcast_except(self, exclude_user_id: int, message: dict): async with self._lock: for uid, sockets in self.active_connections.items(): if uid == exclude_user_id: continue for ws in sockets: try: await ws.send_json(message) except Exception: pass @property def online_count(self) -> int: """Количество уникальных пользователей онлайн""" return len(self.active_connections) manager = ConnectionManager() # ─── Crash Game State ──────────────────────────────────────────────────────── class CrashGameState: def __init__(self): self.running = False self.current_multiplier = 1.0 self.crash_point = 1.0 self.bets: Dict[int, float] = {} self.cashed_out: Dict[int, float] = {} self.phase = "waiting" self.timer = 0 self.round = 0 self.history: list = [] self._start_time = 0.0 self._elapsed = 0.0 def generate_crash_point(self) -> float: """True CS2-style provably fair crash point using exponential distribution""" # Используем распределение: чем выше множитель, тем меньше шанс r = random.random() # Формула: crash_point = 1 / (1 - r) с ограничением # Это даёт экспоненциальное распределение crash_point = 1.0 / (1.0 - r * 0.99) # В редких случаях может быть очень высоким (>1000), ограничиваем crash_point = min(crash_point, 1000.0) return round(crash_point, 2) def get_multiplier_at_time(self, elapsed_seconds: float) -> float: """Экспоненциальный рост множителя: e^(t * speed)""" speed = 0.08 # скорость роста return round(math.exp(elapsed_seconds * speed), 2) def get_time_for_multiplier(self, mult: float) -> float: """Сколько времени нужно для достижения множителя""" return math.log(mult) / 0.08 def calculate_payout(self, bet_amount: float, cashout_at: float) -> float: return round(bet_amount * cashout_at, 2) def to_dict(self): return { "phase": self.phase, "round": self.round, "current_multiplier": self.current_multiplier, "crash_point": self.crash_point, "timer": self.timer, "bet_count": len(self.bets), "total_bets": sum(self.bets.values()), "cashed_out": {str(k): v for k, v in self.cashed_out.items()}, "history": self.history[-20:], } crash_game = CrashGameState()