Created
December 19, 2018 01:13
-
-
Save chadselph/3fbf1224de90bb573c5c0775461cca53 to your computer and use it in GitHub Desktop.
Choice voting election
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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