File size: 6,071 Bytes
dc03771
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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