Ants (code update v2-v2.1)

For the moment they dont do much, and you cant interract. But they are fun to watch.
http://www.youtube.com/watch?v=VJ9PR34-Jxw
Here the code


--# Main
-- 0  fourmi
-- copyright JMV38 2013 - all rights reserved

displayMode(FULLSCREEN)

function setup()
    physics.gravity(0,0)
    smooth()
    world = World()
    local brown = color(120, 65, 30, 255)
    ants = Ants(200, brown )
    if FPS then fps=FPS() end
end

function draw()
    background(255, 255, 255, 255)
    world:draw()
    ants:draw()
    if fps then fps:draw() end
end


--# Ant
Ant = class()
-- copyright JMV38 2013 - all rights reserved

function Ant:init(ms,v0,r0,nbrStates,caller)
    -- utilities
    local rnd = math.random
    local deg = math.deg
    local rad = math.rad
    local cos = math.cos
    local sin = math.sin
    self.rnd = math.random
    self.deg = math.deg
    self.rad = math.rad
    self.cos = math.cos
    self.sin = math.sin
    -- object creation and links
    self.body = physics.body(CIRCLE,5)
    local body = self.body
    body.info = self
    body.type = DYNAMIC
    body.angle = rad(rnd(360))
    body.fixedRotation = true
    local v = vec2((rnd()-0.5)*2*r0,(rnd()-0.5)*2*r0)
    body.position = v + v0
    self.imgState = 1

    self.caller = caller
    self.coords = caller.coords
    self.ms = caller.ms

    self.pausePeriod = 3
    self.pauseDuration = 1
    self.pausing = true
    
    self.speed = 75
    self.changeDirPeriod = 0.5 
    
    self:pause()
    self:changeDir()

    -- init mesh information
    local sizeFactor = 0.9
    self.w = 26 *sizeFactor
    self.h = 21 *sizeFactor
    self.rect = self.ms:addRect(v.x,v.y,self.w,self.h,body.angle)     
    self.ms:setRectColor(self.rect,caller.color)
    self.ms:setRectTex(self.rect,0,0,1/nbrStates,1)

end

function Ant:changeImgState(di,ms,coords)
    if self.pausing then return  end
    local i = self.imgState
    i = (i -1+ di)%6 +1
    self.imgState = i
    ms:setRectTex(self.rect, unpack(coords[i]))
end

function Ant:changeDir()
    local v,speed
    local body = self.body
    body.angle = body.angle + self.rad(self.rnd()*180-90)
    if self.pausing then speed=0 else speed=self.speed end
    v = vec2(self.cos(body.angle),self.sin(body.angle)) * speed
    body.linearVelocity = v
    self.changeDirT0 = ElapsedTime + (self.rnd()+1)/2*self.changeDirPeriod
    if self.rect then self:changeImgState(3,self.ms,self.coords) end
end
function Ant:pause()
    local body = self.body
    self.pausing = not self.pausing
    if self.pausing then 
        body.linearVelocity = vec2(0,0)
        self.pauseT0 = ElapsedTime + (self.rnd()+1)/2*self.pauseDuration
    else 
        self:changeDir() 
        self.pauseT0 = ElapsedTime + (self.rnd()+1)/2*self.pausePeriod
    end
end

function Ant:changeSpeed(n)
    local rnd = math.random
    if rnd()<=DeltaTime*60/n then self.speed = 0 else self.speed = 50 end
end


function Ant:touched(touch)
    -- Codea does not automatically call this method
end


--# Ants
Ants = class()
-- copyright JMV38 2013 - all rights reserved

function Ants:init(n,c)
    self.ms = mesh()
    self.color = c

    local img,coords = self:textureGet()
    self.texture = img
    self.coords = coords
    self.ms.texture = self.texture
    local list = {}
    local v0 = vec2(WIDTH/2,HEIGHT/2)
    local nbrStates = #self.coords
    for i=1,n do list[i] = Ant(self.ms,v0,100,nbrStates,self) end
    self.list = list
end

function Ants:draw()
    self:redraw()
    self.ms:draw()
end

function Ants:redraw(debug)
    local rnd = math.random
    local deg = math.deg
    local rad = math.rad
    local cos = math.cos
    local sin = math.sin
    local ms = self.ms
    local coords = self.coords
    local body,v,i,pos,angle
    
    if not debug then
    -- actions to do every frame for all ants
    for _,ant in pairs(self.list) do 
        body = ant.body
        pos = body.position
        ms:setRect(ant.rect,pos.x,pos.y,ant.w,ant.h,body.angle)
        ant:changeImgState(1,ms,coords)
    end
    -- actions that can be skipped sometimes
    self:makeThemAlive()
    
    else -- special to built images
        local ant = self.list[1]
        local s = 10
        ms:setRect(ant.rect, WIDTH/2, HEIGHT/2, ant.w*s, ant.h*s, 0) 
        ant.body.linearVelocity = vec2(0)
        ant:setImgState("stop",ms,self.coords)
        return
    end

end

function Ants:makeThemAlive()
    local ant
    local list = self.list
    for i=1,#list do
        ant = list[i]
        if ant.changeDirT0 < ElapsedTime then ant:changeDir() end
        if ant.pauseT0 < ElapsedTime then ant:pause() end
    end
end

function Ants:textureGet()
    local img = {}
    local coords = {}
    local N = 6
    for i=1,N do img[i] = self:loadImg(i) end
    local w0 = img[1].width 
    local h0 = img[1].height
    local w = w0 * #img
    local h = h0
    local tex = image(w,h)
    setContext(tex)
        background(255, 255, 255, 255)
    setContext()
    for i =1,#img do
        local im = img[i]
        local x1 =  (w0)*(i-1)
        coords[i] = {(x1)/w, 0,w0/w, 1}
        for x=1,im.width do for y=1,im.height do
            tex:set(x+x1,y,color(im:get(x,y)))
        end end
    end
    return tex,coords
end

function Ants:loadImg(phase)
    local w,h = 31,25
    self.img = image(31,25)
    local img = self.img
    setContext(img)
        pushMatrix() pushStyle()
        background(0, 0, 0, 0)
        fill(255, 255, 255, 255)
        stroke(255, 255, 255, 255)
        self:bodyImg()
        self:frontLeg(phase,"left")
        self:frontLeg(phase+3,"right")
        self:midLeg(phase+1,"left")
        self:midLeg(phase+4,"right")
        self:backLeg(phase+2,"left")
        self:backLeg(phase+5,"right")
        popMatrix() popStyle()
    setContext()
    return img
end

function Ants:stopImg()
    local w,h = 31,25
    self.img = image(w,h)
    local img = self.img
    setContext(img)
        pushMatrix() pushStyle()
        background(0, 0, 0, 0)
        fill(0, 0, 0, 255)
        stroke(0, 0, 0, 255)
        self:bodyImg()
        self:frontLeg(0,"left")
        self:frontLeg(0,"right")
        self:midLeg(3,"left")
        self:midLeg(3,"right")
        self:backLeg(5.99,"left")
        self:backLeg(5.99,"right")
        popMatrix() popStyle()
    setContext()
    return img
end


function Ants:moveLeg(phase,side,ref,front,back)
    -- phase: 0 to 5.99, 0 = front and 5.99 = back
    local q = (phase - math.floor(phase/6)*6)/6
    local p = 1-q
    local sgn
    if side =="left" then sgn=1 else sgn=-1 end
    local x0,y0 = unpack(ref)
    local x1a,y1a,x2a,y2a = unpack(front)   -- front position
    local x1b,y1b,x2b,y2b = unpack(back )   -- back position
    local x1,y1 = x1a*p + x1b*q , y1a*p + y1b*q
    local x2,y2 = x2a*p + x2b*q , y2a*p + y2b*q
    self:leg(x0,y0,x1,y1*sgn,x2,y2*sgn)
end

function Ants:frontLeg(phase,side)
    local x0,y0 = 17,(self.img.height + 0)/2
    self:moveLeg(phase,side,{x0,y0},{0,0,5,7},{0,0,3,7})
    self:moveLeg(phase,side,{x0,y0},{4,6,11,12},{3,6,0,12})
end

function Ants:midLeg(phase,side)
    local x0,y0 = 14,(self.img.height + 0)/2
    self:moveLeg(phase,side,{x0,y0},{0,0,3,6},{0,0,1,6})
    self:moveLeg(phase,side,{x0,y0},{3,5,3,9},{1,5,-1,9})
    self:moveLeg(phase,side,{x0,y0},{3,8,3,14},{-1,8,-3,12}) 
