Skip to content

Instantly share code, notes, and snippets.

@chadselph
Created December 19, 2018 01:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chadselph/3fbf1224de90bb573c5c0775461cca53 to your computer and use it in GitHub Desktop.
Save chadselph/3fbf1224de90bb573c5c0775461cca53 to your computer and use it in GitHub Desktop.
Choice voting election
package domain
case class Choice(name: String) extends AnyVal
case class Ballot(topChoice: Choice, next: List[Choice]) {
def eliminateChoice(choice: Choice): Option[Ballot] =
if (topChoice == choice) {
next match {
case Nil => None
case nextTop :: rest => Some(Ballot(nextTop, rest))
}
} else {
Some(this.copy(next = next.filter(_ != choice)))
}
}
object Ballot {
def apply(choices: Choice*) = new Ballot(choices.head, choices.tail.toList)
}
object Election {
@scala.annotation.tailrec
def determineWinner(ballots: List[Ballot], roundNumber: Int): Choice = {
val neededToWin = ballots.size / 2 + 1
val roundResult = counts(ballots.map(_.topChoice))
val (winningChoice, winncingCount) = roundResult.maxBy(_._2)
println(s"Round #$roundNumber most votes ($winncingCount) goes to $winningChoice. All votes: $roundResult")
if (winncingCount >= neededToWin) winningChoice
else {
val (eliminate, count) = roundResult.minBy(_._2)
println(s"Not enough votes, needed $neededToWin. Eliminating $eliminate")
val newBallots = ballots.flatMap(_.eliminateChoice(eliminate))
determineWinner(newBallots, roundNumber + 1)
}
}
private def counts[A](s: Seq[A]): Map[A, Int] = s.groupBy(identity).map { case (item, items) => item -> items.size }
}
import domain.{Ballot, Choice, Election}
object Main extends App {
// TODO: IO.
}
object Test extends App {
val `🍷` = Choice("Winery Tour")
val `🎳` = Choice("Ten pin bowling")
val `πŸŽ₯` = Choice("Movie night")
val `πŸ–Ό` = Choice("Museum")
val ballots = List(
Ballot(`🍷`, `🎳`, `πŸ–Ό`, `πŸŽ₯`),
Ballot(`🎳`, `🍷`, `πŸ–Ό`),
Ballot(`πŸŽ₯`, `🍷`, `🎳`, `πŸ–Ό`),
Ballot(`πŸŽ₯`, `πŸ–Ό`, `🍷`, `🎳`),
Ballot(`πŸ–Ό`, `🍷`),
Ballot(`πŸ–Ό`, `🎳`),
Ballot(`🎳`, `🍷`, `πŸŽ₯`),
Ballot(`πŸŽ₯`, `🎳`, `🍷`, `πŸ–Ό`),
)
Election.determineWinner(ballots, 1)
/**
Output was:
Round #1 most votes (3) goes to Choice(Movie night). All votes: Map(Choice(Museum) -> 2, Choice(Ten pin bowling) -> 2, Choice(Winery Tour) -> 1, Choice(Movie night) -> 3)
Not enough votes, needed 5. Eliminating Choice(Winery Tour)
Round #2 most votes (3) goes to Choice(Ten pin bowling). All votes: Map(Choice(Museum) -> 2, Choice(Ten pin bowling) -> 3, Choice(Movie night) -> 3)
Not enough votes, needed 5. Eliminating Choice(Museum)
Round #3 most votes (4) goes to Choice(Ten pin bowling). All votes: Map(Choice(Ten pin bowling) -> 4, Choice(Movie night) -> 3)
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment