I'm currently trying to create schedules for different leagues. I have read questions on round robin and am also able to create a schedule for a season with a first an a second half. However, what I'm trying to achieve is:
- Leagues with 16 or 32 teams
- 60 games per season (in 16 teams leagues play every opponent 4x, in 32 team leagues play every opponent twice)
- Five to eight Games per day
- No multiple games per team per day
- Even distribution of home and away games
- Games on consecutive days are possible, but not more than two in a row
What have I tried so far? As a first step, I created a list of all possible matchups with itertools:
import itertools
p = ['A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19', 'A20', 'A21', 'A22', 'A23', 'A24', 'A25', 'A26', 'A27', 'A28', 'A29', 'A30', 'A31']
f = list(itertools.combinations(p, 2))
# [('A0', 'A1'), ('A0', 'A2'), ('A0', 'A3'), ... ]
From here on, I might chose a wrong path, deciding to work with indexes. Hence, I created a list of lists c
, where every sub-list is a copy of f
. The list f
is represented n times in c
depending on the size of the league (16 teams: 4x; 32 teams: 2x).
Edit:
A little update as I'm still working on this:
- I implemented the even distribution of home/away games
- The script is now returning an actual schedule with 5 to 8 games per day, although with exceptions
See the following code for a 32 teams league:
import itertools
import random
def get_current_list(c):
if c[0] != 0:
if len(c[0]) != 0:
return 0
if len(c[0]) == 0 and len(c[1]) != 0:
return 1
else:
return 1
def check_list_status(c,cl):
if len(c[cl]) < 9:
return True
else:
return False
def get_matchup(c, cl):
games_today = []
how_many_games_today = random.randint(5,8)
error = False
for game in range(how_many_games_today):
game_not_found = True
counter = 0
while game_not_found:
list_length = len(c[cl])
index = list_length - 1
counter += 1
if counter > 100:
error = True
break
choice_index = random.randint(0,index)
choice = c[cl][choice_index]
invalid = False
for t_d in games_today:
if choice[0] in t_d or choice[1] in t_d:
invalid = True
break
else:
invalid = False
if not invalid:
games_today.append(choice)
c[cl].pop(choice_index)
game_not_found = False
else:
continue
if error: break
if error:
list_ind_max = len(c[cl]) - 1
single_ind = random.randint(0,list_ind_max)
games_today = [c[cl][single_ind]]
c[cl].pop(single_ind)
return games_today,c
def finish_this_list(c, current_list):
ret_list = []
for x in c[current_list]:
ret_list.append(x)
c[current_list] = 0
return c[current_list],c
p = ['A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19', 'A20', 'A21', 'A22', 'A23', 'A24', 'A25', 'A26', 'A27', 'A28', 'A29', 'A30', 'A31']
f = list(itertools.combinations(p, 2))
g = list(itertools.combinations(p, 2))
g = [x[::-1] for x in g]
c = [f, g]
schedule = {}
condition1 = True
day = 0
while condition1:
day += 1
current_list = get_current_list(c)
last_round = check_list_status(c,current_list)
if not last_round:
todays_matchups,c = get_matchup(c, current_list)
schedule[day] = todays_matchups
else:
schedule[day],c = finish_this_list(c, current_list)
if current_list == 1:
condition1 = False
print(schedule)
However, as you can see, this approach is a bit messy and still doesn't consider the rule of no more than two games in a row for each team. Moreover, I implemented some workarounds that don't feel very pythonic. Especially the counter in the main function to prevent ending up in an endless loop (if the script isn't able to find another matchup that doesn't include a team that is already playing on that day) and how the script manages if there are just a few entries left in the list (right now it's taking everything as one last day if there are less than nine entries in the list, although there could be a team playing twice on that day) appear to me like they need improvement.
Any help/hint on how to proceed best would be appreciated.