end

function Ants:backLeg(phase,side)
    local x0,y0 = 13,(self.img.height + 0)/2
    self:moveLeg(phase,side,{x0,y0},{0,0,0,6},{0,0,-3,6})
    self:moveLeg(phase,side,{x0,y0},{1,6,-4,6},{-2,6,-9,6})
    self:moveLeg(phase,side,{x0,y0},{-3,6,-4,13},{-7,6,-13,9})
end

function Ants:bodyImg()
    local y0 = (self.img.height + 1)/2
        strokeWidth(0)
        ellipse(6,y0,10,8)    -- abdomen
        ellipse(14,y0,12,4)    -- centre
        ellipse(21,y0,6,6)    -- tete
        self:legs(22,y0,0,0,3,5)    -- antenne
        self:legs(22,y0,3,5,9,4)
end
function Ants:leg(x0,y0,x1,y1,x2,y2)
        strokeWidth(3)    
        lineCapMode(SQUARE)
        line(x0+x1,y0+y1,x0+x2,y0+y2)
end
function Ants:legs(x0,y0,x1,y1,x2,y2)
    self:leg(x0,y0,x1,y1,x2,y2)
    self:leg(x0,y0,x1,-y1,x2,-y2)
end

function Ants:touched(touch)
    -- Codea does not automatically call this method
end


--# World
World = class()
-- copyright JMV38 2013 - all rights reserved

function World:init()
    -- visibility
    local ms = mesh()
    local i = ms:addRect(75,HEIGHT-15,150,30)
    local c = color(127, 127, 127, 255)
    ms:setRectColor(i,c)
    self.ms = ms
    -- edges
    edge = {}
    edge[1] = physics.body(EDGE,vec2(0,0),vec2(WIDTH,0))
    edge[2] = physics.body(EDGE,vec2(0,0),vec2(0,HEIGHT))
    edge[3] = physics.body(EDGE,vec2(WIDTH,0),vec2(WIDTH,HEIGHT))
    edge[4] = physics.body(EDGE,vec2(0,HEIGHT),vec2(WIDTH,HEIGHT))
end

function World:draw()
    -- Codea does not automatically call this method
    self.ms:draw()
end

function World:touched(touch)
    -- Codea does not automatically call this method
end

. @Jmv38 so funny, indeed as you say “they are fun to watch.” Merci for the code …

Very cool.

Now… A little emergent behavior? Give each ant the ability to leave a trail, find food, etc.?

Thanks you 2. @mark: yes that’s the idea. But i need some encouragements to proceed, so i published this first version. :wink:

A Queen ant to create new ones?

Oooh! Red vs black ants! War!

code update i have fixed and added a lot of things:

  • Ants deposit pheromons to come back home (blue) and to food (red). You can see pheromons by clicking the ‘phero’ button.
  • there are brown obstacles, and food is green. You can move them.
    Pheromons generate ‘emergent behaviors’. There are still some bugs (i mean code bugs :wink: ) and a lot is still to be added. Hope you like it. Here is the video:
    http://www.youtube.com/watch?v=eKUfXJSMQ3c
    Here is the code:
    https://gist.github.com/4542577

UPDATE 2.1 i noticed that the ants had a tendancy to get stuck onto obstacles, so i made a small change, now its ok.

Impressive and fun!

I think they look very real.

Thanks for sharing.

You’re one step closer to Sandkings! (Those who’ve never read the original George R. R. Martin short story are missing out – go read).

Thanks ! There is much more i thought about adding, but i’ll wait to see if enough people are interested. Try with 100 ants, they are more efficient.

@Jmw38 - This is fun, and somehow very captivating heh.

Thanks @Xavier. I think you are on codea beta 1.5(14) so the ants dont look anymore as they should. If so get the v2.2 from http://jmv38.comze.com/CODEA/server.php
That will be better. People who are on 1.4 should not load this one, the legs of the ants wont be visible for them.

Thanks, Xavier, the latest version on your web page is very nice. You seem to have considerable experience in artificial life programming!

Thanks @Ignatz. It is true that i spend so much time programming on the ipad that sometimes my own life feels artificial… Hence the experience… :wink:

Thanks for posting this up, Jmv. Its really awesome!