├── requirements.txt ├── death_toll.png ├── README.md └── epidemic.py /requirements.txt: -------------------------------------------------------------------------------- 1 | pygame==1.9.6 2 | -------------------------------------------------------------------------------- /death_toll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/najibelmokhtari/basic-epidemic-simulation/HEAD/death_toll.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Basic epidemic simulation application 2 | ================================ 3 | 4 | 5 | What Is This? 6 | ------------- 7 | 8 | This is a simple Python epidemic simulation used to illustrate the spread of epidemics through two factors: 9 | - Death rate of a disease 10 | - Contagion ratio (R0) of the disease 11 | 12 | The script can be configured by changing the constant values corresponding to each of these factors. A pre-vaccination ratio can also be used to make a part of the population immune before the epidemic outbreak. 13 | This is only a basic model and the purpose of this application was to illustrate basic concepts of epidemiology in a Moroccan pop science youtube video. 14 | 15 | The episode is available here: 16 | 17 | https://youtu.be/02WZ08QTVmg 18 | 19 | 20 | How To Use This 21 | --------------- 22 | - `pip install -r requirements.txt` 23 | 24 | - Modify constants such as `PROBA_DEATH`, `CONTAGION_RATE` and `VACCINATION_RATE`. 25 | 26 | - Run the simulation through: 27 | `python epidemic.py` 28 | 29 | - Press the Space bar to run or re-run the simulation 30 | -------------------------------------------------------------------------------- /epidemic.py: -------------------------------------------------------------------------------- 1 | 2 | # 0: healthy 3 | # 1: immune 4 | # 10 - 20 : infected time 5 | # -1: dead 6 | 7 | from random import randrange 8 | import pygame 9 | import sys 10 | 11 | 12 | PROBA_DEATH = 100 # the black plague was 100%. smallpox was ~30%-35% 13 | CONTAGION_RATE = 4.5 # This is the R0 factor. Number of people an individual will infect on average 14 | 15 | PROBA_INFECT = CONTAGION_RATE * 10 16 | 17 | VACCINATION_RATE = 0 18 | SIMULATION_SPEED = 25 # time between days in milliseconds. 0: fastest. 19 | # 500 means every day the simulation pauses for 500 ms 20 | # 25 is good for watching 21 | 22 | nb_rows = 50 23 | nb_cols = 50 24 | 25 | global states, states_temp 26 | states = [[0] * nb_cols for i1 in range(nb_rows)] 27 | states_temp = [[0] * nb_cols for i1 in range(nb_rows)] 28 | 29 | 30 | def get_neighbour(x, y): 31 | incx = randrange(3) 32 | incy = randrange(3) 33 | 34 | incx = (incx * 1) - 1 35 | incy = (incy * 1) - 1 36 | 37 | x2 = x + incx 38 | y2 = y + incy 39 | 40 | if x2 < 0: 41 | x2 = 0 42 | if x2 >= nb_cols: 43 | x2 = nb_cols - 1 44 | if y2 < 0: 45 | y2 = 0 46 | if y2 >= nb_rows: 47 | y2 = nb_rows - 1 48 | 49 | return [x2, y2] 50 | 51 | 52 | global display 53 | global myfont 54 | global initial_pause 55 | 56 | WHITE = (255, 255, 255) 57 | BLUE = (0, 0, 255) 58 | GREEN = (0, 100, 0) 59 | BLACK = (0, 0, 0) 60 | 61 | 62 | def init_screen(): 63 | global display 64 | 65 | global myfont 66 | myfont = pygame.font.SysFont('Calibri', 40) 67 | display.fill(WHITE) 68 | 69 | image = pygame.image.load("death_toll.png").convert_alpha() 70 | image = pygame.transform.rotozoom(image, 0, .35) 71 | 72 | display.blit(image, (540, 23)) 73 | 74 | 75 | def vaccinate(): 76 | for x in range(nb_cols): 77 | for y in range(nb_rows): 78 | if randrange(99) < VACCINATION_RATE: 79 | states[x][y] = 1 80 | 81 | 82 | def count_dead(): 83 | global states 84 | count = 0 85 | for x in range(nb_cols): 86 | for y in range(nb_rows): 87 | if states[x][y] == -1: 88 | count = count + 1 89 | return count 90 | 91 | 92 | def main(): 93 | pygame.init() 94 | 95 | pygame.font.init() 96 | 97 | global display 98 | display=pygame.display.set_mode((800,750),0,32) 99 | pygame.display.set_caption("Virus Epidemic") 100 | 101 | init_screen() 102 | 103 | global states, states_temp 104 | states[5][5] = 10 105 | vaccinate() 106 | 107 | image = pygame.image.load("death_toll.png").convert_alpha() 108 | image = pygame.transform.rotozoom(image, 0, .35) 109 | 110 | display.blit(image, (540, 23)) 111 | 112 | global initial_pause 113 | initial_pause = True 114 | 115 | it = 0 116 | death_toll = 0 117 | while True: 118 | 119 | pygame.time.delay(SIMULATION_SPEED) 120 | 121 | it = it + 1 122 | if it <= 10000 and it >= 2: 123 | states_temp = states.copy() 124 | for x in range(nb_cols): 125 | for y in range(nb_rows): 126 | 127 | state = states[x][y] 128 | 129 | if state == -1: 130 | pass 131 | 132 | if state >= 10: 133 | states_temp[x][y] = state + 1 134 | if state >= 20: 135 | if randrange(99) < PROBA_DEATH: 136 | states_temp[x][y] = -1 137 | else: 138 | states_temp[x][y] = 1 139 | 140 | if state >= 10 and state <= 20: 141 | if randrange(99) < PROBA_INFECT: 142 | neighbour = get_neighbour(x, y) 143 | x2 = neighbour[0] 144 | y2 = neighbour[1] 145 | 146 | neigh_state = states[x2][y2] 147 | if neigh_state == 0: 148 | states_temp[x2][y2] = 10 149 | 150 | states = states_temp.copy() 151 | death_toll = count_dead() 152 | 153 | pygame.draw.rect(display, WHITE, (450, 30, 80, 50)) 154 | textsurface = myfont.render(str(death_toll), False, (0, 0, 0)) 155 | display.blit(textsurface, (450, 30)) 156 | 157 | for x in range(nb_cols): 158 | for y in range(nb_rows): 159 | if states[x][y] == 0: 160 | color = BLUE 161 | if states[x][y] == 1: 162 | color = GREEN 163 | if states[x][y] >= 10: 164 | color = (states[x][y] * 12, 50, 50) 165 | if states[x][y] == -1: 166 | color = WHITE 167 | 168 | pygame.draw.circle(display, color, (100 + x * 12 + 5, 100 + y * 12 + 5), 5) 169 | pygame.draw.rect(display, WHITE, (100 + x * 12 + 3, 100 + y * 12 + 4, 1, 1)) 170 | pygame.draw.rect(display, WHITE, (100 + x * 12 + 5, 100 + y * 12 + 4, 1, 1)) 171 | 172 | for event in pygame.event.get(): 173 | if event.type==pygame.QUIT: 174 | pygame.quit() 175 | sys.exit() 176 | if event.type == pygame.KEYDOWN: 177 | if event.key == pygame.K_SPACE: 178 | states = [[0] * nb_cols for i1 in range(nb_rows)] 179 | states_temp = [[0] * nb_cols for i1 in range(nb_rows)] 180 | vaccinate() 181 | states_temp = states.copy() 182 | states[5][5] = 10 183 | init_screen() 184 | it = 0 185 | death_toll = 0 186 | initial_pause = True 187 | pygame.display.update() 188 | 189 | if it == 1: 190 | initial_pause = True 191 | 192 | if initial_pause: 193 | while initial_pause: 194 | for event in pygame.event.get(): 195 | if event.type == pygame.QUIT: 196 | pygame.quit() 197 | sys.exit() 198 | if event.type == pygame.KEYDOWN: 199 | if event.key == pygame.K_SPACE: 200 | initial_pause = False 201 | break 202 | 203 | 204 | main() 205 | --------------------------------------------------------------------------------