love-cellular-automata/ants/main.lua

159 lines
3.3 KiB
Lua

-- based on
-- https://healeycodes.com/virtual-ants
-- https://github.com/healeYcodes/virtual-ants/blob/main/index.html
require("util")
local function round2even(num)
return 2 * math.floor(num / 2)
end
local function rotate(direction, clockwise)
local match
if clockwise then
match = {
u = "r",
r = "d",
d = "l",
l = "u",
}
else
match = {
u = "l",
l = "d",
d = "r",
r = "u"
}
end
return match[direction]
end
local function initColors(rules)
rules = rules:lower()
local colorbrewer = require("colorbrewer")
-- set a specific random seed to avoid picking the same palette every time
math.randomseed(os.time())
local scheme = colorbrewer.random()
local variant = scheme[#rules]
COLORS = {}
for i = 1, #rules do
COLORS[i] = {
clockwise = rules:sub(i, i) == "r",
rgba = variant[i]
}
end
end
local function initScreen()
local width, height = love.graphics.getDimensions()
WIDTH, HEIGHT = round2even(width), round2even(height)
CANVAS = love.graphics.newCanvas(WIDTH, HEIGHT)
CANVAS:renderTo(function()
local color = COLORS[1].rgba
color[4] = 0.9
love.graphics.setColor(color)
love.graphics.rectangle("fill", 0, 0, WIDTH, HEIGHT)
end)
end
local function initAnt()
ANT = {
x = (WIDTH / 2) - ((WIDTH / 2) % UNITS),
y = (HEIGHT / 2) - ((HEIGHT / 2) % UNITS),
direction = "u",
}
end
local function initGrid()
GRID = {}
GRID.mt = {}
setmetatable(GRID, GRID.mt)
GRID.mt.__index = function(table, key)
table[key] = {}
table[key].mt = {}
setmetatable(table[key], table[key].mt)
table[key].mt.__index = function(innerTable, innerKey)
innerTable[innerKey] = 1
return innerTable[innerKey]
end
return table[key]
end
end
function love.load(args)
local rules = args[1] or "RL"
UNITS = args[2] or 8
STEPS_PER_FRAME = args[3] or 5
initColors(rules)
initScreen()
initAnt()
initGrid()
end
local function color(x, y)
return GRID[x][y]
end
local function current()
return color(ANT.x, ANT.y)
end
local function setCurrent(index)
GRID[ANT.x][ANT.y] = index
end
local function updateCurrent()
local val = current()
if val == #COLORS then
val = 1
else
val = val + 1
end
setCurrent(val)
end
local function nextColor()
local c = COLORS[current()]
updateCurrent()
return c
end
function love.update()
require("lurker").update()
for _ = 1, STEPS_PER_FRAME do
local c = nextColor()
ANT.direction = rotate(ANT.direction, c.clockwise)
CANVAS:renderTo(function()
love.graphics.setBlendMode("alpha")
love.graphics.setColor(c.rgba)
love.graphics.rectangle("fill", ANT.x, ANT.y, UNITS, UNITS)
end)
if ANT.direction == "u" then
ANT.y = ANT.y - UNITS
elseif ANT.direction == "r" then
ANT.x = ANT.x + UNITS
elseif ANT.direction == "d" then
ANT.y = ANT.y + UNITS
else
ANT.x = ANT.x - UNITS
end
if ANT.x >= WIDTH then
ANT.x = 0
elseif ANT.x < 0 then
ANT.x = (WIDTH - 1) - ((WIDTH - 1) % UNITS)
end
if ANT.y >= HEIGHT then
ANT.y = 0
elseif ANT.y < 0 then
ANT.y = (HEIGHT - 1) - ((HEIGHT - 1) % UNITS)
end
end
end
function love.draw()
love.graphics.setBlendMode("alpha", "premultiplied")
love.graphics.setColor(COLORS[1].rgba)
love.graphics.draw(CANVAS, 0, 0)
end