My question is focused on finding a constraint that ensures that teams stay in the same group throughout each round.
I am organizing a tournament with over 200 teams divided into 40+ groups. Each group will play for four rounds. The teams are placed into groups of six based on their age and strength. The final goal is to minimize the overall driving distance for the teams.
In a simplified version of the specific problem, I have 12 teams that need to be divided into three different groups based on age and strength, and they will play two rounds.
My code can assign the teams to three groups of four teams each in round 1, according to a predetermined list.
[[1,2,3,4,5], # In Group 1, 4 out of 5 teams can be placed: 1,2,3,4,5
[5,6,7,8,9], # In Group 2, 4 out of 5 teams can be placed: 5,6,7,8,9
[8,9,10,11,12]] # In Group 3, 4 out of 5 teams can be placed: 8,9,10,11,12
If Group 1 for round 1 is [1,2,3,4] the match will be team 1 vs 2 and team 3 vs 4.
I need to find a solution for round two that ensures that Group 1 is either [1,3,2,4] or [1,4,2,3] . I need all possible combinations and not just a combination, because the driving distance may vary depending on the team arrangements. For round 2 it could be better with 1 vs 3 than 1 vs 4. For six teams, I have 15 different combinations.
I have tried using AddAllowedAssignments and AddElement in different variations, but I have not been able to make them work since the groups are defined during solving and not before. I would really appriciate some suggestion.
from ortools.sat.python import cp_model
def main():
#SportsTournement
#Teams are placed into groups according to stregth and age
#Make a game plan with 3 rounds where the 4 teams in each group playing each other one time
GroupsList = [] #turple for possible allowed group combinations
GroupsTempList = []
GroupTypeList = [[1,2,3,4,5], # Group 1 can consist of 4 teams 1,2,3,4,5
[5,6,7,8,9], # Group 2 can consist of 4 teams 5,6,7,8,9
[8,9,10,11,12]] # Group 3 can consist of 4 teams 8,9,10,11,12
num_teams = max([max(i) for i in GroupTypeList]) # number of teams = 12
Groups = len(GroupTypeList) #Number of groups =3
GroupSize = 4 # 4 teams in each group
rounds = 2
GroupsList = [ #tuples of possible combinations
[[2,3,4,5],[1,2,3,5],[1,2,4,5],[1,3,4,5],[1,2,3,4]], # Possible combinations of group 1
[[5,6,7,8],[5,6,7,9],[5,6,8,9],[5,7,8,9],[6,7,8,9]], # Possible combinations of group 2
[[8,9,10,11],[8,9,10,12],[8,10,11,12],[9,10,11,12]] # Possible combinations of group 3
]
# Model
model = cp_model.CpModel()
# Variables NewIntVar 1-12)
Teams = {}
for r in range(rounds):
for i in range(Groups):
for j in range(GroupSize):
Teams[(r, i, j)] = model.NewIntVar(1, num_teams, 'Teams %i %i %i' % (r, i, j))
# Constraints
# Make sure teams are placed in a group where they are allowed. Check the tuples list
for i in range(Groups):
model.AddAllowedAssignments([Teams[(0,i,j)] for j in range(GroupSize)],GroupsList[i],)
# All teams needs to be assigned and no teams assigned 2 times
for r in range(rounds):
AllTeams = []
for i in range(Groups):
for j in range(GroupSize):
AllTeams.append(Teams[r,i,j])
model.AddAllDifferent(AllTeams)
#Just to force the not most obvious solution and make team 8 be assigned to Group 3
model.Add(Teams[(0,2,0)]==8)
#Missing constraints
#1 Teams in group 1 stays in group1 for all 3 rounds
# (if group 1 in round 1 is [1,2,3,4] the matches will be 1 vs 2 and 3 vs 4)
#2 Teams should only play 1 team only 1 time. So Group 1 for Round2
# could be [1,3,2,4] and Round 3 [1,4,2,3]
#Solve
solver = cp_model.CpSolver()
status = solver.Solve(model)
# Print Groups
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
for r in range(rounds):
print('Round ',r+1)
for i in range(Groups):
print ('Group',i+1,end = "")
print([int(solver.Value(Teams[(r,i, j)])) for j in range(GroupSize)])
if __name__ == '__main__':
main()