-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtournament_calculators.py
More file actions
164 lines (122 loc) · 4.4 KB
/
tournament_calculators.py
File metadata and controls
164 lines (122 loc) · 4.4 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from tournament_core import IPointsCalculator, Match, MatchResult
class StandardPointsCalculator(IPointsCalculator):
"""
Standard points calculator.
Win = 1 point, Draw = 0.5 points, Loss = 0 points.
"""
def get_calculator_name(self) -> str:
return "standard"
def calculate_points(
self, player_id: str, match: Match, result: MatchResult
) -> float:
"""Calculate points for standard scoring."""
if result.is_draw:
return 1.0 / len(match.player_ids)
if player_id in result.winner_ids:
return 1.0
return 0.0
class ThreePointsCalculator(IPointsCalculator):
"""
Three-points-for-win calculator (like soccer).
Win = 3 points, Draw = 1 point, Loss = 0 points.
"""
def get_calculator_name(self) -> str:
return "three_point"
def calculate_points(
self, player_id: str, match: Match, result: MatchResult
) -> float:
"""Calculate points for three-point scoring."""
if result.is_draw:
return 1.0
if player_id in result.winner_ids:
return 3.0
return 0.0
class RankingPointsCalculator(IPointsCalculator):
"""
Ranking-based points calculator for n-player games.
Points awarded based on finishing position.
1st place = n points, 2nd = n-1 points, etc.
"""
def get_calculator_name(self) -> str:
return "ranking"
def calculate_points(
self, player_id: str, match: Match, result: MatchResult
) -> float:
"""Calculate points based on ranking."""
if not result.rankings or player_id not in result.rankings:
return 0.0
rank = result.rankings[player_id]
total_players = len(match.player_ids)
points = max(0, total_players - rank + 1)
return float(points)
class EloCalculator(IPointsCalculator):
"""
Elo-style rating calculator.
Points based on expected vs actual performance.
"""
def __init__(self, k_factor: float = 32):
self.k_factor = k_factor
def get_calculator_name(self) -> str:
return "elo"
def calculate_points(
self, player_id: str, match: Match, result: MatchResult
) -> float:
"""
Calculate Elo rating change.
Note: This is a simplified implementation.
Full Elo requires opponent ratings.
"""
if result.is_draw:
actual_score = 0.5
elif player_id in result.winner_ids:
actual_score = 1.0
else:
actual_score = 0.0
expected_score = 0.5
rating_change = self.k_factor * (actual_score - expected_score)
return rating_change
class PercentagePointsCalculator(IPointsCalculator):
"""
Percentage-based calculator for n-player games.
Points distributed based on how many players you beat.
"""
def get_calculator_name(self) -> str:
return "percentage"
def calculate_points(
self, player_id: str, match: Match, result: MatchResult
) -> float:
"""Calculate points as percentage of players beaten."""
if not result.rankings or player_id not in result.rankings:
return 0.0
rank = result.rankings[player_id]
total_players = len(match.player_ids)
players_beaten = total_players - rank
percentage = (
(players_beaten / (total_players - 1)) * 100 if total_players > 1 else 0
)
return percentage
class CustomWeightedCalculator(IPointsCalculator):
"""
Custom weighted calculator allowing different point values.
Useful for different game types or tournament formats.
"""
def __init__(self, weights: dict[int, float]):
"""
Initialize with custom weights.
Args:
weights: dict mapping rank to points (e.g., {1: 10, 2: 5, 3: 2, 4: 1})
"""
self.weights = weights
def get_calculator_name(self) -> str:
return "custom_weighted"
def calculate_points(
self, player_id: str, match: Match, result: MatchResult
) -> float:
"""Calculate points using custom weights."""
if result.is_draw:
total_points = sum(self.weights.values())
return total_points / len(match.player_ids)
if not result.rankings or player_id not in result.rankings:
return 0.0
rank = result.rankings[player_id]
return self.weights.get(rank, 0.0)