aboutsummaryrefslogtreecommitdiff
path: root/controller.py
blob: 751586e260934697b764c003ad11f52a511511a5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from typing import Tuple
from operator import attrgetter
from dataclasses import dataclass
from utilities import sample_list
from model import Team, Game, PlayerList, CourtList, MAX_LEVEL, PLAYER_PER_COURT


def _create_placeholders(courts: dict, name: str) -> list[Game]:
    dummy_team = Team(player1=name, player2=name)
    games = [Game(court, dummy_team, dummy_team) for court in courts]
    return games


def _normalize_skill(players: dict, new_max: int) -> int:
    player_levels = []
    for _, player in players.items():
        skill_level = player.skill
        new_level = round((new_max - 1) * (skill_level / MAX_LEVEL)) + 1
        player_levels.append(new_level)
    return player_levels


def _create_groups_of_four_players(players: dict, new_max: int) -> list[dict]:
    player_levels = _normalize_skill(players, new_max)
    identifiers = sample_list([*players], player_levels)
    clumps = []
    for i in range(0, len(players), PLAYER_PER_COURT):
        clumps.append(identifiers[i : i + PLAYER_PER_COURT])
    combinations = [{id: players[id] for id in clump} for clump in clumps]
    return combinations


def _create_games(courts: dict, players: list[dict], levels: int):
    grouped_players = _create_groups_of_four_players(players, levels)
    games = []
    for court, players in zip(courts, grouped_players):
        ids = [*players]
        team1 = Team(player1=players[ids[0]].name, player2=players[ids[3]].name)
        team2 = Team(player1=players[ids[1]].name, player2=players[ids[2]].name)
        games.append(Game(court, team1, team2))
    return games


@dataclass
class RoundGenerator:
    courts: CourtList
    players: PlayerList
    round: int = 0
    proposal: tuple = tuple()

    def __post_init__(self) -> None:
        self.history = {key: 0 for key, _ in self.players.get_players().items()}
        self.games = self._generate_proposal(MAX_LEVEL)[0]

    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 _select_players(self, count: int) -> dict:
        active_players = self.players.get_active_players()
        identifiers = [*active_players]
        play_count = [self.history[player_id] for player_id in active_players]
        ids = sample_list(identifiers, play_count, count)
        return {player_id: active_players[player_id] for player_id in ids}

    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
        selected_players = self._select_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
        for player_id in proposed_players:
            self.history[player_id] = self.history[player_id] + 1
        self.proposal = tuple()
        self.round += 1
        return self.games