# Complete DOCPLEX implementation

from docplex.mp.model import Model

def climbing_optimization():
    """Climbing competition optimization using DOCPLEX"""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="climbing_competition")
    
    # Data: Points each climber can score
    climber_points = [15, 25, 10]
    n_climbers = len(climber_points)
    
    # Data: Number of mountains
    n_mountains = 3
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert n_climbers == 3, "Unexpected number of climbers"
    assert n_mountains == 3, "Unexpected number of mountains"
    
    # Safe range for indexing
    climber_range = range(n_climbers)
    mountain_range = range(n_mountains)
    
    # 2. VARIABLES
    # Binary decision variables: x[i][j] = 1 if climber i is assigned to mountain j
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in climber_range for j in mountain_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total points scored by all climbers assigned to mountains
    objective = mdl.sum(climber_points[i] * x[i, j] for i in climber_range for j in mountain_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Each climber must be assigned to exactly one mountain
    for i in climber_range:
        mdl.add_constraint(mdl.sum(x[i, j] for j in mountain_range) == 1, ctname=f"climber_{i}_assignment")
    
    # Each mountain must have at least one climber assigned to it
    for j in mountain_range:
        mdl.add_constraint(mdl.sum(x[i, j] for i in climber_range) >= 1, ctname=f"mountain_{j}_coverage")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in climber_range:
            for j in mountain_range:
                if solution.get_value(x[i, j]) > 0.5:  # Check if the climber is assigned to the mountain
                    print(f"Climber {i+1} is assigned to Mountain {j+1}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
climbing_optimization()