0

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

1 Answers1

0

First I would remove all integer variables. Just have an array of Boolean variables for each integer variable, with an ExactlyOne constraint on top.

To code a feasible assignment, create 1 Boolean variable, and have this Boolean variable imply the Boolean var corresponding to the value of the assignment.

Then you can implement whatever logic on the assignment Boolean var (fixed order, at_least...)

Laurent Perron
  • 8,594
  • 1
  • 8
  • 22
  • Hi Laurent. Thank you for your input. It took me a little time to get around it but I have solved my initial questions using only boolean variables. I have however a new constraint problem. I cannot figure out - avoiding teams play each other twice. I would appreciate if you could have a look and guide me in the right direction. The new question is here: https://stackoverflow.com/questions/76244015/in-or-tools-using-cp-sat-solver-i-an-tournament-how-do-i-ensure-that-teams-nev – Carsten Nørgård May 13 '23 at 17:17