Skip to content

enzobonansea/voting-systems

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Voting systems

This is my solution to the following challenge:

Given the recent buzz around the New York City mayoral election and its use of ranked-choice voting (a basic explanation of which can be found here), we have created a project to simulate the ballot counting and show how a majority winner would not necessarily win a ranked-choice election. The task is to implement SimpleElection.CountVotes() and RankedChoiceElection.CountVotes() in such a way that their respective ElectionRunners will return the correct winner of the election based on the voting system (you may also want to take a look at ElectionRunner.cs). Changes can be made to anything in the solution, including the BallotGenerators, which currently just generate 100,000 random votes. The submission should include a short explanation of your solution and any unit tests you used along the way.

The codebase provided is here.

I used Test-Driven Development in every line of code added, and all commits follow this convention with some personal variations. In the beginning, I pushed first the test and then the feature in order to emphasize TDD usage, but then I pushed both the test and feature together for simplicity. The code was written using DDD, since I captured the ubiquitous language in this election's bounded context.

Step-by-step solution

  • I created a new repo with the codebase and xUnit as testing framework.
  • Constructors must return complete and valid instances of objects. In order to have complete and valid instances of SimpleElection I added the preconditions "must have ballots", "must have candidates", "people can't vote more than once" and "voted candidates must be valid" in the constructor.
  • I developed SimpleElection behavior that states that the winner is whose have more votes in both absolute majority and non-absolute majority cases. Furthermore, for simplicity, I decided that on tie an exception was thrown.
  • In order to have complete and valid instances of RankedChoiceElection I added the preconditions "must have ballots", "must have candidates", "people can't vote more than once", "voted candidates must be valid", "all ballots must have same votes quantity", "ballots' votes must have different ranks", "ranks must be between one and votes length", "ballots must have same voter" and "ballots' votes must have different candidates".
  • I developed RankedChoiceElection behavior that states that the candidate with an absolute majority in first-preference votes wins. Since here, I captured the concept of "first-preference vote" from ubiquitous language.
  • I developed RankedChoiceBallot behavior that describes that when a candidate is removed, the rank of the others is modified. Also, I add here a useful getter for checking if a ballot has a candidate or not.
  • I noticed that in the ubiquitous language was an object that represents the different rounds in RankedChoiceElection. I called it RankedChoiceElectionRound, and it's behavior is: "in every round, there is a winner and a loser based on first-preference votes count; winner could have an absolute majority or not, and in next round, the loser (i.e. the candidate with fewest first-preference votes) is removed from election". I developed that behavior and then, RankedChoiceElection collaborates with RankedChoiceElectionRound in CountVotes creating a new round and asking for the next until an absolute majority winner appears. I made a design decision for simplicity that states that: if we have all the candidates ordered by first-preference votes count descending and there is a tie between the last two candidates, the last is the loser.

Things to improve

  • Use value objects for ids instead of ints to increase code declarativity, e.g. having a value object called CandidateId that under the hood holds the int value.
  • Some methods should be divided into smaller, e.g. RankedChoiceElectionRound.CalculateWinnerAndLoser
  • The preconditions described in RankedChoiceElectionTest.BallotsMustHaveSameVoter, RankedChoiceElectionTest.BallotsMustHaveDifferentCandidates, RankedChoiceElectionTest.BallotsVotesMustHaveDifferentRanks and RankedChoiceElectionTest.BallotsMustHaveRanksBetweenOneAndVotesLength are checked in RankedChoiceElection but must be checked in RankedChoiceBallot. The tests must be moved from RankedChoiceElectionTest to RankedChoiceBallotTest and the logic from RankedChoiceElection constructor to RankedChoiceBallot constructor.
  • RankedChoiceElectionRound has no precondition checks in their constructor for candidates or ballots. Surely it should have some checks similar to RankedChoiceElection checks.
  • An interesting feature is a configurable on-tie action. In both voting systems, I made my design decisions for tie scenarios, but it could be configurable.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages