Skip to content

Instantly share code, notes, and snippets.

@ngocdaothanh
Created November 20, 2013 01:54
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 ngocdaothanh/d3506b81c1343506af35 to your computer and use it in GitHub Desktop.
Save ngocdaothanh/d3506b81c1343506af35 to your computer and use it in GitHub Desktop.
reactive.2
package simulations
import math.random
class EpidemySimulator extends Simulator {
def randomBelow(i: Int) = (random * i).toInt
protected[simulations] object SimConfig {
val population = 300
val roomRows = 8
val roomColumns = 8
val incubationTime = 6
val dieTime = 14
val immuneTime = 16
val healTime = 18
val prevalenceRate = 0.01
val transRate = 0.4
val dieRate = 0.25
val moveMaxDelay = 5
}
import SimConfig._
val persons = initPersons()
def isVisiblyInfectiousRoom(row: Int, col: Int) = persons.exists { p =>
p.row == row && p.col == col && p.isVisiblyInfectious
}
def isInfectiousRoom(row: Int, col: Int) = persons.exists { p =>
p.row == row && p.col == col && p.infected
}
class Person(val id: Int) {
var infected = false // should remain infected after immune
var sick = false
var immune = false
var dead = false
var row = randomBelow(roomRows)
var col = randomBelow(roomColumns)
var infectedAt = -1
var nextMoveAt = currentTime + 1 + randomBelow(moveMaxDelay)
override def toString = s"(id: $id, infected: $infected (dt = ${currentTime - infectedAt}), sick: $sick, immune: $immune, dead: $dead)"
def isVisiblyInfectious = sick || dead
def act() {
if (dead) return
if (infected) {
val dt = currentTime - infectedAt
if (incubationTime <= dt && dt < dieTime) {
sick = true
} else if (dieTime <= dt && dt < immuneTime) {
if (randomBelow(100) < 100 * dieRate) {
// dead people do not move
dead = true
return
}
} else if (immuneTime <= dt && dt < healTime) {
immune = true
sick = false
} else if (healTime <= dt) {
infected = false
immune = false
infectedAt = -1
}
}
if (nextMoveAt >= currentTime) {
nextMoveAt = currentTime + 1 + randomBelow(moveMaxDelay)
val direction = randomBelow(4)
direction match {
case 0 =>
val newCol = if (col > 0) col - 1 else roomColumns - 1
moveAvoidVisiblyInfectiousRoom(row, newCol)
case 1 =>
val newCol = if (col < roomColumns - 1) col + 1 else 0
moveAvoidVisiblyInfectiousRoom(row, newCol)
case 2 =>
val newRow = if (row > 0) row - 1 else roomRows - 1
moveAvoidVisiblyInfectiousRoom(newRow, col)
case 3 =>
val newRow = if (row < roomRows - 1) row + 1 else 0
moveAvoidVisiblyInfectiousRoom(newRow, col)
}
}
if (!infected && isInfectiousRoom(row, col) && randomBelow(100) < 100 * transRate) {
infected = true
infectedAt = currentTime
}
afterDelay(1)(act)
}
private def moveAvoidVisiblyInfectiousRoom(newRow: Int, newCol: Int) {
if (!isVisiblyInfectiousRoom(newRow, newCol)) {
row = newRow
col = newCol
}
}
}
//----------------------------------------------------------------------------
private def initPersons(): List[Person] = {
val numInfected = (population * prevalenceRate).toInt
doInitPersons(true, 1, numInfected, Nil) ++
doInitPersons(false, numInfected + 1, population - numInfected, Nil)
}
private def doInitPersons(infected: Boolean, nextId: Int, count: Int, acc: List[Person]): List[Person] = {
if (count == 0) {
acc
} else {
val p = new Person(nextId)
if (infected) {
p.infected = infected
p.infectedAt = currentTime
}
afterDelay(1)(p.act)
doInitPersons(infected, nextId + 1, count - 1, p :: acc)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment