Last active
November 6, 2017 12:52
-
-
Save tOverney/47a67e0a7dc3eecfcc151b54c5f7e18f to your computer and use it in GitHub Desktop.
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 akka.actor._ | |
// These are the messages sent by the Scrutineer | |
case object VoteIsOver | |
case class IssueVoted(val question: String) | |
// These are all the messages sent by an Assembly member | |
case object ImHere | |
sealed trait VoteOption | |
case object Yes extends VoteOption | |
case object No extends VoteOption | |
case object Blank extends VoteOption | |
// Message related to the VoteTimer | |
case object TimesUp | |
case class StartTimer(duration: Long) | |
object Quorum extends App{ | |
val system = ActorSystem("Quorum") | |
val AssemblySize: Int = 153 | |
val VotationItem: IssueVoted = IssueVoted("raise the EPFL tuition fee") | |
val scrutineer = | |
system.actorOf(Props(new Scrutineer(AssemblySize, VotationItem))) | |
val AssemblyMembers = (0 to AssemblySize).map{ | |
id => system.actorOf(Props(new AssemblyMember(scrutineer, | |
(10000 * Math.random()).toInt)), name = s"Assembly_member_$id") | |
} | |
} | |
class Scrutineer(assemblySize: Int, votationItem: IssueVoted) extends Actor { | |
val AbsoluteMajority = assemblySize/2 + 1 | |
val VoteDuration = 50000L | |
val timer = context.actorOf(Props(new Timer)) | |
override def receive = waitingToStart() | |
def waitingToStart(presentAssemblyMembers: Set[ActorRef] = Set()): Receive = { | |
case ImHere => | |
val presentMembers = presentAssemblyMembers + context.sender() | |
val currentItem = 0 | |
if (presentMembers.size > assemblySize/2) { | |
startVoteOn(presentMembers) | |
context.become(voting(alreadyVoted = Set(), presentMembers, | |
yesNoVotes = (0, 0))) | |
} else { | |
context.become(waitingToStart(presentMembers)) | |
} | |
} | |
def voting(alreadyVoted: Set[ActorRef], | |
presentMembers: Set[ActorRef], yesNoVotes: (Int, Int)): Receive = { | |
case ImHere => | |
val newMember = context.sender() | |
if (!presentMembers(newMember)) { | |
newMember ! votationItem | |
context.become(voting(alreadyVoted, | |
presentMembers + newMember, yesNoVotes)) | |
} | |
case _: VoteOption if alreadyVoted(context.sender()) => | |
println("You cannot vote twice on the same issue :" + context.sender()) | |
case v: VoteOption => | |
val (yesVotes, noVotes) = yesNoVotes | |
val votes = v match { | |
case Blank => yesNoVotes | |
case Yes => (yesVotes + 1, noVotes) | |
case No => (yesVotes, noVotes + 1) | |
} | |
val voteCasted = alreadyVoted + context.sender() | |
tallyVotes(votes, voteCasted, presentMembers, | |
presentMembers == voteCasted){ () => | |
context.become(voting(voteCasted, presentMembers, votes)) | |
} | |
case TimesUp => | |
tallyVotes(yesNoVotes, alreadyVoted, presentMembers, | |
isOver = true)(() => {}) | |
} | |
def done(): Receive = { | |
case _ => println(s"The voting session is over, it is too late to vote " + | |
context.sender()) | |
} | |
def startVoteOn(members: Set[ActorRef]): Unit = { | |
println(s"Do you agree to ${votationItem.question}?") | |
members foreach(_ ! votationItem) | |
timer ! StartTimer(VoteDuration) | |
} | |
def tallyVotes(votes: (Int, Int), alreadyVoted: Set[ActorRef], | |
members: Set[ActorRef], isOver: Boolean)(cont: () => Unit): Unit = { | |
lazy val IssueVoted(question) = votationItem | |
val (yesVotes, noVotes) = votes | |
def endVote(message: String): Unit = { | |
(members -- alreadyVoted) foreach(_ ! VoteIsOver) | |
println(message) | |
context.become(done()) | |
} | |
if (yesVotes >= AbsoluteMajority) | |
endVote(s"The Assembly agreed to $question by an absolute majority!") | |
else if (noVotes >= AbsoluteMajority) | |
endVote(s"The Assembly refused to $question by an absolute majority!") | |
else if (isOver) { | |
if (yesVotes > noVotes) | |
endVote(s"The Assembly agreed to $question: $yesVotes against $noVotes votes!") | |
else if (yesVotes == noVotes) | |
endVote(s"The Assembly was unable to choose whether to $question or not") | |
else | |
endVote(s"The Assembly refused to $question: $noVotes against $yesVotes votes!") | |
} else cont() | |
} | |
} | |
class AssemblyMember(scrutineer: ActorRef, thinkDelay: Int) extends Actor { | |
val timer = context.actorOf(Props(new Timer)) | |
scrutineer ! ImHere | |
override def receive = waiting() | |
def waiting(): Receive = { | |
case issue: IssueVoted => | |
timer ! StartTimer(thinkDelay) | |
context.become(thinking()) | |
} | |
def thinking(): Receive = { | |
case VoteIsOver => | |
context.become(waiting()) | |
case TimesUp => | |
Math.random() match { | |
case rng if rng < 0.45 => scrutineer ! Yes | |
case rng if rng < 0.9 => scrutineer ! No | |
case _ => scrutineer ! Blank | |
} | |
context.become(waiting()) | |
} | |
} | |
class Timer extends Actor { | |
override def receive: Receive = { | |
case StartTimer(duration: Long) => | |
Thread.sleep(duration) | |
context.sender() ! TimesUp | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment