[Share] Bot Swarm

Just a little project I made while testing AI that I thought was fun to play around with and decided to share. :slight_smile:

https://www.youtube.com/watch?v=FezouyXIpjo


--# Bot
Bot = class()

function Bot:init()
    self.pos = vec2(WIDTH / 4, HEIGHT / 4)
    self.target = target
    self.size = 50
end

function Bot:draw()
    local mot = (self.target - self.pos):normalize() * 200 * DeltaTime
    local angle = math.atan2(mot.y, mot.x)
    local next = self.pos + mot
    local move = true
    local i = math.random() * 45 - 22.5
    while i <= 180 do
        for k, v in ipairs(bots) do
            if v ~= self then
                if v.pos:dist(next) < (v.size + self.size) / 2 and v.pos:dist(v.target) < self.pos:dist(self.target) then
                    next = self.pos + vec2(math.cos(angle + math.rad(i)), math.sin(angle + math.rad(i))) * 200 * DeltaTime
                end
            end
        end
        for k, v in ipairs(obstacles) do
            if math.abs(v.x - self.pos.x) < (v.size + self.size) / 2 and math.abs(v.y - self.pos.y) < (v.size + self.size) / 2 then
                next = self.pos + vec2(math.cos(angle + math.rad(i)), math.sin(angle + math.rad(i))) * 200 * DeltaTime
            end
        end
        i = -i
        local sign = i / math.abs(i)
        if sign ~= 1 and sign ~= -1 then
            sign = 1
        end
        if sign == 1 then
            i = i + sign * 22.5
        end
    end
    
    if move then
        self.pos = next
    end
    
    noStroke()
    fill(0)
    
    ellipse(self.pos.x, self.pos.y, self.size)
end

--# Main
-- Bot Swarm

function setup()
    tool = "target"
    parameter.watch("tool")
    parameter.action("Find Tool", function()
        tool = "find"
    end)
    parameter.action("Target Tool", function()
        tool = "target"
    end)
    parameter.action("Obstacle Tool", function()
        tool = "obstacle"
    end)
    parameter.action("Army Tool", function()
        tool = "army"
    end)
    parameter.boolean("Autospawn", true)
    parameter.action("Clear Bots", function()
        bots = {}
    end)
    parameter.action("Clear Obstacles", function()
        obstacles = {}
    end)
    parameter.action("Clear All", function()
        bots = {}
        obstacles = {}
    end)
    target = vec2(WIDTH / 1.5, HEIGHT / 1.5)
    bots = {}
    loop()
    obstacles = {}
    new = image(WIDTH, HEIGHT)
    old = image(WIDTH, HEIGHT)
end

function loop()
    if Autospawn then
        table.insert(bots, Bot())
    end
    tween.delay(1, loop)
end

function draw()
    rectMode(CENTER)
    
    background(255)
    
    noStroke()

    setContext(old, false)
    background(0, 0)
    sprite(new, old.width / 2, old.height / 2, old.width, old.height)
    setContext()
    setContext(new, false)
    background(0, 0)
    for k, v in ipairs(obstacles) do
        rect(v.x, v.y, v.size, v.size)
    end
    
    for k, v in ipairs(bots) do
        v:draw()
    end
    setContext()
    noTint()
    sprite(new, WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT)
    tint(255, 127)
    sprite(old, WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT)
    
    collectgarbage()
end

function touched(touch)
    if tool == "find" then
        local closest = 1
        local tvec = vec2(touch.x, touch.y)
        for k, v in ipairs(bots) do
            if v.pos:dist(tvec) < bots[closest].pos:dist(tvec) then
                closest = k
            end
        end
        local null = {pos = vec2(0, 0)}
        local cbot = bots[closest] or null
        if cbot == null then
            closest = 0
        end
        print("Closest is bot #" .. closest)
        print("X: " .. cbot.pos.x .. "\
Y: " .. cbot.pos.y)
    elseif tool == "target" then
        target = vec2(touch.x, touch.y)
        for k, v in ipairs(bots) do
            v.target = target
        end
    elseif tool == "obstacle" then
        if touch.state == BEGAN then
            table.insert(obstacles, {x = touch.x, y = touch.y, size = math.random(50, 150)})
        end
    elseif tool == "army" then
        local gridx = 10
        local gridy = 10
        if touch.state == BEGAN or math.floor(touch.x / WIDTH * gridx + 0.5) ~= math.floor(touch.prevX / WIDTH * gridx + 0.5) or math.floor(touch.y / HEIGHT * gridy + 0.5) ~= math.floor(touch.prevY / HEIGHT * gridy + 0.5) then
            table.insert(bots, Bot())
            bots[#bots].target = vec2(math.floor(touch.x / WIDTH * gridx + 0.5) / gridx * WIDTH, math.floor(touch.y / HEIGHT * gridy + 0.5) / gridy * HEIGHT)
        end
    end
end


@SkyTheCoder That reminds me of a swarm program I wrote for the 50 line challenge.


displayMode(FULLSCREEN)
function setup()
    size,ob=25,0
    objects1, objects2, objects3, objects4 = {},{},{},{}
    for z=1,100 do
        a=physics.body(CIRCLE,size)
        a.x=math.random(WIDTH)
        a.y=math.random(HEIGHT)
        a.gravityScale=0
        ob=(ob+1)%4
        if ob==0 then table.insert(objects1,a)
        elseif ob==1 then table.insert(objects2,a)
        elseif ob==2 then table.insert(objects3,a)
        elseif ob==3 then table.insert(objects4,a) end
    end    
    target1 = vec2(WIDTH/2, HEIGHT-150)
    target2 = vec2(WIDTH/2, 150)
    target3 = vec2(150,HEIGHT/2)
    target4 = vec2(WIDTH-150,HEIGHT/2)
end
function draw()
    background(142, 88, 38, 255)   fill(255)
    text("tap screen to swap groups.",WIDTH/2,HEIGHT/2)
    drawMob(objects1,target1,"Planet Cute:Character Boy")
    drawMob(objects2,target2,"Planet Cute:Character Cat Girl")
    drawMob(objects3,target3,"Planet Cute:Character Horn Girl")
    drawMob(objects4,target4,"Planet Cute:Character Pink Girl")
end
function drawMob(obj,tar,spr)
    for i,v in ipairs(obj) do
        move = tar - vec2(v.x, v.y)   
        if move:len() > 10 then move = move:normalize() * 10 end
        v.x = v.x + move.x
        v.y = v.y + move.y
        sprite(spr, v.x, v.y, 20)
    end
end
function touched(touch)
    if touch.state==BEGAN then
        target1,target2=target2,target1
        target3,target4=target4,target3
    end
end

Nice game