# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def optimize_club_assignment():
    # 1. MODEL & DATA SETUP
    model = gp.Model("club_assignment")

    # Data from the problem
    students = [1, 2, 3]
    clubs = [101, 102, 103]
    
    engagement_scores = {
        (1, 101): 0.6,
        (2, 102): 0.8,
        (3, 103): 0.4
    }
    
    club_capacities = {
        101: 20,
        102: 25,
        103: 15
    }

    # CRITICAL: Validate array lengths before loops
    assert len(engagement_scores) == len(students) * len(clubs), "Engagement scores length mismatch"
    assert len(club_capacities) == len(clubs), "Club capacities length mismatch"

    # 2. VARIABLES
    x = model.addVars(students, clubs, vtype=GRB.BINARY, name="x")

    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(engagement_scores[s, c] * x[s, c] for s in students for c in clubs),
        GRB.MAXIMIZE
    )

    # 4. CONSTRAINTS

    # Club Capacity Constraint
    for c in clubs:
        model.addConstr(
            gp.quicksum(x[s, c] for s in students) <= club_capacities[c],
            name=f"club_capacity_{c}"
        )

    # Student Assignment Constraint
    for s in students:
        model.addConstr(
            gp.quicksum(x[s, c] for c in clubs) <= 1,
            name=f"student_assignment_{s}"
        )

    # 5. SOLVING & RESULTS
    model.optimize()

    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for s in students:
            for c in clubs:
                if x[s, c].x > 0.5:
                    print(f"Student {s} is assigned to Club {c}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

    return model

# Execute the optimization
optimize_club_assignment()