local Vector
Vector = function(x, y)
return setmetatable({
x = x,
y = y,
}, {
__add = function(self, other)
return Vector(self.x + other.x, self.y + other.y)
end,
__tostring = function(self)
return string.format("%d, %d", self.x, self.y)
end,
})
end
local dirFromString = function(dir)
if dir == "left" then
return Vector(-1, 0)
elseif dir == "right" then
return Vector(1, 0)
elseif dir == "up" then
return Vector(0, -1)
elseif dir == "down" then
return Vector(0, 1)
end
end
local Cell
local commands = {
move = function(self, pos, arena, direction)
if arena:hasCell(pos + dirFromString(direction)) then
local from = arena:getCell(pos)
arena:add(pos + dirFromString(direction), self, function()
arena:add(pos, from)
end)
arena:remove(pos, self)
arena:add(pos, Cell{owner=self.owner})
end
return 4
end,
split = function(self, pos, arena, direction, fun, energy)
if arena:hasCell(pos + dirFromString(direction)) then
energy = energy or 0.5
local oldEnergy = self.energy
local cell = Cell{owner=self.owner, core=fun, energy=self.energy * energy}
arena:add(pos + dirFromString(direction), cell, function()
self.energy = oldEnergy
end)
self.energy = self.energy * (1 - energy)
end
return 4
end,
attack = function(self, pos, arena, direction)
if arena:hasCell(pos + dirFromString(direction)) then
arena:add(pos + dirFromString(direction), Cell{owner=self.owner})
end
return 4
end,
send = function(_, pos, arena, data, direction)
if arena:hasCell(pos) then
table.insert(arena:getCell(pos + dirFromString(direction)).data, data)
end
return 1
end,
inspect = function(_, pos, arena, direction)
if not arena:hasCell(pos + dirFromString(direction)) then
return 1
end
return 1, arena:getCell(pos + dirFromString(direction)).data
end,
charge = function(self)
self.energy = self.energy + 4
return 2, self.energy
end,
wait = function(_, amount)
return amount or 1
end,
}
Cell = function(t)
t = t or {}
local coreState = {
data = {}
}
for command, fun in pairs(commands) do
coreState[command] = function(...)
return coroutine.yield(fun, {...})
end
end
local cell = {
waitTime = 0,
energy = t.energy or 1,
owner = t.owner,
core = t.core and coroutine.create(t.core),
data = t.core and coreState.data,
returnValue = coreState,
}
return cell
end
local IntermediateArena = function(arena)
local intermediate = {cells = {}}
function intermediate:resolve(pos)
local result
for _, cell in ipairs(self.cells[pos.x][pos.y].cells) do
if not result then
result = cell
elseif cell.core and result.core then
return nil
elseif cell.core and not result.core then
result = cell
end
end
return result or self:getCell(pos)
end
function intermediate:undo(pos)
local cell = self.cells[pos.x][pos.y]
for _, action in ipairs(cell.actions) do
action()
end
cell.cells = {cell.original}
end
function intermediate:add(pos, cell, undo)
if undo then
table.insert(self.cells[pos.x][pos.y].actions, undo)
end
table.insert(self.cells[pos.x][pos.y].cells, cell)
end
function intermediate:remove(pos, cell)
local cells = self.cells[pos.x][pos.y].cells
for index, checkCell in ipairs(cells) do
if cell == checkCell then
table.remove(cells, index)
break
end
end
end
function intermediate:getCell(pos)
return self.cells[pos.x][pos.y].original
end
function intermediate:hasCell(pos)
return self.cells[pos.x] and self.cells[pos.x][pos.y]
end
for x, row in ipairs(arena.cells) do
intermediate.cells[x] = {}
for y, cell in ipairs(row) do
intermediate.cells[x][y] = {
original = cell,
cells = {cell},
actions = {}
}
end
end
return intermediate
end
local Arena
Arena = function(width, height)
local arena = {
cells = {}
}
for x = 1, width do
arena.cells[x] = {}
for y = 1, height do
arena.cells[x][y] = Cell()
end
end
function arena:getCell(pos)
return self.cells[pos.x][pos.y]
end
function arena:setCell(pos, cell)
self.cells[pos.x][pos.y] = cell
end
function arena:spawn(x, y, name, core)
self:setCell(Vector(x, y), Cell{core=core, owner=name})
end
function arena:simulate()
local intermediate = IntermediateArena(self)
for x, row in ipairs(self.cells) do
for y, cell in ipairs(row) do
if cell.core then
cell.waitTime = cell.waitTime - 1
if cell.waitTime <= 0 then
if coroutine.status(cell.core) == "dead" then
else
local _, command, args = coroutine.resume(cell.core, cell.returnValue)
local valid = false
for _, fun in pairs(commands) do
if fun == command then
valid = true
end
end
if valid then
cell.waitTime, cell.returnValue = command(cell, Vector(x, y), intermediate, table.unpack(args))
cell.waitTime = cell.waitTime * (1 - cell.energy) * 10
end
end
end
end
end
end
local hasConflicts = true
while hasConflicts do
hasConflicts = false
for x, row in ipairs(self.cells) do
for y, _ in ipairs(row) do
if not intermediate:resolve(Vector(x, y)) then
hasConflicts = true
intermediate:undo(Vector(x, y))
end
end
end
end
for x, row in ipairs(self.cells) do
for y, _ in ipairs(row) do
local pos = Vector(x, y)
self:setCell(pos, intermediate:resolve(pos))
end
end
end
function arena:draw()
for y = 1, height do
for _ = 1, width*7-9 do
io.write("-")
end
io.write("\n|")
for x = 1, width do
local owner = self:getCell(Vector(x, y)).owner
io.write(string.format("%-5s", owner or " "):sub(1, 5))
io.write("|")
end
io.write("\n|")
for x = 1, width do
local core = self:getCell(Vector(x, y)).core
io.write(core and "Core " or " ")
io.write("|")
end
io.write("\n|")
for x = 1, width do
local cell = self:getCell(Vector(x, y))
io.write(cell.core and string.format("%-5f", cell.energy):sub(1, 5) or " ")
io.write("|")
end
io.write("\n")
end
for _ = 1, width*7-9 do
io.write("-")
end
io.write("\n")
end
return arena
end
return Arena