Langton's Ant

I would like to code Langton’s Ant in Codea [ http://en.wikipedia.org/wiki/Langton’s_ant ], and it would seem like the easiest way to do this would be to use retained backing and some getPixelColor function. It seems like Codea lacks the latter, is there any chance it could be added?

I’d say you could do it just by drawing to an image - the image class has both get() and set() methods.

Ah, thank you. That should work :slight_smile:

I couldn’t help but have a go at this myself. I’ve got a direction wrong somewhere though, as my resulting image is rotated 90° from the image on Wikipedia!

function setup()
    -- set up parameters and watch
    Steps=0
    watch("Steps")
    parameter("Delay",0,1.0,0.1)
    parameter("StepsPerDraw",1,1000,1)
    
    -- create our image to work on
    noSmooth()
    img = image(WIDTH/4,HEIGHT/4)
    setContext(img)
    fill(255, 255, 255, 255)
    noStroke()
    rect(0,0,img.width,img.height)
    setContext()
    
    -- inital ant position and direction
    antpos = vec2(img.width/2, img.height/2)
    dir = vec2(0,1)
end

-- move the ant on
function step()
    if not onscreen() then
        return
    end
    
    -- find the current colour
    local r,g,b,a = img:get(antpos.x, antpos.y)
    
    if r == 255 then
        -- if white left
        img:set(antpos.x, antpos.y, color(0,0,0,255))
    
        if dir.x == 1 then
            dir = vec2(0,-1)
        elseif dir.x == -1 then
            dir = vec2(0,1)
        elseif dir.y == 1 then
            dir = vec2(1,0)
        elseif dir.y == -1 then
            dir = vec2(-1,0)
        end
    else
        -- if black right
        img:set(antpos.x, antpos.y, color(255,255,255,255))
        
        if dir.x == 1 then
            dir = vec2(0,1)
        elseif dir.x == -1 then
            dir = vec2(0,-1)
        elseif dir.y == 1 then
            dir = vec2(-1,0)
        elseif dir.y == -1 then
            dir = vec2(1,0)
        end
    end
    
    antpos = antpos + dir
    Steps = Steps + 1
end

counter = 0

function draw()
    counter = counter + DeltaTime
    if counter > Delay then
        for i = 1,StepsPerDraw do
            step()
        end
        counter = 0
    end
    
    spriteMode(CORNER)
    sprite(img, 0, 0, WIDTH, HEIGHT)
end

function onscreen()
    if antpos.x > 0 and antpos.x < img.width and
       antpos.y > 0 and antpos.y < img.height then
        return true
    else
        return false
    end
end

I believe this may help you:

http://twolivesleft.com/Codea/Reference/#detail/index/vec2.rotate

http://twolivesleft.com/Codea/Reference/#detail/index/vec2.rotate90

Im pursuing an OO model, so I can change the ant’s rules, add multiple ants, etc.

EDIT: This is what the code looks like so far. Ants will ignore any colors they do not recognize.

Main: http://pastebin.com/jiAwXbTy

Ant Class: http://pastebin.com/1YL6J1tx

Something interesting happens if you play with default settings, in portrait mode, on an iPad 1!

Couldn’t resist. Here’s a mesh implementation.



function setup()
    --RANDOM = true
    landscape = mesh()
    grid = {}
    grain = 10
    w = math.floor(WIDTH/grain)
    h = math.floor(HEIGHT/grain)
    local i
    for x=1,w do
        grid[x] = {}
        for y=1,h do
            i = landscape:addRect(
                (x-.5)*grain,
                (y-.5)*grain,
                grain,
                grain
                )
            grid[x][y] = i
            if RANDOM and math.random(0,1) == 0 then
                landscape:setRectColor(i,color(255,255,255))
            else
                landscape:setRectColor(i,color(0,0,0))
            end
            if i%500 == 499 then
                landscape:addRect(0,0,0,0)
            end
        end
    end
    ant = mesh()
    ant.texture = "Tyrian Remastered:Space Bug Right"
    local i =ant:addRect(0,0,grain,grain)
    ant:setRectTex(i,0,0,1,1)
    
    x = math.floor(w/2)
    y = math.floor(h/2)
    d = 0
    rate = .1
    parameter("rate",0,1,rate)
    UpdateTime = 0
    iterations = 0
    watch("iterations")
    watch("math.floor(1/DeltaTime)")
end

function draw()
    background(40, 40, 50)

    landscape:draw()
    translate(grain*(x-.5),grain*(y-.5))
    rotate(d*90)
    ant:draw()
    if ElapsedTime - UpdateTime > rate then
        UpdateTime = ElapsedTime
        iterations = iterations + 1
        local c = landscape:color(6*grid[x][y])
        if c.r == 255 then
            d = (d-1)%4
            landscape:setRectColor(grid[x][y],color(0,0,0))
        else
            d = (d+1)%4
            landscape:setRectColor(grid[x][y],color(255,255,255))
        end
        if d%2 == 0 then
            x = x - d + 1
        else
            y = y - d + 2
        end
        if x == w+1 then
            x = 1
        end
        if x == 0 then
            x = w
        end
        if y == h+1 then
            y = 1
        end
        if y == 0 then
            y = h
        end
    end
end

Put it on a torus so I didn’t have to worry about the edges (try a Klein bottle next!).

@Simeon I got odd behaviour on rendering the 500th rectangles (and 1000th etc). Try commenting out the lines if i %500 ... and set RANDOM to true (so that the behaviour is obvious).

Very nice!

A couple of suggestions:

  • use noSmooth() before the sprite command
  • draw the board on the neighbouring screens as well (so you can pan and zoom on the corners of the screen as well)
    for i=-1,1 do
        for j=-1,1 do
            sprite(board, i*WIDTH,j*HEIGHT)
        end
    end