require('stardust') -- Just some shorthand local P = point.new local T = math.tau -- Some constants to tweak local CAVE_SIZE = 100 local CELL_DIST = 100 local STARTING_MINERS = 10 local MIN_CELL_SIZE = math.sqrt(2*CELL_DIST^2)/2*1.2 local MAX_CELL_SIZE = MIN_CELL_SIZE * 1.5 local PADDING = MAX_CELL_SIZE -- Return a point with integer components indicating a random passable tile for -- the miner. function takeStep(world, pos) local direction = math.random(0,7) if direction >= 4 then direction = direction + 1 end local x = pos.x + math.floor(direction / 3) - 1 local y = pos.y + direction % 3 - 1 if x > 0 and x < CAVE_SIZE and y > 0 and y < CAVE_SIZE then return point.new(x, y) end return pos end function countPassableTiles(world, pos) local count = 0 for x=pos.x-1,pos.x+1 do for y=pos.y-1,pos.y+1 do if (x > 0 and x < CAVE_SIZE) and (y > 0 and y < CAVE_SIZE) and (x ~= 0 or y ~= 0) and not world[x][y] then count = count + 1 end end end return count end function cleanWall(poly) poly = sd.subdividePolyline(poly, 4, 0.8) for i=1,3 do poly = sd.round(poly) poly = sd.rdp_simplify(poly, 1.0) end return poly end function main() -- Table of points representing an active miner local miners = { } for i=1,STARTING_MINERS do table.insert(miners, point.new(CAVE_SIZE / 2, CAVE_SIZE / 2)) end local world = { } for x=1,CAVE_SIZE do world[x] = { } for y=1,CAVE_SIZE do world[x][y] = true end end local cellsExcavated = 0 local spawnPoints = { } local repairItems = { } while cellsExcavated < 1000 do for i=1, #miners do local miner = miners[i] if miner then if world[miner.x][miner.y] then world[miner.x][miner.y] = false cellsExcavated = cellsExcavated + 1 end local dest = takeStep(world, miner) if #miners > 1 and countPassableTiles(world, dest) < 7 then if math.random() > 0.5 then table.insert(spawnPoints, miner) else table.insert(repairItems, miner) end table.remove(miners, i) else miners[i] = dest if math.random() < .01 then table.insert(miners, miners[i]) end end end end end local holes = { } for x=1,CAVE_SIZE do for y=1,CAVE_SIZE do if world[x][y] == false then table.insert(holes, sd.makeCircle(point.new(x*CELL_DIST,y*CELL_DIST), math.random(MIN_CELL_SIZE, MAX_CELL_SIZE))) end end end local result = Geom.clipPolygonsAsTree(ClipType.Union, holes, { }) local obstructions = { } for i,child in ipairs(result.children) do table.insert(obstructions, cleanWall(child.points)) end local hull = cleanWall(result.points) local ex = sd.extents(hull) local bounds = { point.new(ex.minx - PADDING, ex.miny - PADDING), point.new(ex.maxx + PADDING, ex.miny - PADDING), point.new(ex.maxx + PADDING, ex.maxy + PADDING), point.new(ex.minx - PADDING, ex.maxy + PADDING) } local shell = Geom.clipPolygons(ClipType.Difference, { bounds }, { hull }, true) for i,poly in ipairs(sd.concat(obstructions, shell)) do if #poly > 2 then bf:addItem(PolyWall.new(poly)) end end for i,p in ipairs(spawnPoints) do bf:addItem(Spawn.new(p * CELL_DIST, 1)) end for i,p in ipairs(repairItems) do bf:addItem(RepairItem.new(p * CELL_DIST)) end bf:addItem(Spawn.new(point.new(CAVE_SIZE * CELL_DIST / 2, CAVE_SIZE * CELL_DIST / 2), 1)) end