-
Notifications
You must be signed in to change notification settings - Fork 23
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
Adding tiebreaker order #379
Conversation
With this implementation the candidates can know ahead of time which one will get tie breaker priority before the election How would you feel if we used a random number generator, but use "current vote count + the election id" as the seed? That way the result can't be predicted, but recalculating with the same set of votes will give the same result |
Done. I'm not necessary against the tiebreak order being known in advance, but your idea is probably a better approach. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
@@ -41,6 +41,7 @@ | |||
"pg-boss": "^8.0.0", | |||
"pg-format": "^1.0.4", | |||
"qs": "^6.10.3", | |||
"seedrandom": "^3.0.5", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised the base math class didn't support seeding, weird
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea its frustrating. That's why I didn't go with seeds before, but I found a library that does it.
@@ -48,8 +49,11 @@ const getElectionResults = async (req: IElectionRequest, res: Response, next: Ne | |||
} | |||
const msg = `Tabulating results for ${voting_method} election` | |||
Logger.info(req, msg); | |||
results[race_index] = VotingMethods[voting_method](candidateNames, cvr, num_winners) | |||
let rng = seedrandom(election.election_id + ballots.length.toString()) | |||
const tieBreakOrders = election.races[race_index].candidates.map((Candidate) => (rng() as number)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was initially confused here since rng() outputs floats, but the test cases work with lists of unique integers. But it looks like it'll work fine. I guess it's technically possible for rng() to output the same number twice? but I'm not worried about that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not worried about duplicate floats. And I think in JS integers are just treated as floats.
Description
Previously random tiebreakers used a random number generator inside the tabulators to determine results. This issue with this is results would change every time you calculated them.
This adds a tiebreaker order list input to the tabulators so the outcome is determinative. The list uses strings so UUIDs can be used. To break a tie, the candidate with the lower string value wins. For example, 'A' defeats 'B', '0' defeats '1'. If no list is defined it defaults to using candidate index.
Now that candidate IDs are UUIDs, they can be used for their tiebreaker order so that random ties can be random but still determinative.