aboutsummaryrefslogtreecommitdiff
path: root/src/match_up/controller.py
blob: 3b4a6f9d4ea2466c25c98a4616899006e3e43fae (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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