How a Virus spreads

I took one of my older programs and modified it to show how a virus could spread. When the program starts, there are 350 white balls roaming around the screen. Once you tap the screen, you infect one of the balls (color yellow). That ball will interact with the other balls but nothing will happen. After it’s been infected for 6 days (1 day = 3 seconds), that ball becomes contagious (color red). Any white ball that comes in contact with the red ball becomes infected (yellow). That ball then goes thru the 6 day stretch before it becomes contagious (red). Each red ball will infect other balls for 15 days after which time it will be cured (color green). It takes about 50 some days for all balls to go from white to green. Of course, if we had millions of balls and a larger area, it would take a lot longer for this to run. I have counts in the middle of the screen to show the status of the balls.

Remember, tap the screen to start the infection.

displayMode(FULLSCREEN) 

function setup()
    colTab={color(255),color(255,255,0),color(255,0,0),color(0,255,0)}
    dti=5
    dtc=20
    physics.continuous=true
    speed=50
    line1 = physics.body(EDGE,vec2(5,5),vec2(10,HEIGHT-5))
    line2 = physics.body(EDGE,vec2(WIDTH-5,5),vec2(WIDTH-5,HEIGHT-5))
    line3 = physics.body(EDGE,vec2(5,5),vec2(WIDTH-5,5))
    line4 = physics.body(EDGE,vec2(5,HEIGHT-5),vec2(WIDTH-5,HEIGHT-5)) 
    val=0
    days=0
    cnt=0
    limit=180  
    nbr=350    
    tab={}                    -- table for balls    
    for x=1,nbr do             -- start with 20 balls     
        create()
    end    
end

function draw()
    background(50, 50, 50)    
    normal,infected,contagious,cured=0,0,0,0
    strokeWidth(0)
    for a,b in pairs(tab) do
        if b.inf>0 then
            if days-b.inf>=dti and b.col==2 then
                b.col=3
            elseif days-b.inf>=dtc and b.col==3 then
                b.col=4
            end
        end
        fill(colTab[b.col])
        ellipse(b.x,b.y,12)
        if b.col==1 then
            normal=normal+1
        elseif b.col==2 then
            infected=infected+1
        elseif b.col==3 then
            contagious=contagious+1
        elseif b.col==4 then
            cured=cured+1
        end
    end    
    stroke(255) 
    strokeWidth(5)
    line(5,5,5,HEIGHT-5)  
    line(WIDTH-5,5,WIDTH-5,HEIGHT-5) 
    line(5,5,WIDTH-5,5)
    line(5,HEIGHT-5,WIDTH-5,HEIGHT-5)
    cnt=cnt+val
    if cnt>limit then
        cnt=0
        days=days+1
    end
    fill(255, 0, 217)
    text("day "..days,WIDTH/2,HEIGHT/2+200)
    fill(255)
    text("normal "..normal,WIDTH/2,HEIGHT/2+150)
    fill(255,255,0)
    text("infected "..infected,WIDTH/2,HEIGHT/2+100)
    fill(255,0,0)
    text("contagious "..contagious,WIDTH/2,HEIGHT/2+50)
    fill(0,255,0)
    text("cured "..cured,WIDTH/2,HEIGHT/2)
end

function touched(t)
    if t.state==BEGAN and infected==0 then
        tab[1].col=2
        tab[1].inf=1
        val=1
    end
end

function collide(contact)
    if contact.state==BEGAN then
        if contact.bodyA.col==3 then
            if contact.bodyB.col==1 then
                contact.bodyB.inf=days
                contact.bodyB.col=2
                infected=infected+1
                normal=normal-1
            end
        elseif contact.bodyB.col==3 then
            if contact.bodyA.col==1 then
                contact.bodyA.inf=days
                contact.bodyA.col=2
                infected=infected+1
                normal=normal-1
            end
        end
    end
end

function create()
    local a=#tab+1
    tab[a]=physics.body(CIRCLE,6)
    tab[a].mass=2
    tab[a].friction=0
    tab[a].x=math.random(30,WIDTH-30)
    tab[a].y=math.random(30,HEIGHT-30)
    tab[a].gravityScale=0
    tab[a].restitution=1
    tab[a].sleepingAllowed=false
    a1=math.random(-1,1)*speed
    a2=math.random(-1,1)*speed
    tab[a].linearVelocity=vec2(a1,a2)
    tab[a].inf=0
    tab[a].col=1
end

@dave1707 - interesting, funnily enough I’ve been looking for game of Life to do something similar. Overlaying a graph of the numbers of the 4 inhabitants of the game would show progress. Saved the project as Corona.

@dave1707 cool! Would be nice to add a plot of the time dependence for the various categories. Then send it to Boris Johnson, so he finally takes it seriously!

Neat … why does it slow to a stop? Looks like there’s no friction and perfect collisions …

@RonJeffries I don’t know the exact answer why it’s slows, but it might be based on the angle it hits the side walls. If it’s straight on, it bounces back ok. If it’s a glazing hit, it doesn’t bounce back enough. I’ll have to make a copy and strip out a lot of stuff and display velocities of the ball when it hits at different angles.

physics, can’t live with it, can’t live without it.

@RonJeffries I tried different angles and speeds with one ball bouncing off the wall. The smaller the angle, the higher the velocity had to be to get a good bounce. There might be a speed that can be calculated based on the angles where you get the bounce limit. I haven’t found a setting that fixes everything. I tried linearDamping to get the bounce to work, but that just caused the balls to speed up out of control.

I think the balls slow down because they are “sheltering in place”. What an interesting program.

@Scotty I like your answer, “sheltering in place”. But one thing it shows is that even though they shelter in place, if one of them is infected, eventually they all get infected. I ran this one time where one of the balls didn’t get infected for a very long time. Eventually it ran into an infected ball because just about every ball around it was infected. But it shows that as long as you can avoid the ones infected, then your OK.

Added code to graph the different categories as the program runs.

displayMode(FULLSCREEN) 

function setup()
    nbr=350    
    nTab,iTab,coTab,cuTab={nbr},{0},{0},{0}
    colTab={color(255),color(255,255,0),color(255,0,0),color(0,255,0)}
    dti=5
    dtc=20
    physics.continuous=true
    speed=50
    line1 = physics.body(EDGE,vec2(5,5),vec2(10,HEIGHT-5))
    line2 = physics.body(EDGE,vec2(WIDTH-5,5),vec2(WIDTH-5,HEIGHT-5))
    line3 = physics.body(EDGE,vec2(5,5),vec2(WIDTH-5,5))
    line4 = physics.body(EDGE,vec2(5,HEIGHT-5),vec2(WIDTH-5,HEIGHT-5)) 
    val=0
    days=0
    cnt=0
    limit=180
    tab={}                    -- table for balls    
    for x=1,nbr do             
        create()
    end    
end

function draw()
    background(50, 50, 50)    
    stroke(255) 
    strokeWidth(5)
    line(5,5,5,HEIGHT-5)  
    line(WIDTH-5,5,WIDTH-5,HEIGHT-5) 
    line(5,5,WIDTH-5,5)
    line(5,HEIGHT-5,WIDTH-5,HEIGHT-5)
    
    normal,infected,contagious,cured=0,0,0,0
    strokeWidth(0)
    for a,b in pairs(tab) do
        if b.inf>0 then
            if days-b.inf>=dti and b.col==2 then
                b.col=3
            elseif days-b.inf>=dtc and b.col==3 then
                b.col=4
            end
        end
        fill(colTab[b.col])
        ellipse(b.x,b.y,12)
        if b.col==1 then
            normal=normal+1
        elseif b.col==2 then
            infected=infected+1
        elseif b.col==3 then
            contagious=contagious+1
        elseif b.col==4 then
            cured=cured+1
        end
    end 

    fill(255, 0, 217)
    text("day "..days,WIDTH/2,HEIGHT/2+200)
    fill(255)
    text("normal "..normal,WIDTH/2,HEIGHT/2+150)
    fill(255,255,0)
    text("infected "..infected,WIDTH/2,HEIGHT/2+100)
    fill(255,0,0)
    text("contagious "..contagious,WIDTH/2,HEIGHT/2+50)
    fill(0,255,0)
    text("cured "..cured,WIDTH/2,HEIGHT/2)
    
    if days<75 then
        cnt=cnt+val
        if cnt>limit then
            cnt=0
            days=days+1
        end
    end

    w=WIDTH/60
    nTab[days]=normal
    iTab[days]=infected
    coTab[days]=contagious
    cuTab[days]=cured 

    strokeWidth(3)
    stroke(colTab[1])
    for z=1,#nTab-1 do
        line((z-1)*w,nTab[z-1]*2+100,z*w,nTab[z]*2+100)            
    end
    stroke(colTab[2])
    for z=1,#iTab-1 do
        line((z-1)*w,iTab[z-1]*2+100,z*w,iTab[z]*2+100)            
    end
    stroke(colTab[3])
    for z=1,#coTab-1 do
        line((z-1)*w,coTab[z-1]*2+100,z*w,coTab[z]*2+100)            
    end
    stroke(colTab[4])
    for z=1,#cuTab-1 do
        line((z-1)*w,cuTab[z-1]*2+100,z*w,cuTab[z]*2+100)            
    end
end

function touched(t)
    if t.state==BEGAN then
        if infected==0 then
            tab[1].col=2
            tab[1].inf=1
            val=1
        end        
    end
end

function collide(contact)
    if contact.state==BEGAN then
        if contact.bodyA.col==3 then
            if contact.bodyB.col==1 then
                contact.bodyB.inf=days
                contact.bodyB.col=2
                infected=infected+1
                normal=normal-1
            end
        elseif contact.bodyB.col==3 then
            if contact.bodyA.col==1 then
                contact.bodyA.inf=days
                contact.bodyA.col=2
                infected=infected+1
                normal=normal-1
            end
        end
    end
end

function create()
    local a=#tab+1
    tab[a]=physics.body(CIRCLE,6)
    tab[a].mass=20
    tab[a].friction=0
    tab[a].x=math.random(30,WIDTH-30)
    tab[a].y=math.random(30,HEIGHT-30)
    tab[a].gravityScale=0
    tab[a].restitution=1
    tab[a].sleepingAllowed=false
    a1=math.random(-1,1)*speed
    a2=math.random(-1,1)*speed
    tab[a].linearVelocity=vec2(a1,a2)
    tab[a].inf=0
    tab[a].col=1
end

@dave1707 - just started trying to do this today but didn’t get far. Ran the project and it’s great - just misses one component at a level of about 3%. One thing to note - ran this on three occasions and initially followed the progress and exited. On the other two occasions I was interrupted and had to leave it running - on both occasions it crashed Codea. Possibly need an end point on this to stop progress.

@dave1707 - just started trying to do this today but didn’t get far. Ran the project and it’s great - just misses one component at a level of about 3%. Just one thing - I have run the project 4 times now. The first ran to completion one closed the project. The second and third I ran and was interrupted - left the project running and in each case Codea crashed. Ran it on the fourth time and completed, plotted beyond the screen up to 108 days.

Ran it a fifth time - my iPad has a case with a magnet in the cover which when sensed freezes the iPad. Closed my cover this time - project incomplete and Codea crashed.

@Simeon - the shutdown is a design feature of the iPad as there are sensors present in the iPad. I have only noticed this with Codea - are there routines/protocols available from Apple to cope with this?

@dave1707 good, now i can send it to Boris! Interesting, the curves come out quite Gaussian.

@Bri_G I modified the above code to stop updating the counts and tables at 75 days. That should stop the crash. How long did you let it run. The program still updates the physics information. I never thought anyone would let this run for any length of time.

@dave1707 - first time when I took the dog for a walk, second time when I had my tea (each about 20 mins) but the third time as soon as the graph plotting had reached the right hand edge of the screen. But - I don’t think it is necessarily the code but due to the iPad freezing running apps. I think there must be routines or protocols to be run/complied with to re-open apps in a frozen state.

@piinthesky I’ve run this several times and noticed that when the infected graph peaks, it’s close to when the normal and contagious graphs cross. Also, when the contagious graph peaks, it’s close to when the infected and cured graphs cross.

@dave1707 that’s a very interesting simulation. I like the differentiation between “contagious” and “infected”

i sure wonder why they grind to a halt on the sides

@RonJeffries As far as I can determine, once the speed gets below a certain value, the physics engine doesn’t work like it does at faster speeds. I’m not sure if I’m not setting everything right, or that’s just the way it is.

Yes … with restitution = 1, I’d expect perfect collisions. Maybe there’s some friction thing somewhere that we’re missing. I fiddled with settings on the EDGEs but no joy.