Skip to content

Instantly share code, notes, and snippets.

@MathyFurret
Created April 12, 2024 04:00
Show Gist options
  • Save MathyFurret/d97df0618a736d6cb8ff72f1f4d1ad27 to your computer and use it in GitHub Desktop.
Save MathyFurret/d97df0618a736d6cb8ff72f1f4d1ad27 to your computer and use it in GitHub Desktop.
import random
import itertools
import math
from collections import Counter
class Player:
def __init__(self, id, *, is_inactive=False):
self.id = id
self.is_inactive = is_inactive
self.wins = 0
self.losses = 0
self.games_inactive = 0
self.dropped = False
def __repr__(self):
return f'Player({self.id}, {'in' if self.is_inactive else ''}active)'
def will_play(self):
if self.is_inactive:
return False
return random.random() > 0.1
class BaseTournament:
def __init__(self, player_count, *, max_inactive_rounds=0):
self.players = [Player(i, is_inactive=i % 10 == 5) for i in range(player_count)]
self.remaining_players = self.players.copy()
self.max_inactive_rounds = max_inactive_rounds
self.round = 1
n = 1
while n < player_count:
n *= 2
self.bracket_size = n
def next_round(self):
pass
def run(self):
for _ in range(round(math.log2(self.bracket_size))):
self.next_round()
# print('---')
inactives = [p for p in self.players if p.is_inactive]
best_inactive = max(inactives, key=lambda p: p.wins)
# print(f"Furthest advancing inactive player: Player {best_inactive.id} ({best_inactive.wins} wins)")
# print(f"Average wins for inactive player: {statistics.mean(p.wins for p in inactives)}")
return best_inactive.wins
def get_results(self, p1, p2):
if p2 is None:
# print(f"Player {p1.id} advances on bye")
p1.wins += 1
return (p1, p2)
p1active = p1.will_play()
p2active = p2.will_play()
if p1active:
p1.games_inactive = 0
p1.dropped = False
else:
p1.games_inactive += 1
if self.max_inactive_rounds > 0 and p1.games_inactive >= self.max_inactive_rounds:
p1.dropped = True
if p2active:
p2.games_inactive = 0
else:
p2.games_inactive += 1
if self.max_inactive_rounds > 0 and p2.games_inactive >= self.max_inactive_rounds:
p2.dropped = True
if p1active == p2active:
if not p1active:
if p1.dropped and not p2.dropped:
result = (p2, p1)
elif p2.dropped and not p1.dropped:
result = (p1, p2)
else:
result = (p1, p2) if random.random() < 0.5 else (p2, p1)
else:
result = (p1, p2) if random.random() < 0.5 else (p2, p1)
# if p1active:
# print(f"Player {result[0].id} beat Player {result[1].id}")
# else:
# print(f"Player {result[0].id} won by coinflip over Player {result[1].id}")
else:
if p1active:
result = (p1, p2)
else:
result = (p2, p1)
# print(f"Player {result[0].id} won by default over Player {result[1].id}")
result[0].wins += 1
result[1].losses += 1
return result
class EliminationTournament(BaseTournament):
def next_round(self):
players_shuffled = random.sample(self.remaining_players, len(self.remaining_players))
pairings = itertools.zip_longest(players_shuffled[0:self.bracket_size//2**self.round], players_shuffled[self.bracket_size//2**self.round:])
advancing_players = []
for p1, p2 in pairings:
result = self.get_results(p1, p2)
advancing_players.append(result[0])
self.remaining_players = advancing_players
self.round += 1
class SwissTournament(BaseTournament):
def next_round(self):
player_bins = []
for i in range(min(self.round, 3)):
player_bins.append([p for p in self.remaining_players if p.losses == i])
for [i, bin] in enumerate(player_bins):
bin_shuffled = random.sample(bin, len(bin))
if self.round <= 3 and i == self.round - 1:
# bottom bracket has byes
partition = self.bracket_size // 2**self.round
pairings = itertools.zip_longest(bin_shuffled[0:partition], bin_shuffled[partition:])
else:
if len(bin_shuffled) % 2 == 1 and i < len(player_bins) - 1:
plays_down = bin_shuffled.pop()
player_bins[i+1].append(plays_down)
pairings = itertools.zip_longest(bin_shuffled[0:math.ceil(len(bin) / 2)], bin_shuffled[math.ceil(len(bin) / 2):])
for p1, p2 in pairings:
self.get_results(p1, p2)
self.remaining_players = [p for p in self.remaining_players if p.losses < 3]
self.round += 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment