Skip to content

Instantly share code, notes, and snippets.

@e4c5
Last active February 9, 2024 08:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save e4c5/31e37c374df53fd68666 to your computer and use it in GitHub Desktop.
Save e4c5/31e37c374df53fd68666 to your computer and use it in GitHub Desktop.
A round robing pairing generator where you are control the round in which certain players get the bye or meet certain other players. If a large number of rounds need to be fixed in this manner and they involve players other than the Bye, there is no guarantee that a pairing exists.
import sys
import random
from itertools import permutations
class RoundRobin(object):
'''
Usage:
r = RoundRobin(['Bye','Nigel','Wellignton','Andrew','Sherwin','Chollapat','David','Craig'])
r.fix_pairing('Nigel',5, 'Bye')
r.fix_pairing( 'Craig',0,'Bye')
r.brute_force()
Note: if you have an odd number of players you need to add a player called 'Bye' as in the
example above.
'''
def __init__(self,players):
self.players = players
self.num_players = len(self.players)
self.num_rounds = self.num_players
self.games = [[None for x in range(self.num_rounds)] for x in range(self.num_players)]
self.fixed_rounds = [ [] for x in range(self.num_rounds)]
self.matches = 0
def print_table(self, dr = False):
'''
Prints the generated pairing table. If this is a double round robin
tournament set dr = True
'''
sys.stdout.write("{:<10}".format(''))
for rnd in range(1,self.num_players):
if dr:
sys.stdout.write("{:<10}".format('{0},{1}'.format(rnd*2-1,rnd*2)))
else :
sys.stdout.write("{:<10}".format(rnd))
print()
for i in range(self.num_players):
sys.stdout.write("{:<10}".format(self.players[i]))
for j in self.games[i]:
if j is not None:
if j == -1:
p = 'XX'
else :
p = self.players[j]
sys.stdout.write("{:<10}".format(p))
else :
sys.stdout.write("{:<10}".format('None'))
print()
def berger_table(self):
'''
Produces a Berger table. Standard pairing with no fixing
'''
rotator = [i for i in range(self.num_players)]
mode = len(rotator)//2
for rnd in range(self.num_rounds):
first = rotator[0:mode]
second = rotator[mode:]
second.reverse()
#print first, second
for idx in range(mode):
player = first[idx]
opponent = second[idx]
self.games[player][rnd] = opponent
self.games[opponent][rnd] = player
popped = rotator.pop()
rotator.insert(1,popped)
def berger_table_with_fixes(self, permutation):
'''
Brute force a solution to the pairing problem.
A solution might not always exists. The possibilities that needs to be
examined grows as N! so for large number it might never complete
'''
self.players = permutation
rotator = [i for i in range(self.num_players)]
mode = len(rotator)/2
for rnd in range(self.num_rounds):
first = rotator[0:mode]
second = rotator[mode:]
second.reverse()
#print first, second
for idx in range(mode):
player = first[idx]
opponent = second[idx]
self.games[player][rnd] = opponent
self.games[opponent][rnd] = player
if self.fixed_rounds[rnd]:
for p,o in self.fixed_rounds[rnd]:
player_idx = self.players.index(p)
opponent_idx = self.players.index(o)
if self.games[player_idx][rnd] != opponent_idx :
return False
popped = rotator.pop()
rotator.insert(1,popped)
self.matches +=1
return True
def fix_pairing(self, player, rnd, opponent):
''' Marks player has having to play opponent in round n '''
self.fixed_rounds[ rnd ].append( (player, opponent) )
def brute_force(self):
'''
Brute force method to find a pairing that meets all player requirements.
'''
i = 0
for perm in permutations(r.players):
i +=1
if self.berger_table_with_fixes( list(perm)):
self.print_table()
break
if i == 100000000:
break
@mitchbear
Copy link

The numbers used to specify the round to be fixed are zero based in the input, but one based in the output - you should be consistent.
You should also have a parameter to specify the number of rounds to be output (e.g. the number of rounds in round robin.)

@danielmerino
Copy link

Congratulations, this is just what I was looking for.

An usual case in chess championships is a round-robin tournament with several players from the same club. These players must be paired as soon as possible to avoid cheating (i.e. a player who has no chances of winning plays against a player with chances in the last round, and both are of the same club).

This is solved with a simple Berger table where the players are assigned, but it's not a good solution, often having pairings in the last rounds.

I have tested your algorithm manually entering the first rounds of a real use case (8 players from three clubs, with 4 players in one club and 2 players in the others). It works nice, but soon the fixed pairings are impossible and some players of the same club are paired in the last rounds.

An algorithm which would receive some players of several clubs and would find the best permutation for this use case would be highly useful in chess world! Just finding the Berger table where all players of the same clubs are paired before.

If you manage to find the solution, please share it!
Thanks and best regards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment