Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Positions on Draftkings Allows duped lineups #157

Open
rfr3sh opened this issue Jan 10, 2020 · 14 comments
Open

Multiple Positions on Draftkings Allows duped lineups #157

rfr3sh opened this issue Jan 10, 2020 · 14 comments

Comments

@rfr3sh
Copy link

rfr3sh commented Jan 10, 2020

image

I believe since each player is given a unique ID based on their positional eligibility these lineups are being counted as not duplicated

@BenBrostoff
Copy link
Owner

BenBrostoff commented Jan 10, 2020 via email

@rfr3sh
Copy link
Author

rfr3sh commented Jan 10, 2020

nba.zip

Used these PIDS anre projections

rosters, _ = run_multi(
iterations=2,
rule_set=rules.DK_NBA_RULE_SET,
player_pool=players,
verbose=False,
)

Should do it

@harg0055
Copy link

harg0055 commented Jan 11, 2020

Preface - I am VERY new to python so I have no idea if this would work or not...

Would creating a key based on sorting the PIDS work? So after each run, sort the PIDS and concatenate them together. Then compare that against all previous lineups to see if that key exists prior to adding to the list?

EDIT - or could you do something like defining the previous runs optimal projection and then using that as a new upper bound on the next run?

@BenBrostoff
Copy link
Owner

Yep, this will work @harg0055 - there's still a question though of how to implement if you use this technique. The short story is at optimize time, the optimizer isn't smart enough to know this is a dupe, and the technique you're proposing would be pre-optimize time and not at optimize time.

The optimizer detects existing lineups like this https://github.com/BenBrostoff/draftfast/blob/master/draftfast/optimizer.py#L314-L329 - player.solver_id is a function of position, team and player name - https://github.com/BenBrostoff/draftfast/blob/master/draftfast/orm.py#L319-L321 . I think what will fix this is to just change self.player_to_idx_map.get(player.solver_id) to self.player_to_idx_map.get('{}{}'.format(player.name, player.team)), although I have to test it. I also need to ensure this behavior is overridable and doesn't apply for Showdown (Captain Mode), where position impacts the number of points the player scores.

@rfr3sh
Copy link
Author

rfr3sh commented Jan 12, 2020

I think this won't return a value since the keys include the Position? ie Bradley Beal SF WAS: 0 , Bradley Beal SG WAS: 1

If I wasn't planning to use showdown, would this work?

def _add_player_to_idx_maps(self, p: Player, idx: int):
    #self.player_to_idx_map[p.solver_id] = idx
    self.player_to_idx_map[p.name] = idx

i = self.player_to_idx_map.get('{}'.format(player.name))

@BenBrostoff
Copy link
Owner

BenBrostoff commented Jan 14, 2020 via email

@rfr3sh
Copy link
Author

rfr3sh commented Jan 15, 2020

No luck unfortunately, I think because the constraint on the solver is based on player solver ID.

@BenBrostoff
Copy link
Owner

@rfr3sh Might get some time to put pen to paper on this over long weekend - to be clear I think it's solvable, just need to mess around with it.

@deathdonkey-code
Copy link

This is listed as an open item - wondering if anyone found a workaround / solution? I've considered checking for uniqueness of rosters after they are created and updating the number of rosters to generate by only counting the unique ones. But this gets slow for large numbers of teams (10 unique teams might be 100+ rosters that have different permutations of the same lineups)

@BenBrostoff
Copy link
Owner

BenBrostoff commented Mar 6, 2021

@deathdonkey-code That workaround should work, but the solution in #157 (comment) should also hypothetically work (have not tested). PRs welcome! Also the point about keeping showdown behavior the same is important, as in Showdown position impacts points scored through the multiplier.

@deathdonkey-code
Copy link

@deathdonkey-code That workaround should work, but the solution in #157 (comment) should also hypothetically work (have not tested). PRs welcome! Also the point about keeping showdown behavior the same is important, as in Showdown position impacts points scored through the multiplier.

Thanks for getting back to me. I am not using Showdown slates but I understand its not a good idea to modify the module and break that ability. I believe my workaround is equivalent to the suggestion you pointed to (I keep a Counter of PIDs for each run and see if it already exists) but it can be quite slow for large numbers of lineups (100 unique lineups might take 300+ runs). Unless you are referring to his second suggestion of putting an upper bound on the next run based on the projected score of the previous run with a duplicate lineup? But I am not too sure how to implement this / I didn't realize there is a way to impose an upper bound on projected score in the current code.

@BenBrostoff
Copy link
Owner

If you're changing how _set_no_duplicate_lineups works and redefining the .solver_id prop on player we're talking about the same thing - this will significantly slow down the optimizer as you're adding a new constraint (I literally mean solver.Constraint here) on each run.

Some speed stuff I've used in the past (final two trade off accuracy):

  • multiprocessing and using multiple procs for generating lineups
  • Removing players from the pool instead of relying on Constraints
  • Randomizing projections

Let me know if this helps at all. I do think it's fair to keep this issue open because the current way of preventing duplicates still can product duplicates with different positions.

@deathdonkey-code
Copy link

If you're changing how _set_no_duplicate_lineups works and redefining the .solver_id prop on player we're talking about the same thing - this will significantly slow down the optimizer as you're adding a new constraint (I literally mean solver.Constraint here) on each run.

Some speed stuff I've used in the past (final two trade off accuracy):

  • multiprocessing and using multiple procs for generating lineups
  • Removing players from the pool instead of relying on Constraints
  • Randomizing projections

Let me know if this helps at all. I do think it's fair to keep this issue open because the current way of preventing duplicates still can product duplicates with different positions.

I think I am confused now or we are referencing different posts from this thread. I am following harg0055's first idea to check for uniqueness in the rosters after the optimizer completes an iteration. It works fine but slow. I have tried your suggestion to modify the _set_no_duplicate_lineups and .solver_id like so (player_id is a unique identifier from a custom CSV format):

        for player in roster.sorted_players():
            #i = self.player_to_idx_map.get(player.solver_id)
            i = self.player_to_idx_map.get('{}'.format(player.player_id))

and

def solver_id(self):
    #return '{} {} {}'.format(self.name, self.pos, self.team)
    return str(self.player_id)

However when I run this on a sample that works fine without these code changes I immediately get: WARNING: Logging before InitGoogleLogging() is written to STDERR
F0307 00:31:46.264128 14264 map_util.h:147] Check failed: collection->insert(value_type(key, data)).second duplicate key: 16564212
*** Check failure stack trace: ***

I interpret this error to mean that it is failing because at least one player is duplicated (here, '16564212' refers to the player_id of Kyrie Irving who plays two positions, PG/SG). I have two Kyrie Irving's in my player_pool, one for each eligible position, with the possible_positions variable and multi_position flag set correctly I believe.

@BenBrostoff
Copy link
Owner

BenBrostoff commented Mar 7, 2021

Yep, that's exactly right on why that error happens I think - if you redefine solver_id that way you're going to wind up with a repeated constraint here https://github.com/BenBrostoff/draftfast/blob/master/draftfast/optimizer.py#L49-L51 . I'd need to mess around with it but my recommendation would be to only modify _set_no_duplicate_lineups.

Some more background here - solver_id can't completely change because it's important the optimizer knows what positions a player could use. The way that's implemented right now is to have each Player in the pool unique by position and team. The benefit of this is that for sports like NBA / NFL / MLB (really, anything with multi position), optimizations look at all possible combinations instead of ones where it just takes the first position of a player in the pool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants