import pandas as pd import numpy as np from pulp import * from typing import List, Dict, Tuple class DFSOptimizer: def __init__(self): self.model = None self.player_vars = {} self.constraints = [] def setup_model(self, players_df: pd.DataFrame, salary_cap: float = 50000): """Initialize the optimization model with basic constraints""" self.model = LpProblem("NBA_DFS_Optimizer", LpMaximize) # Create binary variables for each player for idx, player in players_df.iterrows(): var_name = f"player_{idx}" self.player_vars[var_name] = LpVariable(var_name, 0, 1, LpBinary) # Objective: Maximize projected points self.model += lpSum([self.player_vars[f"player_{idx}"] * row["Projected Points"] for idx, row in players_df.iterrows()]) # Constraint: Salary cap self.model += lpSum([self.player_vars[f"player_{idx}"] * row["Credits"] for idx, row in players_df.iterrows()]) <= salary_cap # Constraint: Total players = 8 self.model += lpSum(self.player_vars.values()) == 8 # Position constraints self._add_position_constraints(players_df) def _add_position_constraints(self, players_df: pd.DataFrame): """Add NBA position constraints""" positions = {"PG": (1, 4), "SG": (1, 4), "SF": (1, 4), "PF": (1, 4), "C": (1, 4)} for pos, (min_count, max_count) in positions.items(): pos_vars = [self.player_vars[f"player_{idx}"] for idx, row in players_df.iterrows() if row["Position"] == pos] if pos_vars: self.model += lpSum(pos_vars) >= min_count self.model += lpSum(pos_vars) <= max_count def add_team_stack(self, players_df: pd.DataFrame, min_players: int = 2, max_players: int = 5): """Add team stacking constraints""" teams = players_df["Team Name"].unique() for team in teams: team_vars = [self.player_vars[f"player_{idx}"] for idx, row in players_df.iterrows() if row["Team Name"] == team] if team_vars: self.model += lpSum(team_vars) >= min_players self.model += lpSum(team_vars) <= max_players def add_exposure_constraints(self, players_df: pd.DataFrame, max_exposure: float = 0.6): """Add player exposure constraints""" for player in players_df["Player"].unique(): player_vars = [self.player_vars[f"player_{idx}"] for idx, row in players_df.iterrows() if row["Player"] == player] if player_vars: self.model += lpSum(player_vars) <= max_exposure def optimize(self) -> Tuple[List[str], float, float]: """Solve the optimization model and return the optimal lineup""" self.model.solve() if self.model.status != 1: raise Exception("Could not find optimal solution") selected_players = [] total_points = 0 total_salary = 0 for var in self.model.variables(): if var.varValue > 0: player_idx = int(var.name.split('_')[1]) selected_players.append({ "Player": players_df.iloc[player_idx]["Player"], "Position": players_df.iloc[player_idx]["Position"], "Team": players_df.iloc[player_idx]["Team Name"], "Salary": players_df.iloc[player_idx]["Credits"], "Projected": players_df.iloc[player_idx]["Projected Points"] }) total_points += players_df.iloc[player_idx]["Projected Points"] total_salary += players_df.iloc[player_idx]["Credits"] return selected_players, total_points, total_salary def generate_lineups(self, players_df: pd.DataFrame, num_lineups: int = 10, min_salary: float = 48500, max_salary: float = 50000, min_stack: int = 2, max_stack: int = 5, max_exposure: float = 0.6) -> List[Dict]: """Generate multiple unique lineups""" lineups = [] for _ in range(num_lineups): try: self.setup_model(players_df, max_salary) self.add_team_stack(players_df, min_stack, max_stack) self.add_exposure_constraints(players_df, max_exposure) # Add minimum salary constraint self.model += lpSum([self.player_vars[f"player_{idx}"] * row["Credits"] for idx, row in players_df.iterrows()]) >= min_salary # Add uniqueness constraint for previous lineups for prev_lineup in lineups: prev_players = [p["Player"] for p in prev_lineup["players"]] overlap_vars = [self.player_vars[f"player_{idx}"] for idx, row in players_df.iterrows() if row["Player"] in prev_players] if overlap_vars: self.model += lpSum(overlap_vars) <= 6 # Allow max 6 players overlap players, points, salary = self.optimize() lineups.append({ "players": players, "total_points": points, "total_salary": salary }) except Exception as e: print(f"Failed to generate lineup: {str(e)}") continue return lineups