from typing import Tuple from operator import attrgetter from dataclasses import dataclass, field from match_up.utilities import sample_list, Timer from match_up.model import ( Team, Game, Player, DisplayPlayer, PlayerList, CourtList, MAX_LEVEL, PLAYER_PER_COURT, ) def _create_placeholders(courts: dict, name: str) -> list[Game]: dummy_player = Player(first=name) dummy_team = Team( player1=DisplayPlayer(dummy_player), player2=DisplayPlayer(dummy_player) ) games = [Game(court, dummy_team, dummy_team) for court in courts] return games def _normalize_skill(level: int, new_max: int) -> int: new_level = round((new_max - 1) * (level / MAX_LEVEL)) + 1 return new_level def _select_players(players: list[Player], count: int) -> dict: identifiers = [player.id for player in players] play_count = [player.count for player in players] ids = sample_list(identifiers, play_count, count) selected_players = [player for player in players if player.id in ids] return selected_players def _create_groups_of_four_players( players: list[Player], new_max: int ) -> list[list[Player]]: identifiers = [player.id for player in players] player_levels = [_normalize_skill(player.level, new_max) for player in players] identifiers = sample_list(identifiers, player_levels) court_players = [] for i in range(0, len(players), PLAYER_PER_COURT): court_ids = identifiers[i : i + PLAYER_PER_COURT] selected_players = [player for player in players if player.id in court_ids] court_players.append(selected_players) return court_players def _create_games(courts: dict, players: list[Player], levels: int): court_players = _create_groups_of_four_players(players, levels) games = [] for court, players in zip(courts, court_players): team1 = Team( player1=DisplayPlayer(players[0]), player2=DisplayPlayer(players[3]) ) team2 = Team( player1=DisplayPlayer(players[1]), player2=DisplayPlayer(players[2]) ) games.append(Game(court, team1, team2)) return games @dataclass class RoundGenerator: courts: CourtList = field(default_factory=lambda: CourtList()) players: PlayerList = field(default_factory=lambda: PlayerList()) round: int = 0 proposal: tuple = tuple() def __post_init__(self) -> None: self.timer = Timer() self.games = _create_placeholders(self.courts.get_courts(), "---") def _separate_courts(self, courts: dict) -> Tuple[dict, dict]: possible_games = min(len(courts), self.players.get_possible_game_count()) courts_as_list = list(courts.items()) used_courts = dict(courts_as_list[:possible_games]) unused_courts = dict(courts_as_list[possible_games:]) return used_courts, unused_courts def _create_inactive_games(self) -> list[Game]: active_courts, reserved_courts = self.courts.separate_courts() reserved_games = _create_placeholders(reserved_courts, "RESERVED") _, unused_courts = self._separate_courts(active_courts) blank_games = _create_placeholders(unused_courts, "---") games = reserved_games + blank_games return games def _create_active_games(self, total_levels: int): active_courts, _ = self.courts.separate_courts() used_courts, _ = self._separate_courts(active_courts) required_player_count = len(used_courts) * PLAYER_PER_COURT active_players = self.players.get_players() selected_players = _select_players(active_players, required_player_count) games = _create_games(used_courts, selected_players, total_levels) return games, selected_players def _generate_proposal(self, total_levels: int) -> Tuple[list[Game], dict]: inactive_games = self._create_inactive_games() active_games, selected_players = self._create_active_games(total_levels) games = active_games + inactive_games games.sort(key=attrgetter("court")) return games, selected_players def clear(self) -> list[Game]: self.proposal = tuple() return self.games def propose(self, levels: int) -> list[Game]: self.proposal = self._generate_proposal(levels) return self.proposal[0] def confirm(self) -> list[Game]: proposed_games, proposed_players = self.proposal if self.games == proposed_games or len(proposed_games) == 0: return self.games self.games = proposed_games identifiers = [player.id for player in proposed_players] self.players.increment_game_count(identifiers) self.proposal = tuple() self.round += 1 self.timer.start() return self.games