-- perimeter_grid.lua -- CC:Tweaked turtle script -- 1) Move forward until a wall, then right-hand-rule follow to map perimeter. -- 2) After full loop, find a 9x9 square grid (10x10 nodes) fully inside polygon, -- with the starting position centered in one grid square. -- 3) Place blocks DOWN at each node, then return to start pose. ------------------------------ -- Pose & Movement Utilities -- ------------------------------ local POS = {x=0, y=0, z=0, d=0} -- d: 0=N,1=E,2=S,3=W; x east+, z south+, y up+ local START = {x=0,y=0,z=0,d=0} local function dirToVec(d) if d==0 then return 0,0,-1 elseif d==1 then return 1,0,0 elseif d==2 then return 0,0,1 else return -1,0,0 end end local function updateForward() local dx,dy,dz = dirToVec(POS.d) POS.x, POS.y, POS.z = POS.x+dx, POS.y+dy, POS.z+dz end local function updateBack() local dx,dy,dz = dirToVec(POS.d) POS.x, POS.y, POS.z = POS.x-dx, POS.y-dy, POS.z-dz end local function turnRight() turtle.turnRight() POS.d = (POS.d+1)%4 end local function turnLeft() turtle.turnLeft() POS.d = (POS.d+3)%4 end local function turnAround() turtle.turnRight(); turtle.turnRight() POS.d = (POS.d+2)%4 end local function forward() while not turtle.forward() do if turtle.detect() then return false end -- blocked by entity? try dig then forward turtle.dig() sleep(0.15) end updateForward() return true end local function back() if turtle.back() then updateBack() return true else turnAround() local ok = forward() turnAround() return ok end end local function up() while not turtle.up() do if turtle.detectUp() then turtle.digUp() end sleep(0.15) end POS.y = POS.y+1 end local function down() while not turtle.down() do if turtle.detectDown() then turtle.digDown() end sleep(0.15) end POS.y = POS.y-1 end local function faceDir(targetD) local diff = (targetD - POS.d) % 4 if diff==1 then turnRight() elseif diff==2 then turnAround() elseif diff==3 then turnLeft() end end local function gotoPose(x,y,z,d) -- Simple Manhattan navigator at single Y level first; will adjust Y as needed. -- Step 1: adjust Y while POS.y < y do up() end while POS.y > y do down() end -- Step 2: adjust X if POS.x ~= x then faceDir(POS.x < x and 1 or 3) while POS.x ~= x do assert(forward(), "Blocked en route (X).") end end -- Step 3: adjust Z if POS.z ~= z then faceDir(POS.z < z and 2 or 0) while POS.z ~= z do assert(forward(), "Blocked en route (Z).") end end -- Face desired direction if d~=nil then faceDir(d) end end ------------------ -- Fuel Routine -- ------------------ local function refuelMax() for i=1,16 do local detail = turtle.getItemDetail(i) if detail and turtle.refuel(0) then turtle.select(i) while turtle.refuel(1) do end end end turtle.select(1) end ------------------------- -- Perimeter Following -- ------------------------- -- Right-hand rule helper: check relative blocks local function lookForward() return turtle.detect() end local function lookRight() turnRight() local ok = turtle.detect() turnLeft() return ok end local function lookLeft() turnLeft() local ok = turtle.detect() turnRight() return ok end -- We assume a single-layer mapping at POS.y == START.y. -- First: move forward until hit a wall to start hugging. local function advanceUntilWall() while not turtle.detect() do if not forward() then break end end end -- Right-hand rule step: -- Prefer: turnRight & forward if open; else if forward open go; else if left open turnLeft & go; else turnAround & try. local function rhsStep() -- try right turnRight() if not turtle.detect() then local ok = forward() if not ok then return false end return true else -- can't go right; face back forward turnLeft() if not turtle.detect() then local ok = forward() if not ok then return false end return true else -- try left turnLeft() if not turtle.detect() then local ok = forward() if not ok then return false end -- we are now facing left; keep that heading return true else -- dead end: turn around turnRight() -- return to original forward turnAround() if not turtle.detect() then local ok = forward() if not ok then return false end return true else -- completely boxed (rare) return false end end end end end -- Follow the perimeter and record the 2D (x,z) vertex path at current y. local function followPerimeterAndMap() local y0 = START.y -- Ensure we’re on the same plane while POS.y > y0 do down() end while POS.y < y0 do up() end advanceUntilWall() -- Start hugging with right-hand rule local path = {} -- sequence of positions (x,z) local function rec() path[#path+1] = {x=POS.x, z=POS.z} end rec() local steps = 0 local maxSteps = 20000 local started = false local startSignature = string.format("%d,%d,%d", START.x, START.z, START.d) while steps < maxSteps do if not rhsStep() then error("Got stuck while following perimeter.") end steps = steps + 1 rec() -- loop detection: returned to starting tile with starting heading, after moving at least some steps local sig = string.format("%d,%d,%d", POS.x, POS.z, POS.d) if sig == startSignature and steps > 10 then break end end if steps >= maxSteps then error("Perimeter too large or loop not detected within step budget.") end -- Simplify path by removing consecutive duplicates local simp = {} local last = nil for _,p in ipairs(path) do if not last or p.x~=last.x or p.z~=last.z then simp[#simp+1] = p last = p end end -- Close polygon if needed if simp[1].x ~= simp[#simp].x or simp[1].z ~= simp[#simp].z then simp[#simp+1] = {x=simp[1].x, z=simp[1].z} end return simp end --------------------------- -- Geometry / PIP / Grid -- --------------------------- local function pointInPolygon(px, pz, poly) -- Ray casting on XZ plane; polygon is { {x,z}, ... } closed local inside = false for i=1,#poly-1 do local x1,z1 = poly[i].x, poly[i].z local x2,z2 = poly[i+1].x, poly[i+1].z -- Check edges that cross horizontal ray to +X local zmin = math.min(z1,z2) local zmax = math.max(z1,z2) if (pz > zmin) and (pz <= zmax) and (z1 ~= z2) then local xint = (pz - z1) * (x2 - x1) / (z2 - z1) + x1 if xint > px then inside = not inside end end end return inside end local function bbox(poly) local xmin,xmax = math.huge,-math.huge local zmin,zmax = math.huge,-math.huge for _,p in ipairs(poly) do if p.x < xmin then xmin = p.x end if p.x > xmax then xmax = p.x end if p.z < zmin then zmin = p.z end if p.z > zmax then zmax = p.z end end return xmin,xmax,zmin,zmax end -- Find a 9x9 grid (10x10 nodes) fully inside polygon, with start centered in a cell. -- We define grid nodes at integer lattice points: (gx0 + i, gz0 + j) for i,j=0..9 -- The starting position must be the center of some grid square: -- => start at (gx0+0.5 + c, gz0+0.5 + r) for some cell (c,r). We choose c=r=4 (middle-ish) -- so enforce: START.x == gx0 + 4.5 and START.z == gz0 + 4.5 -> gx0 = START.x - 4.5, gz0 = START.z - 4.5 -- Then we try small integer offsets around that to fit the polygon. local function findFittingGrid(poly) local targetSize = 9 local nodes = targetSize + 1 -- 10 -- base (floating) origin local baseGx0 = START.x - 4.5 local baseGz0 = START.z - 4.5 -- Try small integer shifts to keep all nodes inside polygon local search = {} local function push(dx,dz) search[#search+1] = {dx=dx,dz=dz} end for dz=-4,4 do for dx=-4,4 do push(dx,dz) end end for _,off in ipairs(search) do local gx0 = math.floor(baseGx0 + off.dx + 0.5) local gz0 = math.floor(baseGz0 + off.dz + 0.5) local allInside = true for j=0,targetSize do for i=0,targetSize do local nx, nz = gx0 + i, gz0 + j -- Require node strictly inside polygon (not on edge) for safety: if not pointInPolygon(nx + 1e-6, nz + 1e-6, poly) then allInside = false break end end if not allInside then break end end if allInside then return {gx0=gx0, gz0=gz0, nodes=nodes} end end return nil, "Could not fit 10x10 node lattice inside mapped perimeter near start." end ------------------------- -- Block Placement API -- ------------------------- local function ensureBlockSelected() for i=1,16 do local d = turtle.getItemDetail(i) if d then turtle.select(i); return true end end return false end local function placeDownAtNode(x,z,y) -- Go at y level to (x,z) and place down gotoPose(x,y,z,nil) if not turtle.detectDown() then -- ensure we have something to place if not ensureBlockSelected() then error("Out of blocks while placing grid nodes.") end turtle.placeDown() end end ----------------------- -- Main Orchestration ----------------------- local function main() print("Refueling to maximum...") refuelMax() print("Mapping perimeter with right-hand rule...") local poly = followPerimeterAndMap() print(("Perimeter mapped: %d vertices."):format(#poly)) print("Searching for 9x9 grid (10x10 nodes) inside polygon...") local grid, err = findFittingGrid(poly) if not grid then printError(err) print("Returning to start pose.") gotoPose(START.x, START.y, START.z, START.d) return end print(("Grid origin (nodes): gx0=%d, gz0=%d"):format(grid.gx0, grid.gz0)) -- Place nodes at y = START.y (down placement) local yLevel = START.y print("Placing blocks at grid nodes...") for j=0,grid.nodes-1 do -- Simple boustrophedon to reduce turning if j % 2 == 0 then for i=0,grid.nodes-1 do local nx, nz = grid.gx0 + i, grid.gz0 + j placeDownAtNode(nx, nz, yLevel) end else for i=grid.nodes-1,0,-1 do local nx, nz = grid.gx0 + i, grid.gz0 + j placeDownAtNode(nx, nz, yLevel) end end end print("Grid placement done. Returning to start...") gotoPose(START.x, START.y, START.z, START.d) print("Complete.") end ----------------------- -- Execute ----------------------- local ok, err = pcall(main) if not ok then printError("Error: "..tostring(err)) -- try to return to start safely pcall(function() gotoPose(START.x, START.y, START.z, START.d) end) end