I'm trying to write a script to randomise a round-robin schedule for a tournament.
The constraints are:
My code works fine in theory, but when it's generated it sometimes freezes on certain weeks when there are only two teams left for that week, and both possible games have already been played. I use a numpy array to check which matchups have been played.
At the moment my code looks like this:
import random
import numpy
regular_season_games = 14
regular_season_week = 0
checker = numpy.full((8,8), 0)
for x in range (0,8):
checker[x][x] = 1
teams_left = list(range(8))
print ("Week " + str(regular_season_week+1))
while (regular_season_week < regular_season_games):
game_set = False
get_away_team = False
while get_away_team == False:
Team_A = random.choice(teams_left)
if 0 in checker[:,Team_A]:
for x in range (0,8):
if checker[x][Team_A] == 0 and x in teams_left:
teams_left.remove(Team_A)
get_away_team = True
break
while game_set == False:
Team_B = random.choice(teams_left)
if checker[Team_B][Team_A] == 0:
teams_left.remove(Team_B)
print(str(Team_A) + " vs " + str(Team_B))
checker[Team_B][Team_A] = 1
game_set = True
if not teams_left:
print ("Week " + str(regular_season_week+2))
teams_left = list(range(8))
regular_season_week = regular_season_week + 1
I've used an adaptation of the scheduling algorithm from here to achieve this. Basically, we generate a list of the teams - list(range(8))
- and choose as our initial matchup 0 vs 4, 1 vs 5, 2 vs 6, 3 vs 7
. We then rotate the list, excluding the first element, and choose as our next matchup 0 vs 3, 7 vs 4, 1 vs 5, 2 vs 6
. We continue on in the following way until we have every pairing.
I've added a handler for home & away matches - if a pairing has already been played, we play the opposite home/away pairing. Below is the code, including a function to check if a list of games is valid, and a sample output.
import random
# Generator function for list of matchups from a team_list
def games_from_list(team_list):
for i in range(4):
yield team_list[i], team_list[i+4]
# Function to apply rotation to list of teams as described in article
def rotate_list(team_list):
team_list = [team_list[4]] + team_list[0:3] + team_list[5:8] + [team_list[3]]
team_list[0], team_list[1] = team_list[1], team_list[0]
return team_list
# Function to check if a list of games is valid
def checkValid(game_list):
if len(set(game_list)) != len(game_list):
return False
for week in range(14):
teams = set()
this_week_games = game_list[week*4:week*4 + 4]
for game in this_week_games:
teams.add(game[0])
teams.add(game[1])
if len(teams) < 8:
return False
else:
return True
# Generate list of teams & empty list of games played
teams = list(range(8))
games_played = []
# Optionally shuffle teams before generating schedule
random.shuffle(teams)
# For each week -
for week in range(14):
print(f"Week {week + 1}")
# Get all the pairs of games from the list of teams.
for pair in games_from_list(teams):
# If the matchup has already been played:
if pair in games_played:
# Play the opposite match
pair = pair[::-1]
# Print the matchup and append to list of games.
print(f"{pair[0]} vs {pair[1]}")
games_played.append(pair)
# Rotate the list of teams
teams = rotate_list(teams)
# Checks that the list of games is valid
print(checkValid(games_played))
Week 1
0 vs 7
4 vs 3
6 vs 1
5 vs 2
Week 2
0 vs 3
7 vs 1
4 vs 2
6 vs 5
Week 3
0 vs 1
3 vs 2
7 vs 5
4 vs 6
Week 4
0 vs 2
1 vs 5
3 vs 6
7 vs 4
Week 5
0 vs 5
2 vs 6
1 vs 4
3 vs 7
Week 6
0 vs 6
5 vs 4
2 vs 7
1 vs 3
Week 7
0 vs 4
6 vs 7
5 vs 3
2 vs 1
Week 8
7 vs 0
3 vs 4
1 vs 6
2 vs 5
Week 9
3 vs 0
1 vs 7
2 vs 4
5 vs 6
Week 10
1 vs 0
2 vs 3
5 vs 7
6 vs 4
Week 11
2 vs 0
5 vs 1
6 vs 3
4 vs 7
Week 12
5 vs 0
6 vs 2
4 vs 1
7 vs 3
Week 13
6 vs 0
4 vs 5
7 vs 2
3 vs 1
Week 14
4 vs 0
7 vs 6
3 vs 5
1 vs 2
True