robathome

MC gridmaker

Nov 12th, 2025
331
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.78 KB | None | 0 0
  1. -- perimeter_grid.lua
  2. -- CC:Tweaked turtle script
  3. -- 1) Move forward until a wall, then right-hand-rule follow to map perimeter.
  4. -- 2) After full loop, find a 9x9 square grid (10x10 nodes) fully inside polygon,
  5. -- with the starting position centered in one grid square.
  6. -- 3) Place blocks DOWN at each node, then return to start pose.
  7.  
  8. ------------------------------
  9. -- Pose & Movement Utilities --
  10. ------------------------------
  11. 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+
  12. local START = {x=0,y=0,z=0,d=0}
  13.  
  14. local function dirToVec(d)
  15. if d==0 then return 0,0,-1
  16. elseif d==1 then return 1,0,0
  17. elseif d==2 then return 0,0,1
  18. else return -1,0,0 end
  19. end
  20.  
  21. local function updateForward()
  22. local dx,dy,dz = dirToVec(POS.d)
  23. POS.x, POS.y, POS.z = POS.x+dx, POS.y+dy, POS.z+dz
  24. end
  25. local function updateBack()
  26. local dx,dy,dz = dirToVec(POS.d)
  27. POS.x, POS.y, POS.z = POS.x-dx, POS.y-dy, POS.z-dz
  28. end
  29.  
  30. local function turnRight()
  31. turtle.turnRight()
  32. POS.d = (POS.d+1)%4
  33. end
  34. local function turnLeft()
  35. turtle.turnLeft()
  36. POS.d = (POS.d+3)%4
  37. end
  38. local function turnAround()
  39. turtle.turnRight(); turtle.turnRight()
  40. POS.d = (POS.d+2)%4
  41. end
  42.  
  43. local function forward()
  44. while not turtle.forward() do
  45. if turtle.detect() then return false end
  46. -- blocked by entity? try dig then forward
  47. turtle.dig()
  48. sleep(0.15)
  49. end
  50. updateForward()
  51. return true
  52. end
  53.  
  54. local function back()
  55. if turtle.back() then
  56. updateBack()
  57. return true
  58. else
  59. turnAround()
  60. local ok = forward()
  61. turnAround()
  62. return ok
  63. end
  64. end
  65.  
  66. local function up()
  67. while not turtle.up() do
  68. if turtle.detectUp() then turtle.digUp() end
  69. sleep(0.15)
  70. end
  71. POS.y = POS.y+1
  72. end
  73.  
  74. local function down()
  75. while not turtle.down() do
  76. if turtle.detectDown() then turtle.digDown() end
  77. sleep(0.15)
  78. end
  79. POS.y = POS.y-1
  80. end
  81.  
  82. local function faceDir(targetD)
  83. local diff = (targetD - POS.d) % 4
  84. if diff==1 then turnRight()
  85. elseif diff==2 then turnAround()
  86. elseif diff==3 then turnLeft() end
  87. end
  88.  
  89. local function gotoPose(x,y,z,d)
  90. -- Simple Manhattan navigator at single Y level first; will adjust Y as needed.
  91. -- Step 1: adjust Y
  92. while POS.y < y do up() end
  93. while POS.y > y do down() end
  94. -- Step 2: adjust X
  95. if POS.x ~= x then
  96. faceDir(POS.x < x and 1 or 3)
  97. while POS.x ~= x do assert(forward(), "Blocked en route (X).") end
  98. end
  99. -- Step 3: adjust Z
  100. if POS.z ~= z then
  101. faceDir(POS.z < z and 2 or 0)
  102. while POS.z ~= z do assert(forward(), "Blocked en route (Z).") end
  103. end
  104. -- Face desired direction
  105. if d~=nil then faceDir(d) end
  106. end
  107.  
  108. ------------------
  109. -- Fuel Routine --
  110. ------------------
  111. local function refuelMax()
  112. for i=1,16 do
  113. local detail = turtle.getItemDetail(i)
  114. if detail and turtle.refuel(0) then
  115. turtle.select(i)
  116. while turtle.refuel(1) do end
  117. end
  118. end
  119. turtle.select(1)
  120. end
  121.  
  122. -------------------------
  123. -- Perimeter Following --
  124. -------------------------
  125. -- Right-hand rule helper: check relative blocks
  126. local function lookForward() return turtle.detect() end
  127. local function lookRight()
  128. turnRight()
  129. local ok = turtle.detect()
  130. turnLeft()
  131. return ok
  132. end
  133. local function lookLeft()
  134. turnLeft()
  135. local ok = turtle.detect()
  136. turnRight()
  137. return ok
  138. end
  139.  
  140. -- We assume a single-layer mapping at POS.y == START.y.
  141. -- First: move forward until hit a wall to start hugging.
  142. local function advanceUntilWall()
  143. while not turtle.detect() do
  144. if not forward() then break end
  145. end
  146. end
  147.  
  148. -- Right-hand rule step:
  149. -- Prefer: turnRight & forward if open; else if forward open go; else if left open turnLeft & go; else turnAround & try.
  150. local function rhsStep()
  151. -- try right
  152. turnRight()
  153. if not turtle.detect() then
  154. local ok = forward()
  155. if not ok then return false end
  156. return true
  157. else
  158. -- can't go right; face back forward
  159. turnLeft()
  160. if not turtle.detect() then
  161. local ok = forward()
  162. if not ok then return false end
  163. return true
  164. else
  165. -- try left
  166. turnLeft()
  167. if not turtle.detect() then
  168. local ok = forward()
  169. if not ok then return false end
  170. -- we are now facing left; keep that heading
  171. return true
  172. else
  173. -- dead end: turn around
  174. turnRight() -- return to original forward
  175. turnAround()
  176. if not turtle.detect() then
  177. local ok = forward()
  178. if not ok then return false end
  179. return true
  180. else
  181. -- completely boxed (rare)
  182. return false
  183. end
  184. end
  185. end
  186. end
  187. end
  188.  
  189. -- Follow the perimeter and record the 2D (x,z) vertex path at current y.
  190. local function followPerimeterAndMap()
  191. local y0 = START.y
  192. -- Ensure we’re on the same plane
  193. while POS.y > y0 do down() end
  194. while POS.y < y0 do up() end
  195.  
  196. advanceUntilWall()
  197.  
  198. -- Start hugging with right-hand rule
  199. local path = {} -- sequence of positions (x,z)
  200. local function rec()
  201. path[#path+1] = {x=POS.x, z=POS.z}
  202. end
  203. rec()
  204.  
  205. local steps = 0
  206. local maxSteps = 20000
  207. local started = false
  208. local startSignature = string.format("%d,%d,%d", START.x, START.z, START.d)
  209.  
  210. while steps < maxSteps do
  211. if not rhsStep() then
  212. error("Got stuck while following perimeter.")
  213. end
  214. steps = steps + 1
  215. rec()
  216.  
  217. -- loop detection: returned to starting tile with starting heading, after moving at least some steps
  218. local sig = string.format("%d,%d,%d", POS.x, POS.z, POS.d)
  219. if sig == startSignature and steps > 10 then
  220. break
  221. end
  222. end
  223.  
  224. if steps >= maxSteps then
  225. error("Perimeter too large or loop not detected within step budget.")
  226. end
  227.  
  228. -- Simplify path by removing consecutive duplicates
  229. local simp = {}
  230. local last = nil
  231. for _,p in ipairs(path) do
  232. if not last or p.x~=last.x or p.z~=last.z then
  233. simp[#simp+1] = p
  234. last = p
  235. end
  236. end
  237.  
  238. -- Close polygon if needed
  239. if simp[1].x ~= simp[#simp].x or simp[1].z ~= simp[#simp].z then
  240. simp[#simp+1] = {x=simp[1].x, z=simp[1].z}
  241. end
  242. return simp
  243. end
  244.  
  245. ---------------------------
  246. -- Geometry / PIP / Grid --
  247. ---------------------------
  248. local function pointInPolygon(px, pz, poly)
  249. -- Ray casting on XZ plane; polygon is { {x,z}, ... } closed
  250. local inside = false
  251. for i=1,#poly-1 do
  252. local x1,z1 = poly[i].x, poly[i].z
  253. local x2,z2 = poly[i+1].x, poly[i+1].z
  254. -- Check edges that cross horizontal ray to +X
  255. local zmin = math.min(z1,z2)
  256. local zmax = math.max(z1,z2)
  257. if (pz > zmin) and (pz <= zmax) and (z1 ~= z2) then
  258. local xint = (pz - z1) * (x2 - x1) / (z2 - z1) + x1
  259. if xint > px then inside = not inside end
  260. end
  261. end
  262. return inside
  263. end
  264.  
  265. local function bbox(poly)
  266. local xmin,xmax = math.huge,-math.huge
  267. local zmin,zmax = math.huge,-math.huge
  268. for _,p in ipairs(poly) do
  269. if p.x < xmin then xmin = p.x end
  270. if p.x > xmax then xmax = p.x end
  271. if p.z < zmin then zmin = p.z end
  272. if p.z > zmax then zmax = p.z end
  273. end
  274. return xmin,xmax,zmin,zmax
  275. end
  276.  
  277. -- Find a 9x9 grid (10x10 nodes) fully inside polygon, with start centered in a cell.
  278. -- We define grid nodes at integer lattice points: (gx0 + i, gz0 + j) for i,j=0..9
  279. -- The starting position must be the center of some grid square:
  280. -- => start at (gx0+0.5 + c, gz0+0.5 + r) for some cell (c,r). We choose c=r=4 (middle-ish)
  281. -- so enforce: START.x == gx0 + 4.5 and START.z == gz0 + 4.5 -> gx0 = START.x - 4.5, gz0 = START.z - 4.5
  282. -- Then we try small integer offsets around that to fit the polygon.
  283. local function findFittingGrid(poly)
  284. local targetSize = 9
  285. local nodes = targetSize + 1 -- 10
  286.  
  287. -- base (floating) origin
  288. local baseGx0 = START.x - 4.5
  289. local baseGz0 = START.z - 4.5
  290.  
  291. -- Try small integer shifts to keep all nodes inside polygon
  292. local search = {}
  293. local function push(dx,dz) search[#search+1] = {dx=dx,dz=dz} end
  294. for dz=-4,4 do
  295. for dx=-4,4 do
  296. push(dx,dz)
  297. end
  298. end
  299.  
  300. for _,off in ipairs(search) do
  301. local gx0 = math.floor(baseGx0 + off.dx + 0.5)
  302. local gz0 = math.floor(baseGz0 + off.dz + 0.5)
  303.  
  304. local allInside = true
  305. for j=0,targetSize do
  306. for i=0,targetSize do
  307. local nx, nz = gx0 + i, gz0 + j
  308. -- Require node strictly inside polygon (not on edge) for safety:
  309. if not pointInPolygon(nx + 1e-6, nz + 1e-6, poly) then
  310. allInside = false
  311. break
  312. end
  313. end
  314. if not allInside then break end
  315. end
  316. if allInside then
  317. return {gx0=gx0, gz0=gz0, nodes=nodes}
  318. end
  319. end
  320.  
  321. return nil, "Could not fit 10x10 node lattice inside mapped perimeter near start."
  322. end
  323.  
  324. -------------------------
  325. -- Block Placement API --
  326. -------------------------
  327. local function ensureBlockSelected()
  328. for i=1,16 do
  329. local d = turtle.getItemDetail(i)
  330. if d then turtle.select(i); return true end
  331. end
  332. return false
  333. end
  334.  
  335. local function placeDownAtNode(x,z,y)
  336. -- Go at y level to (x,z) and place down
  337. gotoPose(x,y,z,nil)
  338. if not turtle.detectDown() then
  339. -- ensure we have something to place
  340. if not ensureBlockSelected() then
  341. error("Out of blocks while placing grid nodes.")
  342. end
  343. turtle.placeDown()
  344. end
  345. end
  346.  
  347. -----------------------
  348. -- Main Orchestration
  349. -----------------------
  350. local function main()
  351. print("Refueling to maximum...")
  352. refuelMax()
  353.  
  354. print("Mapping perimeter with right-hand rule...")
  355. local poly = followPerimeterAndMap()
  356. print(("Perimeter mapped: %d vertices."):format(#poly))
  357.  
  358. print("Searching for 9x9 grid (10x10 nodes) inside polygon...")
  359. local grid, err = findFittingGrid(poly)
  360. if not grid then
  361. printError(err)
  362. print("Returning to start pose.")
  363. gotoPose(START.x, START.y, START.z, START.d)
  364. return
  365. end
  366. print(("Grid origin (nodes): gx0=%d, gz0=%d"):format(grid.gx0, grid.gz0))
  367.  
  368. -- Place nodes at y = START.y (down placement)
  369. local yLevel = START.y
  370. print("Placing blocks at grid nodes...")
  371. for j=0,grid.nodes-1 do
  372. -- Simple boustrophedon to reduce turning
  373. if j % 2 == 0 then
  374. for i=0,grid.nodes-1 do
  375. local nx, nz = grid.gx0 + i, grid.gz0 + j
  376. placeDownAtNode(nx, nz, yLevel)
  377. end
  378. else
  379. for i=grid.nodes-1,0,-1 do
  380. local nx, nz = grid.gx0 + i, grid.gz0 + j
  381. placeDownAtNode(nx, nz, yLevel)
  382. end
  383. end
  384. end
  385.  
  386. print("Grid placement done. Returning to start...")
  387. gotoPose(START.x, START.y, START.z, START.d)
  388. print("Complete.")
  389. end
  390.  
  391. -----------------------
  392. -- Execute
  393. -----------------------
  394. local ok, err = pcall(main)
  395. if not ok then
  396. printError("Error: "..tostring(err))
  397. -- try to return to start safely
  398. pcall(function() gotoPose(START.x, START.y, START.z, START.d) end)
  399. end
  400.  
Add Comment
Please, Sign In to add comment