feat: implement movement wrapping and pathfinding module for turtle
This commit is contained in:
210
turtle.lua
210
turtle.lua
@@ -203,7 +203,215 @@ end
|
|||||||
|
|
||||||
syncHomeWithServer()
|
syncHomeWithServer()
|
||||||
updateFuel()
|
updateFuel()
|
||||||
print("Ready! Turtle " .. os.getComputerID() .. " online (v5 server-driven)")
|
|
||||||
|
-- ========== Movement Wrapping (heading + position tracking) ==========
|
||||||
|
|
||||||
|
-- Heading: 0=south(+z), 1=west(-x), 2=north(-z), 3=east(+x)
|
||||||
|
local MOVE_DELTA = {
|
||||||
|
[0] = { x = 0, z = 1 }, -- south
|
||||||
|
[1] = { x = -1, z = 0 }, -- west
|
||||||
|
[2] = { x = 0, z = -1 }, -- north
|
||||||
|
[3] = { x = 1, z = 0 }, -- east
|
||||||
|
}
|
||||||
|
|
||||||
|
local _rawForward = turtle.forward
|
||||||
|
local _rawBack = turtle.back
|
||||||
|
local _rawUp = turtle.up
|
||||||
|
local _rawDown = turtle.down
|
||||||
|
local _rawTurnLeft = turtle.turnLeft
|
||||||
|
local _rawTurnRight = turtle.turnRight
|
||||||
|
|
||||||
|
turtle.forward = function()
|
||||||
|
local ok, reason = _rawForward()
|
||||||
|
if ok and state.position then
|
||||||
|
local d = MOVE_DELTA[state.facing]
|
||||||
|
state.position.x = state.position.x + d.x
|
||||||
|
state.position.z = state.position.z + d.z
|
||||||
|
end
|
||||||
|
return ok, reason
|
||||||
|
end
|
||||||
|
|
||||||
|
turtle.back = function()
|
||||||
|
local ok, reason = _rawBack()
|
||||||
|
if ok and state.position then
|
||||||
|
local d = MOVE_DELTA[state.facing]
|
||||||
|
state.position.x = state.position.x - d.x
|
||||||
|
state.position.z = state.position.z - d.z
|
||||||
|
end
|
||||||
|
return ok, reason
|
||||||
|
end
|
||||||
|
|
||||||
|
turtle.up = function()
|
||||||
|
local ok, reason = _rawUp()
|
||||||
|
if ok and state.position then
|
||||||
|
state.position.y = state.position.y + 1
|
||||||
|
end
|
||||||
|
return ok, reason
|
||||||
|
end
|
||||||
|
|
||||||
|
turtle.down = function()
|
||||||
|
local ok, reason = _rawDown()
|
||||||
|
if ok and state.position then
|
||||||
|
state.position.y = state.position.y - 1
|
||||||
|
end
|
||||||
|
return ok, reason
|
||||||
|
end
|
||||||
|
|
||||||
|
turtle.turnLeft = function()
|
||||||
|
local ok = _rawTurnLeft()
|
||||||
|
if ok then
|
||||||
|
state.facing = (state.facing + 3) % 4
|
||||||
|
_G._turtleFacing = state.facing
|
||||||
|
end
|
||||||
|
return ok
|
||||||
|
end
|
||||||
|
|
||||||
|
turtle.turnRight = function()
|
||||||
|
local ok = _rawTurnRight()
|
||||||
|
if ok then
|
||||||
|
state.facing = (state.facing + 1) % 4
|
||||||
|
_G._turtleFacing = state.facing
|
||||||
|
end
|
||||||
|
return ok
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect heading via GPS triangulation
|
||||||
|
local function detectHeading()
|
||||||
|
local x1, _, z1 = gps.locate(2)
|
||||||
|
if not x1 then return false end
|
||||||
|
if _rawForward() then
|
||||||
|
local x2, _, z2 = gps.locate(2)
|
||||||
|
_rawBack()
|
||||||
|
if x2 then
|
||||||
|
local dx = math.floor(x2) - math.floor(x1)
|
||||||
|
local dz = math.floor(z2) - math.floor(z1)
|
||||||
|
if dz > 0 then state.facing = 0 -- south
|
||||||
|
elseif dz < 0 then state.facing = 2 -- north
|
||||||
|
elseif dx > 0 then state.facing = 3 -- east
|
||||||
|
elseif dx < 0 then state.facing = 1 -- west
|
||||||
|
end
|
||||||
|
_G._turtleFacing = state.facing
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if state.position then
|
||||||
|
if detectHeading() then
|
||||||
|
print("Heading: " .. ({"south","west","north","east"})[state.facing + 1])
|
||||||
|
else
|
||||||
|
print("Heading: unknown (blocked)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ========== Pathfinding Module ==========
|
||||||
|
|
||||||
|
local pathfind = {}
|
||||||
|
|
||||||
|
--- Face a target heading.
|
||||||
|
function pathfind.face(targetH)
|
||||||
|
targetH = targetH % 4
|
||||||
|
while state.facing ~= targetH do
|
||||||
|
local diff = (targetH - state.facing) % 4
|
||||||
|
if diff == 1 then
|
||||||
|
turtle.turnRight()
|
||||||
|
elseif diff == 3 then
|
||||||
|
turtle.turnLeft()
|
||||||
|
else
|
||||||
|
turtle.turnRight()
|
||||||
|
turtle.turnRight()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Navigate to target coordinates with simple obstacle avoidance.
|
||||||
|
-- @param tx, ty, tz target position
|
||||||
|
-- @param options { dig = false, maxAttempts = 256 }
|
||||||
|
-- @return true or false, error
|
||||||
|
function pathfind.goto(tx, ty, tz, options)
|
||||||
|
options = options or {}
|
||||||
|
local maxAttempts = options.maxAttempts or 256
|
||||||
|
local dig = options.dig or false
|
||||||
|
local attempts = 0
|
||||||
|
|
||||||
|
while attempts < maxAttempts do
|
||||||
|
local pos = state.position
|
||||||
|
if not pos then return false, "No GPS position" end
|
||||||
|
|
||||||
|
-- Arrived?
|
||||||
|
if pos.x == tx and pos.y == ty and pos.z == tz then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
attempts = attempts + 1
|
||||||
|
local moved = false
|
||||||
|
|
||||||
|
-- Priority: Y first (get to correct height), then X, then Z
|
||||||
|
if not moved and pos.y ~= ty then
|
||||||
|
if pos.y < ty then
|
||||||
|
moved = turtle.up()
|
||||||
|
if not moved and dig then turtle.digUp(); moved = turtle.up() end
|
||||||
|
else
|
||||||
|
moved = turtle.down()
|
||||||
|
if not moved and dig then turtle.digDown(); moved = turtle.down() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not moved and pos.x ~= tx then
|
||||||
|
pathfind.face(tx > pos.x and 3 or 1)
|
||||||
|
moved = turtle.forward()
|
||||||
|
if not moved and dig then turtle.dig(); moved = turtle.forward() end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not moved and pos.z ~= tz then
|
||||||
|
pathfind.face(tz > pos.z and 0 or 2)
|
||||||
|
moved = turtle.forward()
|
||||||
|
if not moved and dig then turtle.dig(); moved = turtle.forward() end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Obstacle avoidance: try going around
|
||||||
|
if not moved then
|
||||||
|
if turtle.up() then
|
||||||
|
moved = true
|
||||||
|
elseif turtle.down() then
|
||||||
|
moved = true
|
||||||
|
else
|
||||||
|
turtle.turnRight()
|
||||||
|
if turtle.forward() then
|
||||||
|
moved = true
|
||||||
|
else
|
||||||
|
turtle.turnLeft()
|
||||||
|
turtle.turnLeft()
|
||||||
|
if turtle.forward() then
|
||||||
|
moved = true
|
||||||
|
else
|
||||||
|
turtle.turnRight() -- restore heading
|
||||||
|
return false, "Stuck at " .. pos.x .. "," .. pos.y .. "," .. pos.z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, "Max attempts exceeded"
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Go home (to saved home position).
|
||||||
|
function pathfind.goHome(options)
|
||||||
|
if not state.homePosition then return false, "No home position set" end
|
||||||
|
return pathfind.goto(state.homePosition.x, state.homePosition.y, state.homePosition.z, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get current heading name.
|
||||||
|
function pathfind.headingName()
|
||||||
|
return ({"south","west","north","east"})[state.facing + 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Expose globally for eval access
|
||||||
|
_G._pathfind = pathfind
|
||||||
|
|
||||||
|
print("Ready! Turtle " .. os.getComputerID() .. " online (v5 server-driven + pathfinding)")
|
||||||
broadcastStatus()
|
broadcastStatus()
|
||||||
|
|
||||||
-- ========== Main Loop ==========
|
-- ========== Main Loop ==========
|
||||||
|
|||||||
Reference in New Issue
Block a user