diff --git a/.classpath b/VirusBroadcast_by_Java/.classpath similarity index 100% rename from .classpath rename to VirusBroadcast_by_Java/.classpath diff --git a/.project b/VirusBroadcast_by_Java/.project similarity index 100% rename from .project rename to VirusBroadcast_by_Java/.project diff --git a/src/Bed.java b/VirusBroadcast_by_Java/src/Bed.java similarity index 100% rename from src/Bed.java rename to VirusBroadcast_by_Java/src/Bed.java diff --git a/src/City.java b/VirusBroadcast_by_Java/src/City.java similarity index 100% rename from src/City.java rename to VirusBroadcast_by_Java/src/City.java diff --git a/src/Constants.java b/VirusBroadcast_by_Java/src/Constants.java similarity index 100% rename from src/Constants.java rename to VirusBroadcast_by_Java/src/Constants.java diff --git a/src/Hospital.java b/VirusBroadcast_by_Java/src/Hospital.java similarity index 100% rename from src/Hospital.java rename to VirusBroadcast_by_Java/src/Hospital.java diff --git a/src/Main.java b/VirusBroadcast_by_Java/src/Main.java similarity index 100% rename from src/Main.java rename to VirusBroadcast_by_Java/src/Main.java diff --git a/src/MathUtil.java b/VirusBroadcast_by_Java/src/MathUtil.java similarity index 96% rename from src/MathUtil.java rename to VirusBroadcast_by_Java/src/MathUtil.java index 60021b9..ba592e4 100644 --- a/src/MathUtil.java +++ b/VirusBroadcast_by_Java/src/MathUtil.java @@ -1,38 +1,38 @@ -import java.util.Random; - -/** - * 数学算法工具类 - * - * @ClassName: MathUtil - * @Description: 数学算法工具类 - * @author: Bruce Young - * @date: 2020年02月06日 11:27 - */ -public class MathUtil { - /** - * 仅仅使用一个随机数生成器 - */ - private static final Random randomGen = new Random(); - - /** - * 标准正态分布化 - *

- * 流动意愿标准化后判断是在0的左边还是右边从而决定是否流动。 - *

- * 设X随机变量为服从正态分布,sigma是影响分布形态的系数 u值决定正态分布均值 - *

- *

- * 推导: - * StdX = (X-u)/sigma - * X = sigma * StdX + u - * - * @param sigma 正态标准差sigma值 - * @param u 正态均值参数mu - * @return - */ - public static double stdGaussian(double sigma, double u) { - double X = randomGen.nextGaussian(); - return sigma * X + u; - } - -} +import java.util.Random; + +/** + * 数学算法工具类 + * + * @ClassName: MathUtil + * @Description: 数学算法工具类 + * @author: Bruce Young + * @date: 2020年02月06日 11:27 + */ +public class MathUtil { + /** + * 仅仅使用一个随机数生成器 + */ + private static final Random randomGen = new Random(); + + /** + * 标准正态分布化 + *

+ * 流动意愿标准化后判断是在0的左边还是右边从而决定是否流动。 + *

+ * 设X随机变量为服从正态分布,sigma是影响分布形态的系数 u值决定正态分布均值 + *

+ *

+ * 推导: + * StdX = (X-u)/sigma + * X = sigma * StdX + u + * + * @param sigma 正态标准差sigma值 + * @param u 正态均值参数mu + * @return + */ + public static double stdGaussian(double sigma, double u) { + double X = randomGen.nextGaussian(); + return sigma * X + u; + } + +} diff --git a/src/MoveTarget.java b/VirusBroadcast_by_Java/src/MoveTarget.java similarity index 100% rename from src/MoveTarget.java rename to VirusBroadcast_by_Java/src/MoveTarget.java diff --git a/src/MyPanel.java b/VirusBroadcast_by_Java/src/MyPanel.java similarity index 100% rename from src/MyPanel.java rename to VirusBroadcast_by_Java/src/MyPanel.java diff --git a/src/Person.java b/VirusBroadcast_by_Java/src/Person.java similarity index 100% rename from src/Person.java rename to VirusBroadcast_by_Java/src/Person.java diff --git a/src/PersonPool.java b/VirusBroadcast_by_Java/src/PersonPool.java similarity index 100% rename from src/PersonPool.java rename to VirusBroadcast_by_Java/src/PersonPool.java diff --git a/src/Point.java b/VirusBroadcast_by_Java/src/Point.java similarity index 100% rename from src/Point.java rename to VirusBroadcast_by_Java/src/Point.java diff --git a/src/Virus.java b/VirusBroadcast_by_Java/src/Virus.java similarity index 100% rename from src/Virus.java rename to VirusBroadcast_by_Java/src/Virus.java diff --git a/VirusBroadcast_by_Kotlin/.idea/VirusBroadcast_by_Kotlin.iml b/VirusBroadcast_by_Kotlin/.idea/VirusBroadcast_by_Kotlin.iml new file mode 100644 index 0000000..245d342 --- /dev/null +++ b/VirusBroadcast_by_Kotlin/.idea/VirusBroadcast_by_Kotlin.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/.idea/kotlinc.xml b/VirusBroadcast_by_Kotlin/.idea/kotlinc.xml new file mode 100644 index 0000000..3c50bd3 --- /dev/null +++ b/VirusBroadcast_by_Kotlin/.idea/kotlinc.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/.idea/libraries/KotlinJavaRuntime.xml b/VirusBroadcast_by_Kotlin/.idea/libraries/KotlinJavaRuntime.xml new file mode 100644 index 0000000..1a7265d --- /dev/null +++ b/VirusBroadcast_by_Kotlin/.idea/libraries/KotlinJavaRuntime.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/.idea/misc.xml b/VirusBroadcast_by_Kotlin/.idea/misc.xml new file mode 100644 index 0000000..8f4aae2 --- /dev/null +++ b/VirusBroadcast_by_Kotlin/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/.idea/modules.xml b/VirusBroadcast_by_Kotlin/.idea/modules.xml new file mode 100644 index 0000000..fe050ca --- /dev/null +++ b/VirusBroadcast_by_Kotlin/.idea/modules.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/VirusBroadcast_by_Kotlin/.idea/vcs.xml similarity index 69% rename from .idea/vcs.xml rename to VirusBroadcast_by_Kotlin/.idea/vcs.xml index 35eb1dd..6c0b863 100644 --- a/.idea/vcs.xml +++ b/VirusBroadcast_by_Kotlin/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/VirusBroadcast_by_Kotlin.iml b/VirusBroadcast_by_Kotlin/VirusBroadcast_by_Kotlin.iml new file mode 100644 index 0000000..245d342 --- /dev/null +++ b/VirusBroadcast_by_Kotlin/VirusBroadcast_by_Kotlin.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/src/Constants.kt b/VirusBroadcast_by_Kotlin/src/Constants.kt new file mode 100644 index 0000000..bf3a5d7 --- /dev/null +++ b/VirusBroadcast_by_Kotlin/src/Constants.kt @@ -0,0 +1,30 @@ +/** + * @ClassName: Constants + * @Description: 模拟参数 + * @Author: cnctemaR + * @Date: 2020/2/7 0:40 + * */ +object Constants { + const val ORIGINAL_COUNT = 50//初始感染数量 + const val BROAD_RATE = 0.8//传播率 + const val SHADOW_TIME = 140.0//潜伏时间,14天为140 + const val HOSPITAL_RECEIVE_TIME = 10//医院收治响应时间 + const val BED_COUNT = 1000//医院床位 + /** + * 流动意向平均值,建议调整范围:[-0.99,0.99] + * + * + * -0.99 人群流动最慢速率,甚至完全控制疫情传播 + * 0.99为人群流动最快速率, 可导致全城感染 + */ + const val u = 0.99 + const val CITY_PERSON_SIZE = 5000//城市总人口数量 + const val FATALITY_RATE = 0.50//fatality_rate病死率,根据2月6日数据估算(病死数/确诊数)为0.02 + const val DIE_TIME = 100//死亡时间均值,30天,从发病(确诊)时开始计时 + const val DIE_VARIANCE = 1.0//死亡时间方差 + /** + * 城市大小即窗口边界,限制不允许出城 + */ + const val CITY_WIDTH = 700 + const val CITY_HEIGHT = 800 +} diff --git a/VirusBroadcast_by_Kotlin/src/EntityClass.kt b/VirusBroadcast_by_Kotlin/src/EntityClass.kt new file mode 100644 index 0000000..31056b4 --- /dev/null +++ b/VirusBroadcast_by_Kotlin/src/EntityClass.kt @@ -0,0 +1,139 @@ +import java.util.* +import java.util.ArrayList + + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 0:12 + * */ +open class Point(open var x: Int, open var y: Int) { + fun moveTo(x: Int, y: Int) { + this.x += x + this.y += y + } +} + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 0:12 + * */ +class Bed(x: Int, y: Int) : Point(x, y) { + var isEmpty = true +} + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 0:12 + * */ +class MoveTarget(var x: Int, var y: Int) { + var arrived: Boolean = false //是否到达目标点 +} + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 0:12 + * */ +class City(var centerX: Int, var cneterY: Int) + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 0:12 + * */ +class Hospital private constructor(x: Int, y: Int) : Point(x, y) { + companion object { + const val HOSPITAL_X = 720 + const val HOSPITAL_Y = 80 + val hospital = Hospital(HOSPITAL_X,HOSPITAL_Y) + } + + var width = 0 + private set + var height = 600 + private set + val point = Point(HOSPITAL_X, HOSPITAL_Y) //第一个床位所在坐标,用于给其他床位定绝对坐标 + val beds = ArrayList() + + //初始化构造 + init { + //医院矩形所在坐标 + this.x = HOSPITAL_X + this.y = HOSPITAL_Y + 10 + //根据床位数量调整医院矩形的大小 + if (Constants.BED_COUNT == 0) { + width = 0 + height = 0 + } + //根据医院床位数量计算医院宽度 + //因为高度定了只能装载100个床位 + val column = Constants.BED_COUNT / 100 + width = column * 6 + //根据第一个床位坐标初始化其他床位的坐标 + for (i in 0 until column) + for (j in 10..606 step 6) { + val bed = Bed(point.x + i * 6, point.y + j) + beds.add(bed) + if (beds.size >= Constants.BED_COUNT) //确定医院床位承载数量 + break + } + } + + fun pickBed(): Bed? { + return beds.find { it.isEmpty } + } + + //死亡或痊愈出院空出床位 + fun returnBed(bed: Bed?): Bed? { + if (bed != null) + bed.isEmpty = true + return bed + } +} + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 0:12 + * */ +class PersonPool private constructor() { + var personList: MutableList = ArrayList() + private set + + init { + val city = City(400, 400) //设置城市中心为坐标(400,400) + for (i in 0 until Constants.CITY_PERSON_SIZE) { + val random = Random() + var x = (100 * random.nextGaussian() + city.centerX).toInt() + val y = (100 * random.nextGaussian() + city.cneterY).toInt() + if (x > 700) + x = 700 + + personList.add(Person(city, x, y)) + } + } + + companion object { + val personPool = PersonPool() + } + + /** + * @param state 市民类型 Person.State的值,若为-1则返回当前总数目 + * @return 获取指定人群数量 + */ + fun getPeopleSize(state: Int): Int { + if (state == -1) + return personList.size + return personList.count {it.state == state} + } + +} \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/src/Main.kt b/VirusBroadcast_by_Kotlin/src/Main.kt new file mode 100644 index 0000000..7a2429f --- /dev/null +++ b/VirusBroadcast_by_Kotlin/src/Main.kt @@ -0,0 +1,56 @@ +import javax.swing.JFrame +import java.util.Random + + + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 0:12 + * */ +private var hospitalWidth: Int = 0 + +fun main(args: Array) { + initHospital() + initPanel() + initInfected() +} + +/** + * 初始化画布 + */ +fun initPanel(){ + val p = MyPanel() + val panelThread = Thread(p) + val frame = JFrame() + frame.add(p) + frame.setSize(Constants.CITY_WIDTH + hospitalWidth + 300, Constants.CITY_HEIGHT) + frame.setLocationRelativeTo(null) + frame.isVisible = true + frame.title = "瘟疫传播模拟" + frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE + panelThread.start()//开启画布线程,即世界线程,接着看代码的下一站可以转MyPanel.java + +} + +/** + * 初始化医院参数 + */ +private fun initHospital() { + hospitalWidth = Hospital.hospital.width +} + +/** + * 初始化初始感染者 + */ +private fun initInfected(){ + val people = PersonPool.personPool.personList //获取所有的市民 + for (i in 0 until Constants.ORIGINAL_COUNT) { + var person: Person + do { + person = people[Random().nextInt(people.size - 1)]//随机挑选一个市民 + } while (person.isInfected())//如果该市民已经被感染,重新挑选 + person.beInfected()//让这个幸运的市民成为感 染者 + } +} \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/src/MathUtil.kt b/VirusBroadcast_by_Kotlin/src/MathUtil.kt new file mode 100644 index 0000000..732714b --- /dev/null +++ b/VirusBroadcast_by_Kotlin/src/MathUtil.kt @@ -0,0 +1,31 @@ +import java.util.* + +/** + * @ClassName: + * @Description:数学算法工具类 + * @Author: cnctemaR + * @Date: 2020/2/7 2:31 + * */ + +class MathUtil{ + companion object { + private val randomGen = Random() + /** + * 标准正态分布化 + *

+ * 流动意愿标准化后判断是在0的左边还是右边从而决定是否流动。 + *

+ * 设X随机变量为服从正态分布,sigma是影响分布形态的系数 u值决定正态分布均值 + *

+ *

+ * 推导: + * StdX = (X-u)/sigma + * X = sigma * StdX + u + * + * @param sigma 正态标准差sigma值 + * @param u 正态均值参数mu + * @return + */ + fun stdGaussian(sigma:Double,u:Double) = sigma*randomGen.nextGaussian()+u + } +} \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/src/MyPanel.kt b/VirusBroadcast_by_Kotlin/src/MyPanel.kt new file mode 100644 index 0000000..3d3bad6 --- /dev/null +++ b/VirusBroadcast_by_Kotlin/src/MyPanel.kt @@ -0,0 +1,85 @@ +import javax.swing.* +import java.awt.* +import java.util.Timer +import java.util.TimerTask + +/** + * 主面板。 + * + * @ClassName: MyPanel + * @Description: 主面板 + * @author: Bruce Young + * @date: 2020年02月02日 17:03 + */ +class MyPanel : JPanel(), Runnable { + val timer = Timer() + companion object { + var worldTime = 0//世界时间 + } + init { + this.background = Color(0x444444) + } + internal inner class MyTimerTask : TimerTask() { + override fun run() { + this@MyPanel.repaint() + worldTime++ + } + } + override fun run() { + timer.schedule(MyTimerTask(), 0, 100)//启动世界计时器,时间开始流动(突然脑补DIO台词:時は停た) + } + + override fun paint(g: Graphics?) { + super.paint(g) + g!!.color = Color(0x00ff00)//设置医院边界颜色 + //绘制医院边界 + g.drawRect(Hospital.hospital.x, Hospital.hospital.y, + Hospital.hospital.width, Hospital.hospital.height) + g.font = Font("微软雅黑", Font.BOLD, 16) + g.color = Color(0x00ff00) + g.drawString("医院", Hospital.hospital.x + Hospital.hospital.width / 4, Hospital.hospital.y - 16) + //绘制代表人类的圆点 + val people = PersonPool.personPool.personList +// if (people.size == 0) return + for (person in people) { + when (person.state) { + Person.State.NORMAL -> g.color = Color(0xdddddd) //健康人 + Person.State.SHADOW -> g.color = Color(0xffee00) //潜伏期感染者 + Person.State.CONFIRMED -> g.color = Color(0xff0000) //确诊患者 + Person.State.FREEZE -> g.color = Color(0x48FFFC) //已隔离者 + Person.State.DEATH -> g.color = Color(0x000000) //死亡患者 + } + person.update()//对各种状态的市民进行不同的处理 + g.fillOval(person.x, person.y, 3, 3) + } + + val captionStartOffsetX = 700 + Hospital.hospital.width + 40 + val captionStartOffsetY = 40 + val captionSize = 24 + + //显示数据信息 + g.color = Color.WHITE + g.drawString("城市总人数:" + Constants.CITY_PERSON_SIZE, captionStartOffsetX, captionStartOffsetY) + g.color = Color(0xdddddd) + g.drawString("健康者人数:" + PersonPool.personPool.getPeopleSize(Person.State.NORMAL), captionStartOffsetX, captionStartOffsetY + captionSize) + g.color = Color(0xffee00) + g.drawString("潜伏期人数:" + PersonPool.personPool.getPeopleSize(Person.State.SHADOW), captionStartOffsetX, captionStartOffsetY + 2 * captionSize) + g.color = Color(0xff0000) + g.drawString("发病者人数:" + PersonPool.personPool.getPeopleSize(Person.State.CONFIRMED), captionStartOffsetX, captionStartOffsetY + 3 * captionSize) + g.color = Color(0x48FFFC) + g.drawString("已隔离人数:" + PersonPool.personPool.getPeopleSize(Person.State.FREEZE), captionStartOffsetX, captionStartOffsetY + 4 * captionSize) + g.color = Color(0x00ff00) + g.drawString("空余病床:" + Math.max(Constants.BED_COUNT - PersonPool.personPool.getPeopleSize(Person.State.FREEZE), 0), captionStartOffsetX, captionStartOffsetY + 5 * captionSize) + g.color = Color(0xE39476) + //暂定急需病床数量为 NEED = 确诊发病者数量 - 已隔离住院数量 + val needBeds = PersonPool.personPool.getPeopleSize(Person.State.CONFIRMED) - PersonPool.personPool.getPeopleSize(Person.State.FREEZE) + + g.drawString("急需病床:" + if (needBeds > 0) needBeds else 0, captionStartOffsetX, captionStartOffsetY + 6 * captionSize) + g.color = Color(0xccbbcc) + g.drawString("病死人数:" + PersonPool.personPool.getPeopleSize(Person.State.DEATH), captionStartOffsetX, captionStartOffsetY + 7 * captionSize) + g.color = Color(0xffffff) + g.drawString("世界时间(天):" + (worldTime / 10.0).toInt(), captionStartOffsetX, captionStartOffsetY + 8 * captionSize) + } + + +} \ No newline at end of file diff --git a/VirusBroadcast_by_Kotlin/src/Person.kt b/VirusBroadcast_by_Kotlin/src/Person.kt new file mode 100644 index 0000000..83fa21e --- /dev/null +++ b/VirusBroadcast_by_Kotlin/src/Person.kt @@ -0,0 +1,213 @@ +import Person.State.Companion.DEATH +import java.util.Random + +/** + * @ClassName: + * @Description: + * @Author: cnctemaR + * @Date: 2020/2/7 2:43 + * */ + +class Person(private val city: City, override var x: Int, override var y: Int) : Point(x, y) { + /** + * 正态分布N(mu,sigma)随机位移目标位置 + */ + private val targetXU: Double = MathUtil.stdGaussian(100.0, x.toDouble()) //x方向的均值mu + private val targetYU: Double = MathUtil.stdGaussian(100.0, y.toDouble()) //y方向的均值mu + private val targetSig: Double = 50.0 //方差sigma + private var moveTarget: MoveTarget? = null + + private val sig = 1 //人群流动意愿影响系数:正态分布方差sigma + private val SAFE_DIST = 2.0//安全距离 + var useBed: Bed? = null + + var state = State.NORMAL + var infectedTime = 0//感染时刻 + var confirmedTime = 0//确诊时刻 + var dieMoment = 0//死亡时刻,为0代表未确定,-1代表不会病死 + + + interface State { + companion object { + const val NORMAL = 0//正常人,未感染的健康人 + const val SUSPECTED = NORMAL + 1//有暴露感染风险 + const val SHADOW = SUSPECTED + 1//潜伏期 + const val CONFIRMED = SHADOW + 1//发病且已确诊为感染病人 + const val FREEZE = CONFIRMED + 1//隔离治疗,禁止位移 + + //已治愈出院的人转为NORMAL即可,否则会与作者通过数值大小判断状态的代码冲突 + const val DEATH = FREEZE + 1//病死者 + //int CURED = DEATH + 1;//治愈数量用于计算治愈出院后归还床位数量,该状态是否存续待定 + } + } + + /** + * 流动意愿标准化 + * + * 根据标准正态分布生成随机人口流动意愿 + * + * 流动意愿标准化后判断是在0的左边还是右边从而决定是否流动。 + * + * 设X随机变量为服从正态分布,sigma是影响分布形态的系数,从而影响整体人群流动意愿分布 + * u值决定正态分布的中轴是让更多人群偏向希望流动或者希望懒惰。 + * + * value的推导: + * StdX = (X-u)/sigma + * X = sigma * StdX + u + * + * @return + */ + fun wantMove(): Boolean = MathUtil.stdGaussian(sig.toDouble(), Constants.u) > 0 + + fun isInfected(): Boolean = state >= State.SHADOW + + fun beInfected() { + state = State.SHADOW + infectedTime = MyPanel.worldTime + } + /** + * 住院 + */ + private fun freezy() { + state = State.FREEZE + } + + /** + * 计算两点之间的直线距离 + * + * @param person + * @return + */ + fun distance(person: Person): Double = Math.sqrt(Math.pow((x - person.x).toDouble(), 2.0) + Math.pow((y - person.y).toDouble(), 2.0)) + + /** + * 不同状态下的单个人实例运动行为 + */ + private fun action() { + + if (state == State.FREEZE || state == State.DEATH) { + return //如果处于隔离或者死亡状态,则无法行动 + } + if (!wantMove()) { + return + } + //存在流动意愿的,将进行流动,流动位移仍然遵循标准正态分布 + if (moveTarget == null || moveTarget!!.arrived) { + //在想要移动并且没有目标时,将自身移动目标设置为随机生成的符合正态分布的目标点 + //产生N(a,b)的数:Math.sqrt(b)*random.nextGaussian()+a + val targetX = MathUtil.stdGaussian(targetSig, targetXU) + val targetY = MathUtil.stdGaussian(targetSig, targetYU) + moveTarget = MoveTarget(targetX.toInt(), targetY.toInt()) + + } + + //计算运动位移 + val dX = moveTarget!!.x - x + val dY = moveTarget!!.y - y + + val length = Math.sqrt(Math.pow(dX.toDouble(), 2.0) + Math.pow(dY.toDouble(), 2.0))//与目标点的距离 + + if (length < 1) { + //判断是否到达目标点 + moveTarget!!.arrived = true + return + } + + var udX = (dX / length).toInt()//x轴dX为位移量,符号为沿x轴前进方向, 即udX为X方向表示量 + if (udX == 0 && dX != 0) + udX = if (dX > 0) 1 else -1 + + + var udY = (dY / length).toInt()//y轴dY为位移量,符号为沿x轴前进方向,即udY为Y方向表示量 + //FIXED: 修正一处错误 + if (udY == 0 && dY != 0) + udY = if (dY > 0) 1 else -1 + + //横向运动边界 + if (x > Constants.CITY_WIDTH || x < 0) { + moveTarget = null + if (udX > 0) + udX = -udX + } + //纵向运动边界 + if (y > Constants.CITY_HEIGHT || y < 0) { + moveTarget = null + if (udY > 0) + udY = -udY + } + moveTo(udX, udY) + + } + + + /** + * 对各种状态的人进行不同的处理,更新发布市民健康状态 + */ + fun update() { + //@TODO找时间改为状态机 + if (state == State.FREEZE || state == State.DEATH) + return//如果已经隔离或者死亡了,就不需要处理了 + + //处理已经确诊的感染者(即患者) + if (state == State.CONFIRMED && dieMoment == 0) { + val destiny = Random().nextInt(10000) + 1//幸运数字,[1,10000]随机数 + dieMoment = if (1 <= destiny && destiny <= (Constants.FATALITY_RATE * 10000).toInt()) { + //如果幸运数字落在死亡区间 + val dieTime = MathUtil.stdGaussian(Constants.DIE_VARIANCE, Constants.DIE_TIME.toDouble()).toInt() + confirmedTime + dieTime//发病后确定死亡时刻 + } else { + -1//逃过了死神的魔爪 + } + } + //TODO 暂时缺失治愈出院市民的处理。需要确定一个变量用于治愈时长。由于案例太少,暂不加入。 + + + if (state == State.CONFIRMED && MyPanel.worldTime - confirmedTime >= Constants.HOSPITAL_RECEIVE_TIME) { + //如果患者已经确诊,且(世界时刻-确诊时刻)大于医院响应时间,即医院准备好病床了,可以抬走了 + val bed = Hospital.hospital.pickBed()//查找空床位 + if (bed == null) { + //没有床位了,报告需求床位数 + + } else { + //安置病人 + useBed = bed + state = State.FREEZE + x = bed.x + y = bed.y + bed.isEmpty = false + } + } + + //处理病死者 + if ((state == State.CONFIRMED || state == State.FREEZE) && MyPanel.worldTime >= dieMoment && dieMoment > 0) { + state = State.DEATH//患者死亡 + Hospital.hospital.returnBed(useBed)//归还床位 + } + + //增加一个正态分布用于潜伏期内随机发病时间 + val stdRnShadowtime = MathUtil.stdGaussian(25.0, Constants.SHADOW_TIME / 2) + //处理发病的潜伏期感染者 + if (MyPanel.worldTime - infectedTime > stdRnShadowtime && state == State.SHADOW) { + state = State.CONFIRMED//潜伏者发病 + confirmedTime = MyPanel.worldTime//刷新时间 + } + //处理未隔离者的移动问题 + action() + //处理健康人被感染的问题 + val people = PersonPool.personPool.personList + if (state >= State.SHADOW) { + return + } + //通过一个随机幸运值和安全距离决定感染其他人 + for (person in people) { + if (person.state == State.NORMAL) { + continue + } + val random = Random().nextFloat() + if (random < Constants.BROAD_RATE && distance(person) < SAFE_DIST) { + this.beInfected() + break + } + } + } +} \ No newline at end of file