#!/usr/bin/env python3 import random import time from matplotlib import pyplot HUMAN_DEAD = 0 HUMAN_HEALTHY = 1 HUMAN_INFECTED = 2 HUMAN_IMMUNE = 3 MOSQ_POSITION = 0 MOSQ_INFECTED = 1 MOSQ_HUNGRY = 2 class Model: def __init__(self, width=32, height=32, humandens=0.15, mosquitodens=0.10, immumepct=0.1, mosqinfpct=0.1, hm_infpct=0.5, mh_infpct=0.5, hinfdiepct=0.01, mhungrypct=0.1, humandiepct=10**-6, mosqdiepct=10**-3, mosqnetdens=0.05): self.width = width self.height = height # The initial density of humans self.humandens = humandens # The initial density mosquitos self.mosquitodens = mosquitodens # The chance that a human is immune self.immumepct = immumepct # The chance that a mosquito is born infected self.mosqinfpct = mosqinfpct # The chance that a human infects a mosquito self.hm_infpct = hm_infpct # The chance that a mosquito infects a human self.mh_infpct = mh_infpct # The chance that a mosquito gets hungry self.mhungrypct = mhungrypct # The chance that an infected human dies self.hinfdiepct = hinfdiepct # The chance that an uninfected/immume human dies self.humandiepct = humandiepct # The chance that a mosquito dies self.mosqdiepct = mosqdiepct # The density of mosquito nets self.mosqnetdens = mosqnetdens maxhumans = round(self.humandens * self.width * self.height) maxmosquitos = round(self.mosquitodens * self.width * self.height) maxmosqnets = round(self.mosqnetdens * self.width * self.height) # Human: 0=dead, 1=healthy, 2=infected, 3=immume # Mosquito: [(x, y), infected, hungry] self.humans = [[HUMAN_DEAD for _ in range(width)] for _ in range(height)] self.mosquitos = [] self.mosqnets = [[False]*width for _ in range(height)] for _ in range(maxhumans): self.addhuman() for _ in range(maxmosquitos): self.addmosquito(random.uniform(0, 1) < mosqinfpct) for _ in range(maxmosqnets): self.addmosqnet() def addhuman(self): x, y = random.choice([(x, y) for x in range(self.width) for y in range(self.height) if self.humans[y][x] == HUMAN_DEAD]) if random.uniform(0, 1) < self.immumepct: self.humans[y][x] = HUMAN_IMMUNE else: self.humans[y][x] = HUMAN_HEALTHY def addmosquito(self, infected=False): height = len(self.humans) width = len(self.humans[0]) self.mosquitos.append([(random.randint(0, width-1), random.randint(0, height-1)), infected, False]) def addmosqnet(self): x, y = random.choice([(x, y) for x in range(self.width) for y in range(self.height) if self.mosqnets[y][x] == False]) self.mosqnets[y][x] = True def move(self, pos): x, y = pos possibs = [(x, y) for x, y in [(x-1, y), (x, y-1), (x+1, y), (x, y+1)] if x > -1 and x < self.width and y > -1 and y < self.height and not self.mosqnets[y][x]] try: return random.choice(possibs) except IndexError: return (x, y) def getstates(self): humanhealthy = sum(1 for row in self.humans for human in row if human == HUMAN_HEALTHY) humaninfected = sum(1 for row in self.humans for human in row if human == HUMAN_INFECTED) humanimmune = sum(1 for row in self.humans for human in row if human == HUMAN_IMMUNE) mosqinfected = sum(1 for mosq in self.mosquitos if mosq[MOSQ_INFECTED]) mosqhungry = sum(1 for mosq in self.mosquitos if mosq[MOSQ_HUNGRY]) return humanhealthy, humaninfected, humanimmune, mosqinfected, mosqhungry def step(self): # Copy the human values humans = [human_row[:] for human_row in self.humans] # Now I am become death, the destroyer of worlds killed = 0 for human_row in humans: for i, human in enumerate(human_row): if human == HUMAN_INFECTED \ and random.uniform(0, 1) < self.hinfdiepct: human_row[i] = HUMAN_DEAD killed += 1 elif human != HUMAN_DEAD \ and random.uniform(0, 1) < self.humandiepct: human_row[i] = HUMAN_DEAD killed += 1 mosqkilled = 0 mosquitos = [] for pos, infected, hungry in self.mosquitos: # Randomly move the mosquito to a new position (x, y) = self.move(pos) if hungry and self.humans[y][x]: # If a mosquito is hungry and in the same cell as a mosquito, # human, it'll bite hungry = False human = self.humans[y][x] if human == HUMAN_INFECTED: # If the human is infected, become infected infected = random.uniform(0, 1) < self.hm_infpct if infected and human == HUMAN_HEALTHY and \ random.uniform(0, 1) < self.mh_infpct: # If the human is healthy and not immune, there's a # mh_infpct chance it will become infected. humans[y][x] = HUMAN_INFECTED elif not hungry: # If the mosquito is not hungry, there's a mhungrypct # chance it'll become hungry hungry = random.uniform(0, 1) < self.mhungrypct # There's a mosqdiepct% chance that a mosq will die if random.uniform(0, 1) < self.mosqdiepct: mosqkilled += 1 else: mosquitos.append(((x, y), infected, hungry)) # Send all new values to the model self.mosquitos = mosquitos self.humans = humans for _ in range(killed): # Add a human for each one that had died self.addhuman() for _ in range(mosqkilled): # Add a mosqito for each one that had died self.addmosquito() @staticmethod def drawhuman(human): if human == HUMAN_DEAD: return (31, 31, 31) elif human == HUMAN_HEALTHY: return (0, 127, 0) elif human == HUMAN_INFECTED: return (255, 0, 0) elif human == HUMAN_IMMUNE: return (0, 255, 0) @staticmethod def drawmosq(mosq): _, hungry, infected = mosq if hungry and infected: return (255, 61, 0) elif hungry: # and not infected return 191, 128, 0 elif infected: # and not hungry return (127, 0, 0) else: # neither return (0, 0, 223) def printlegend(self): print('Humans (large blocks): ', end='') print('\033[38;2;%i;%i;%imDead\033[0m' % self.drawhuman(HUMAN_DEAD), end=', ') print('\033[38;2;%i;%i;%imHealthy\033[0m' % self.drawhuman(HUMAN_HEALTHY), end=', ') print('\033[38;2;%i;%i;%imInfected\033[0m' % self.drawhuman(HUMAN_INFECTED), end=', ') print('\033[38;2;%i;%i;%imImmune\033[0m' % self.drawhuman(HUMAN_IMMUNE)) print('Mosquitos (small blocks): ', end='') print('\033[38;2;%i;%i;%imHungry and Infected\033[0m' % self.drawmosq(((0, 0), True, True)), end=', ') print('\033[38;2;%i;%i;%imHungry\033[0m' % self.drawmosq(((0, 0), True, False)), end=', ') print('\033[38;2;%i;%i;%imInfected\033[0m' % self.drawmosq(((0, 0), False, True)), end=', ') print('\033[38;2;%i;%i;%imNeither\033[0m' % self.drawmosq(((0, 0), False, False))) print('Mosquito nets: /\\') def print(self): data = [[self.drawhuman(human) for human in row] for row in self.humans] # Draw the mosquitos mosqs = [[None] * self.width for _ in range(self.height)] for mosq in self.mosquitos: x, y = mosq[0] mosqs[y][x] = self.drawmosq(mosq) print('\033[2J\033[0;0H' + '\033[0m\n'.join(''.join([ '\033[48;2;%i;%i;%im' % data[row][x] + ('\033[38;2;%i;%i;%im\u2590\u258c' % mosqs[row][x] if mosqs[row][x] else '\033[38;2;255;255;255m/\\' if self.mosqnets[row][x] else ' ') for x in range(self.width)]) for row in range(0, self.height)) + '\033[0m') self.printlegend() def gui(self, printsteps=1): try: fps = 15 while True: prevtime = time.time() for _ in range(printsteps): self.step() self.print() time.sleep(max(0, (1/fps) - (time.time()-prevtime))) except KeyboardInterrupt: return def frange(start, end, steps): stepsize = (end - start) / (steps-1) for n in range(steps): yield start + stepsize * n def statesvstime(tmax=40000): model = Model(64, 64) tr = list(range(tmax)) results = [None]*tmax for t in tr: print(t/tmax, end='\r') model.step() results[t] = model.getstates() humanhealthy, humaninfected, humanimmune, mosqinfected, mosqhungry = zip(*results) pyplot.plot(tr, humanhealthy, label='Healthy humans') pyplot.plot(tr, humaninfected, label='Infected humans') pyplot.plot(tr, humanimmune, label='Immune humans') pyplot.plot(tr, mosqinfected, label='Infected mosquitos') pyplot.plot(tr, mosqhungry, label='Hungry Mosquitos') pyplot.legend() pyplot.xlabel('time (steps)') pyplot.ylabel('amount') pyplot.show() def netvsimmune(tmin=10000, denscnt=200): dr = list(frange(0, 1, denscnt)) results = [None]*denscnt for i, dens in enumerate(dr): print(dens, end='\r') model = Model(16, 16, mosqnetdens=dens) for t in range(tmin): model.step() results[i] = sum(1 for row in model.humans for human in row if human == HUMAN_IMMUNE) pyplot.plot(dr, results, label='Immunity') pyplot.legend() pyplot.xlabel('Mosquito net density') pyplot.ylabel('Immune humans') pyplot.show() def main(): model = Model() model.gui() statesvstime() netvsimmune() if __name__ == '__main__': random.seed() main()