from __future__ import annotations from dataclasses import dataclass, field from typing import Deque, Dict, List, Optional, Tuple from collections import deque from .protocol import Direction Coord = Tuple[int, int] @dataclass class Snake: snake_id: int head: Coord direction: Direction body: Deque[Coord] = field(default_factory=deque) # includes head at index 0 @property def length(self) -> int: return len(self.body) @dataclass class PlayerSession: player_id: int name: str color_id: int peer: object # transport-specific handle input_seq: int = 0 @dataclass class GameState: width: int height: int snakes: Dict[int, Snake] = field(default_factory=dict) apples: List[Coord] = field(default_factory=list) tick: int = 0 occupancy: Dict[Coord, Tuple[int, int]] = field(default_factory=dict) # cell -> (snake_id, index) def in_bounds(self, x: int, y: int) -> bool: return 0 <= x < self.width and 0 <= y < self.height def cell_free(self, x: int, y: int) -> bool: return (x, y) not in self.occupancy def occupy_snake(self, snake: Snake) -> None: for idx, (cx, cy) in enumerate(snake.body): self.occupancy[(cx, cy)] = (snake.snake_id, idx) def clear_snake(self, snake: Snake) -> None: for (cx, cy) in list(snake.body): self.occupancy.pop((cx, cy), None)