def apply(self, solver, players_dict): if not self.optimizer.opposing_teams_position_restriction: return for game in self.optimizer.games: first_team_players = { player: variable for player, variable in players_dict.items() if player.team == game.home_team } second_team_players = { player: variable for player, variable in players_dict.items() if player.team == game.away_team } for first_team_positions, second_team_positions in \ permutations(self.optimizer.opposing_teams_position_restriction, 2): first_team_variables = [ variable for player, variable in first_team_players.items() if list_intersection(player.positions, first_team_positions) ] second_team_variables = [ variable for player, variable in second_team_players.items() if list_intersection(player.positions, second_team_positions) ] for variables in product(first_team_variables, second_team_variables): solver.add_constraint(variables, None, SolverSign.LTE, 1)
def _create_constraints(self, solver, players_dict, exclude_teams=None): # type: (Solver, Dict[Player, Any], Optional[Set[str]]) -> None all_combinations = defaultdict(set) # type: DefaultDict[Tuple[Any, ...], Set[Any]] players_by_teams = get_players_grouped_by_teams(players_dict.keys()) for stack, total_stacks in self.stacks_dict.items(): stack_variables = [] variable_prefix = 'rules_%s' % '_'.join(str(stack)) all_positions = tuple(set(chain.from_iterable(stack))) positions_for_optimizer = Counter(stack) positions_for_optimizer[all_positions] = len(stack) for team_name, team_players in players_by_teams.items(): if exclude_teams and team_name in exclude_teams: variables = [players_dict[player] for player in team_players if list_intersection(player.positions, all_positions)] solver.add_constraint(variables, None, SolverSign.LTE, len(stack) - 1) continue variable_name = '%s_players_%s' % (variable_prefix, team_name) team_stack_var = solver.add_variable(variable_name) stack_variables.append(team_stack_var) for positions, total in positions_for_optimizer.items(): position_variables = set( players_dict[player] for player in sorted(team_players, key=lambda p: p.full_name) if list_intersection(player.positions, positions)) all_combinations[tuple(position_variables)].add(team_stack_var) solver.add_constraint(position_variables, None, SolverSign.GTE, total * team_stack_var) solver.add_constraint(stack_variables, None, SolverSign.GTE, total_stacks) for combination_variables in all_combinations.values(): if len(combination_variables) > 1: solver.add_constraint(combination_variables, None, SolverSign.LTE, 1)
def apply(self, solver, players_dict): if not self.optimizer.opposing_teams_position_restriction: return for game in self.optimizer.games: first_team_players = { player: variable for player, variable in players_dict.items() if player.team == game.home_team } second_team_players = { player: variable for player, variable in players_dict.items() if player.team == game.away_team } for first_team_positions, second_team_positions in \ permutations(self.optimizer.opposing_teams_position_restriction, 2): first_team_variables = [ variable for player, variable in first_team_players.items() if list_intersection(player.positions, first_team_positions) ] second_team_variables = [ variable for player, variable in second_team_players.items() if list_intersection(player.positions, second_team_positions) ] coefficients = [1] * len(second_team_variables) for var in first_team_variables: solver.add_constraint( [var, *second_team_variables], [self.MULTIPLIER, *coefficients], SolverSign.LTE, self.MULTIPLIER + self.optimizer.opposing_teams_max_allowed)
def test_restrict_positions_for_opposing_team_correctness(self): first_team_positions = ['SP', 'RP'] second_team_positions = ['1B', '2B', '3B'] self.optimizer.restrict_positions_for_opposing_team(first_team_positions, second_team_positions) lineup = next(self.optimizer.optimize(1)) pitcher_games = {player.game_info for player in lineup if list_intersection(player.positions, first_team_positions)} hitters_games = {player.game_info for player in lineup if list_intersection(player.positions, second_team_positions)} self.assertFalse(pitcher_games.intersection(hitters_games))
def apply(self, solver): players_dict = {player: variable for player, variable in self.players_dict.items() if list_intersection(player.positions, self.HITTERS)} for team in self.optimizer.available_teams: players_from_team = [variable for player, variable in players_dict.items() if player.team == team] solver.add_constraint(players_from_team, None, SolverSign.LTE, self.MAXIMUM_HITTERS_FROM_ONE_TEAM, name='dk_max_hitters_%s' % team)
def apply(self, solver, players_dict): optimizer = self.optimizer positions, spacing = optimizer.spacing_positions, optimizer.spacing if not spacing or not positions: return available_players = sorted( [(player, variable) for player, variable in players_dict.items() if player.roster_order is not None and list_intersection(player.positions, positions)], key=self.sort_players, ) players_by_roster_positions = { players_spacing: list(players) for players_spacing, players in groupby(available_players, key=self.sort_players) } for roster_position, players in players_by_roster_positions.items(): next_restricted_roster_position = roster_position + spacing restricted_players = chain.from_iterable( players for players_spacing, players in players_by_roster_positions.items() if players_spacing >= next_restricted_roster_position) for first_player, first_variable in restricted_players: for second_player, second_variable in players: if first_player.team != second_player.team: continue solver.add_constraint([first_variable, second_variable], None, SolverSign.LTE, 1)
def build_stacks(self, players: List[Player], optimizer: 'LineupOptimizer') -> List[OptimizerStack]: players_by_teams = get_players_grouped_by_teams( players, for_teams=self.for_teams) all_positions = tuple(set(chain.from_iterable(self.positions))) positions_for_optimizer = Counter(self.positions) positions_for_optimizer[all_positions] = len(self.positions) all_groups = [] # type: List[BaseGroup] for team_name, team_players in players_by_teams.items(): groups = [] for positions, total in positions_for_optimizer.items(): groups.append( PlayersGroup( players=[ player for player in team_players if list_intersection(player.positions, positions) ], min_from_group=total, )) nested_group = NestedPlayersGroup( groups=groups, max_exposure=self.max_exposure_per_team.get( team_name, self.max_exposure), ) all_groups.append(nested_group) return [OptimizerStack(groups=all_groups)]
def apply(self, solver, players_dict): optimizer = self.optimizer positions, spacing = optimizer.spacing_positions, optimizer.spacing if not spacing or not positions: return players_by_roster_positions = defaultdict( list) # type: Dict[int, List[Tuple[Player, Any]]] for player, variable in players_dict.items(): if player.roster_order is None or not list_intersection( player.positions, positions): continue players_by_roster_positions[player.roster_order].append( (player, variable)) for roster_position, players in players_by_roster_positions.items(): next_restricted_roster_position = roster_position + spacing restricted_players = chain.from_iterable( players for players_spacing, players in players_by_roster_positions.items() if players_spacing >= next_restricted_roster_position) for first_player, first_variable in restricted_players: for second_player, second_variable in players: if first_player.team != second_player.team: continue solver.add_constraint([first_variable, second_variable], None, SolverSign.LTE, 1)
def apply_for_iteration(self, solver, players_dict, result): current_lineup = self.lineups[self.current_iteration] unswappable_players = current_lineup.get_unswappable_players() remaining_positions = get_remaining_positions( self.optimizer.settings.positions, unswappable_players) # lock selected players for player in unswappable_players: solver.add_constraint([players_dict[player]], None, SolverSign.EQ, 1) # set remaining positions positions_combinations = set([ tuple(sorted(player.positions)) for player in players_dict.keys() if len(player.positions) > 1 ]) positions = get_positions_for_optimizer(remaining_positions, positions_combinations) players_for_optimization = set() for position, places in positions.items(): players_with_position = [ variable for player, variable in players_dict.items() if list_intersection(position, player.positions) and player not in unswappable_players ] players_for_optimization.update(players_with_position) solver.add_constraint(players_with_position, None, SolverSign.GTE, places) # Set total players for optimization solver.add_constraint(players_for_optimization, None, SolverSign.EQ, len(remaining_positions)) # Exclude players with active games for player, variable in players_dict.items(): if player not in unswappable_players and player.is_game_started: solver.add_constraint([players_dict[player]], None, SolverSign.EQ, 0) self.current_iteration += 1
def apply_for_iteration(self, solver, players_dict, result): current_lineup = self.lineups[self.current_iteration] unswappable_players = current_lineup.get_unswappable_players() positions = self.optimizer.settings.positions[:] # lock selected players for player in unswappable_players: for position in positions: if position.name == player.lineup_position: positions.remove(position) break solver.add_constraint([players_dict[player]], [1], SolverSign.EQ, 1) # set remaining positions positions = get_positions_for_optimizer(positions) for position, places in positions.items(): players_with_position = [ variable for player, variable in players_dict.items() if list_intersection(position, player.positions) and player not in unswappable_players ] coefficients = [1] * len(players_with_position) solver.add_constraint(players_with_position, coefficients, SolverSign.GTE, places) # Exclude players with active games for player, variable in players_dict.items(): if player not in unswappable_players and player.is_game_started: solver.add_constraint([players_dict[player]], [1], SolverSign.EQ, 0) self.current_iteration += 1
def test_positions_from_same_team_with_combo_position(self): self.optimizer.add_stack(PositionsStack(['PG', ('SF', 'C')])) lineups = list(self.optimizer.optimize(2)) stack = ('PG', 'SF', 'C') players_in_stack = max([ len([p for p in lineup if p.team == self.first_team and list_intersection(p.positions, stack)]) for lineup in lineups ]) self.assertEqual(players_in_stack, 2)
def apply(self, solver): if not self.optimizer.opposing_teams_position_restriction: return for game in self.optimizer.games: home_team_players = {player: variable for player, variable in self.players_dict.items() if player.team == game.home_team} away_team_players = {player: variable for player, variable in self.players_dict.items() if player.team == game.away_team} first_team_positions, second_team_positions = self.optimizer.opposing_teams_position_restriction for first_team_players, second_team_players in permutations([home_team_players, away_team_players], 2): first_team_variables = [variable for player, variable in first_team_players.items() if list_intersection(player.positions, first_team_positions)] second_team_variables = [variable for player, variable in second_team_players.items() if list_intersection(player.positions, second_team_positions)] aggregated_var = solver.add_variable(str(uuid4()), min_value=0, max_value=len(second_team_variables)) solver.add_constraint(second_team_variables, None, SolverSign.EQ, aggregated_var) for var in first_team_variables: solver.add_constraint([var, aggregated_var], [self.MULTIPLIER, 1], SolverSign.LTE, self.MULTIPLIER + self.optimizer.opposing_teams_max_allowed)
def apply(self, solver, players_dict): all_players = players_dict.keys() for_positions = self.optimizer.team_stacks_for_positions if for_positions: all_players = [player for player in all_players if list_intersection(player.positions, for_positions)] players_by_teams = get_players_grouped_by_teams(all_players) for team, players in players_by_teams.items(): variables = [players_dict[player] for player in players] self.player_variables_by_teams[team] = variables if not self.teams_max_exposures: self._create_constraints(solver)
def _detect_teams_used_in_stacks(self, lineup): # type: (Lineup) -> Set[str] teams = set([player.team for player in lineup]) all_teams_used_in_stacks = set() for stack in self.stacks_dict.keys(): teams_used_in_stack = teams.copy() for positions in stack: teams_used_in_stack = teams_used_in_stack.intersection( set([player.team for player in lineup if list_intersection(positions, player.positions)]) ) all_teams_used_in_stacks.update(teams_used_in_stack) return all_teams_used_in_stacks
def lock_player(self, player: DirtyPlayer, position: Optional[str] = None): player = self._clean_player(player) if player.max_exposure == 0: raise LineupOptimizerException( 'Can\'t add this player to line up! Player has max exposure set to 0.' ) if player in self._locked_players: raise LineupOptimizerException( 'This player already in your line up!') if self.remaining_budget and player.salary > self.remaining_budget: raise LineupOptimizerException( 'Can\'t add this player to line up! Your team is over budget!') if self.remaining_players < 1: raise LineupOptimizerException( 'Can\'t add this player to line up! You already select all %s players!' % len(self.locked_players)) max_from_one_team = self._settings.max_from_one_team if max_from_one_team: from_same_team = len( [p for p in self.locked_players if p.team == player.team]) if from_same_team + 1 > max_from_one_team: raise LineupOptimizerException( 'You can\'t set more than %s players from one team.' % max_from_one_team) position_for_player = None if position: if position not in self.available_positions: raise LineupOptimizerException( 'Position %s doesn\'t exist. Available positions are %s' % (position, ','.join(self.available_positions))) for remaining_position in self._remaining_positions: if position: if position == remaining_position.name: position_for_player = remaining_position break else: raise LineupOptimizerException('Position %s is filled.' % position) if not list_intersection(player.positions, position_for_player.positions): raise LineupOptimizerException( 'Player can\'t be set to position %s' % position) try: link_players_with_positions({player, *self._locked_players}, self._settings.positions) except LineupOptimizerException: raise LineupOptimizerException('You\'re already select all %s\'s' % '/'.join(player.positions)) if position_for_player: self._remaining_positions.remove(position_for_player) self._locked_players[player] = position_for_player
def _create_constraints(self, solver: Solver, players_dict: Dict[Player, Any]) -> None: for positions, total_for_positions in self.positions.items(): min_exposure_players = [p for p in self.min_exposure_players if list_intersection(p.positions, positions)] if not min_exposure_players: continue total_force = sum(self.min_exposure_players[p] for p in min_exposure_players) - \ total_for_positions * (self.remaining_lineups - 1) if total_force > 0: variables = [players_dict[p] for p in min_exposure_players] total_force = min(total_force, ceil(total_force / self.remaining_lineups)) solver.add_constraint(variables, None, SolverSign.GTE, total_force) for player, total_lineups in self.min_exposure_players.items(): if total_lineups >= self.remaining_lineups: solver.add_constraint([players_dict[player]], None, SolverSign.EQ, 1)
def apply(self, solver, players_dict): extra_positions = self.optimizer.players_with_same_position positions = self.optimizer.get_positions_for_optimizer() for position, places in positions.items(): extra = 0 if len(position) == 1: extra = extra_positions.get(position[0], 0) players_with_position = [ variable for player, variable in players_dict.items() if list_intersection(position, player.positions) ] coefficients = [1] * len(players_with_position) solver.add_constraint(players_with_position, coefficients, SolverSign.GTE, places + extra)
def test_list_intersection(self): self.assertTrue(list_intersection(['PG'], ['SG', 'PG'])) self.assertTrue(list_intersection(['SG', 'PG'], ['PG'])) self.assertFalse(list_intersection(['PG'], ['SF', 'PF']))