Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import com.hamoid.*;
- VideoExport videoExport;
- ArrayList<Person> people;
- Boundary world;
- Boundary hospital;
- int population = 2000;
- PopStats pStats;
- int daysInfectedWithoutSymptoms = 3;
- int daysInfectedWithSymptoms = 7;
- int daysRecoveringButInfectious = 4;
- int simTicksPerDay = 60;
- int walkSpeed = 1;
- float mortalityRate = 0.02;
- //Whether or not people will stay in during certain circumstances
- boolean qStayIn = false; //Will stay in forever, after infected
- boolean qWithoutSymptoms = false; //Will stay in while they're initially infected but not showing symptoms
- boolean qWithSymptoms = true; //will stay in while they're showing symptoms
- boolean qInRecovery = false; //Will stay in while they're recovering without symptoms, but still infectious
- //Initializing bools for determining if the recommended or mandated stay-ins are in effect
- boolean govReccomend = false;
- boolean govRecFinished = false;
- boolean govMandate = false;
- boolean govManFinished = false;
- //How many days (in ticks) the reccomended stay-in will last.
- int recDayTicks = 28 * simTicksPerDay;
- int recTicks = 0;
- //How many days (in ticks) the mandated stay-in will last. Set to the full lifespan of infectiousness.
- int manDayTicks = (daysInfectedWithoutSymptoms + daysInfectedWithSymptoms + daysRecoveringButInfectious) * simTicksPerDay;
- int manTicks = 0;
- ArrayList<PopStats> graph = new ArrayList<PopStats>();
- //Counts the ticks after everyone's free of infections (healthy, recovered, or dead)
- int FinishedTick = 0;
- //Used to calculate R0 (growth rate)
- float oldInf = 1;
- float newInf = 1;
- float r0;
- void setup() {
- //fullScreen();
- size(1400, 1600);
- videoExport = new VideoExport(this);
- videoExport.startMovie();
- //Set the bounds for people to bounce in
- world = new Boundary(0, 280, 1400, 1120);
- //Create an array of people the size of the entered population
- people = new ArrayList<Person>();
- for (int i = 0; i < population; i++) {
- people.add(new Person(world));
- }
- //Set one person to 'Infected Without Symptoms'
- people.get(0).setInfState(1);
- //Initialize simulation stats
- pStats = new PopStats();
- //Initialize Graph
- for (int i = 0; i < 1400; i++) {
- graph.add(new PopStats());
- }
- }
- void draw() {
- background(51);
- world.show();
- //Move each person and progress their infection
- //Bounce them back into the bounds, if they travel outside
- for (Person p : people) {
- p.update();
- p.bounds();
- }
- //Calculate collisions with other people, spreading infection and recalculating velocity
- //Display each person
- //These are separated into their own for() loop, because collisions need to be calculated after EVERYONE moves
- for (Person p : people) {
- p.collide();
- p.show();
- }
- // Update Stats
- pStats.update();
- //Close app if there's no more infection, after 300 ticks
- if (pStats.healthy + pStats.recd + pStats.dead == population) {
- FinishedTick++;
- if (FinishedTick >= 300) {
- videoExport.endMovie();
- exit();
- }
- }
- //Add new stats data to the graph at the bottom of the simulation
- graph.add(new PopStats());
- graph.get(graph.size()-1).healthy = pStats.healthy;
- graph.get(graph.size()-1).infWOSym = pStats.infWOSym;
- graph.get(graph.size()-1).infWSym = pStats.infWSym;
- graph.get(graph.size()-1).recWOSym = pStats.recWOSym;
- graph.get(graph.size()-1).recd = pStats.recd;
- graph.get(graph.size()-1).dead = pStats.dead;
- if (graph.size() > width) {
- graph.remove(0);
- }
- //Plot the graph at the bottom of the simulation
- for (int i = 0; i < graph.size()-1; i++) {
- strokeWeight(1);
- float s1 = (graph.get(i).healthy * 1.0) / (population*1.0) * 200.0;
- float s2 = (graph.get(i).infWOSym * 1.0) / (population*1.0) * 200.0;
- float s3 = (graph.get(i).infWSym * 1.0) / (population*1.0) * 200.0;
- float s4 = (graph.get(i).recWOSym * 1.0) / (population*1.0) * 200.0;
- float s5 = (graph.get(i).recd * 1.0) / (population*1.0) * 200.0;
- float s6 = (graph.get(i).dead * 1.0) / (population*1.0) * 200.0;
- stroke(127, 127, 127);
- line(i, 1400, i, 1400 + s1);
- stroke(255, 255, 0);
- line(i, 1400 + s1, i, 1400 + s1 + s2);
- stroke(255, 0, 0);
- line(i, 1400 + s1 + s2, i, 1400 + s1 + s2 + s3);
- stroke(255, 0, 255);
- line(i, 1400 + s1 + s2 + s3, i, 1400 + s1 + s2 + s3 + s4);
- stroke(0, 0, 255);
- line(i, 1400 + s1 + s2 + s3 + s4, i, 1400 + s1 + s2 + s3 + s4 + s5);
- stroke(0, 0, 0);
- line(i, 1400 + s1 + s2 + s3 + s4 + s5, i, 1400 + s1 + s2 + s3 + s4 + s5 + s6);
- }
- //Reccomend Stay-In when >0.1% of the population is symptomatic
- if ((pStats.infWSym / 1.0) / (population / 1.0) > 0.001) {
- if (!govRecFinished) {
- govReccomend = true;
- }
- }
- //Make the stay in last for 28 days, unless >0.5% of the population is still symptomatic. In which case, extend the stay-in.
- if (govReccomend == true && govRecFinished == false) {
- recTicks++;
- if (recTicks >= recDayTicks && (pStats.infWSym / 1.0) / (population / 1.0) <= 0.005) {
- govReccomend = false;
- govRecFinished = true;
- }
- }
- //Stay-in required by law for all people if >5.0% of the population is symptomatic, but only if the stay-in fails.
- if (govRecFinished) {
- if ((pStats.infWSym / 1.0) / (population / 1.0) > 0.05) {
- if (!govManFinished) {
- govMandate = true;
- }
- }
- //Mandate lasts the length of incubation + symptom + recovery. In this case 14 days, but can be changed at the top.
- if (govMandate == true && govManFinished == false && manTicks < manDayTicks) {
- manTicks++;
- if (manTicks >= manDayTicks) {
- govMandate = false;
- govManFinished = true;
- }
- }
- }
- //Show stats at top of simulation
- fill(255);
- textSize(20);
- text("Healthy: " + pStats.healthy, 10, 20);
- text("Infected: " + (pStats.infWOSym + pStats.infWSym + pStats.recWOSym), 10, 45);
- text("Showing Symptoms: " + pStats.infWSym + " (" + (round(pStats.infWSym / (population/1.0)*1000.0)/10.0) + "% of pop.)", 10, 70);
- text("People showing symptoms stay home sick.", 450, 70);
- text("Recovered: " + (pStats.recd), 10, 95);
- text("Dead: " + (pStats.dead), 10, 120);
- //Show message that the recomended stay-in has been issued.
- textSize(30);
- if (govReccomend == true && govRecFinished == false) {
- fill(255, 255, 0);
- text("Stay-In reccomended by government. 80% of the population is adhering to the Stay-In.", 10, 180);
- if (recDayTicks-recTicks > 0) {
- text(round((recDayTicks-recTicks)/(recDayTicks*1.0)*28.0) + " days of Stay-In remaining.", 10, 220);
- } else {
- text("Stay-In extended until <0.5% pop is symptomatic.", 10, 220);
- }
- }
- //Show message that mandated stay-in has been issued
- if (govMandate == true && govManFinished == false) {
- fill(255, 0, 0);
- text("Government Mandate issued. All of the population is adhering.", 10, 180);
- text(round((manDayTicks-manTicks)/(manDayTicks*1.0)*14.0) + " days of mandate remaining.", 10, 220);
- }
- //Save video frame
- if (frameCount%5 == 0) {
- //videoExport.saveFrame();
- }
- //Calculate R0 (growth rate), printed in console.
- if(frameCount%60 == 30){
- oldInf = newInf;
- newInf = (population - pStats.healthy) - oldInf;
- r0 = newInf / oldInf;
- println(r0);
- }
- }
- //Quit and export if 'Q' is pressed
- void keyPressed() {
- if (key == 'q') {
- videoExport.endMovie();
- exit();
- }
- }
- class Person {
- int infState;
- /*
- 0 = Healthy
- 1 = Infected Without Symptoms
- 2 = Infected
- 3 = Recovering But Infectious
- 4 = Recovered
- 5 = Dead
- */
- //infTick is used to count the ticks for each phase of infection
- int[] infTick = new int[6];
- //Position and Velocity vectors
- PVector pos;
- PVector vel;
- //The bounds to bounce in
- Boundary bounds;
- //the color, based on their state of infection
- color infCol;
- //Whether or not they're isolating
- boolean socDist = false;
- //To determine if they will follow the reccomended stay-in
- boolean followRec;
- Person(Boundary b) {
- infState = 0; //Everyone starts healthy. One person is manually infected at the start of the simulation.
- //Random start position within bounds
- bounds = b;
- pos = new PVector(random(bounds.right) + bounds.left, random(bounds.bottom) + bounds.top);
- //Random initial velocity (any direction on a unit circle * walking speed)
- vel = new PVector(walkSpeed, 0).rotate(random(TWO_PI));
- //Start out looking healthy
- infCol = color(127, 127, 127);
- //Determines if they will follow the reccomended stay-in. Currently 80% chance.
- followRec = random(1)>0.2;
- }
- void update() {
- //Count how many ticks they've been in their current state
- infTick[infState] += 1;
- //If they're infected, advance them to the next stage if it's been long enough
- if (infState == 1 && infTick[1] >= daysInfectedWithoutSymptoms * simTicksPerDay) {
- setInfState(2);
- }
- if (infState == 2 && infTick[2] >= daysInfectedWithSymptoms * simTicksPerDay) {
- setInfState(3);
- }
- if (infState == 3 && infTick[3] >= daysRecoveringButInfectious * simTicksPerDay) {
- setInfState(4);
- }
- //If they're infected, check to see if they happen to die
- //Mortality rate is currently 2%, but infection currently lasts 840 ticks (14 days * 60 ticks per day)
- //So instead of a 1/50 (2%) chance of death each tick, they have a (1/50 * 1/840) chance of death each tick, or 1/42,000
- //Since they have a 1/42,000 chance 840 times, their true mortality rate is still 1/50.
- if (infState == 1 || infState == 2 || infState == 3) {
- float mortalityCheck = random(1);
- if (mortalityCheck <= 1.0 / ((daysInfectedWithoutSymptoms+daysInfectedWithSymptoms+daysRecoveringButInfectious) * simTicksPerDay / mortalityRate)) {
- setInfState(5);
- }
- }
- //Move the character as long as they aren't opting to isolate themselves, and as long as they aren't mandated to stay-in
- if (!(socDist || govMandate || (followRec && govReccomend))) {
- pos.x += vel.x;
- pos.y += vel.y;
- }
- }
- //Changes the state of infection. Also decides whther or not they choose to isolate themselves
- void setInfState(int i) {
- //Infected without symptoms
- if (i == 1) {
- infState = 1;
- infCol = color(255, 255, 0);
- socDist = qWithoutSymptoms || qStayIn;
- }
- //Infected with symptoms
- if (i == 2) {
- infState = 2;
- infCol = color(255, 0, 0);
- socDist = qWithSymptoms || qStayIn;
- }
- //Recovering but infectious, no symptoms
- if (i == 3) {
- infState = 3;
- infCol = color(250, 0, 250);
- socDist = qInRecovery || qStayIn;
- }
- //Recovered
- if (i == 4) {
- infState = 4;
- infCol = color(0, 0, 255);
- socDist = qStayIn;
- }
- //Dead
- if (i == 5) {
- infState = 5;
- infCol = color(0, 0, 0);
- socDist = true;
- }
- }
- //Checks collision with boundary
- void bounds() {
- if (pos.x > bounds.left + bounds.right) {
- pos.x = 2 * bounds.left + 2 * bounds.right - pos.x;
- vel.x *= -1;
- }
- if (pos.y > bounds.top + bounds.bottom) {
- pos.y = 2 * bounds.top + 2 * bounds.bottom - pos.y;
- vel.y *= -1;
- }
- if (pos.x < bounds.left) {
- pos.x = 2 * bounds.left - pos.x;
- vel.x *= -1;
- }
- if (pos.y < bounds.top) {
- pos.y = 2 * bounds.top - pos.y;
- vel.y *= -1;
- }
- }
- //Checks collision with every other person
- void collide() {
- boolean hit = false;
- PVector newVel = new PVector(0, 0);
- for (Person p : people) {
- //As long as both this person and the collided person are not isolating
- if (p != this && !(p.socDist || govMandate || (p.followRec && govReccomend))) {
- if (!(socDist || govMandate || (followRec && govReccomend))) {
- //As long as they touched eachother
- if (pos.dist(p.pos) < 10) {
- hit = true; //Confirm the hit
- //Calculate the temporary bounce vector, and add it to the new veloctiy vector
- //(in case there are more than 1 bounce per tick, we add all bounce vectors first, to determine the true new velocity)
- PVector tempVel = new PVector(pos.x - p.pos.x, pos.y - p.pos.y).normalize();
- newVel.x += tempVel.x;
- newVel.y += tempVel.y;
- //Infect this person, if they aren't already, and collide with an infected
- if (p.infState == 1 || p.infState == 2 || p.infState == 3) {
- if (infState == 0) {
- setInfState(1);
- }
- }
- }
- }
- }
- }
- //Recalculate velocity if they collided
- if (hit) {
- newVel.normalize().setMag(walkSpeed);
- vel.x = newVel.x;
- vel.y = newVel.y;
- }
- }
- //Draw the person
- void show() {
- noStroke();
- fill(infCol);
- circle(pos.x, pos.y, 10);
- }
- }
- class Boundary {
- float top;
- float bottom;
- float left;
- float right;
- Boundary(float a, float b, float c, float d) {
- left = a;
- top = b;
- right = c;
- bottom = d;
- }
- void show() {
- stroke(255);
- strokeWeight(5);
- noFill();
- rect(left, top, right, bottom);
- }
- }
- class PopStats {
- int healthy = population;
- int infWOSym = 0;
- int infWSym = 0;
- int recWOSym = 0;
- int recd = 0;
- int dead = 0;
- void update() {
- healthy = 0;
- infWOSym = 0;
- infWSym = 0;
- recWOSym = 0;
- recd = 0;
- dead = 0;
- for (Person p : people) {
- switch(p.infState) {
- case 0:
- healthy++;
- break;
- case 1:
- infWOSym++;
- break;
- case 2:
- infWSym++;
- break;
- case 3:
- recWOSym++;
- break;
- case 4:
- recd++;
- break;
- case 5:
- dead++;
- break;
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement