Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- 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
Add Comment
Please, Sign In to add comment