3
votes

I'm trying to write a script to randomise a round-robin schedule for a tournament.

The constraints are:

  • 8 Teams
  • Teams face each other twice, once at home and once away
  • 14 weeks, one game for each team per week

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
1

1 Answers

2
votes

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.

Code:

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))

Sample Output:

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