Created
November 20, 2013 01:54
-
-
Save ngocdaothanh/d3506b81c1343506af35 to your computer and use it in GitHub Desktop.
reactive.2
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 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