--[[ Dead-reckoning system v0.7 by Pegasus Epsilon Distribute Unmodified http://pegasus.pimpninjas.org/license The goal of this is to know where we are in relation to 0, 0, 0, 0 (where we were started) at all times, without GPS, regardless of what happens, so long as we're not broken and carried off, or moved on a frame machine. This can have useful side-effects, as a frame machine can move a mess of turtles a given amount of blocks and then signal them to deploy, swarm, and return home when done, and then repeat. Functions in this file are meant to be guaranteed atomic and successful. They will usually complete -- eventually -- 100% of the time. Chunk unloads and server reboots be damned. Known issues: Computercraft does not (can not, according to dan200) check if the move can succeed before releasing control of the tick. Therefore the built-in move functions may not fail until the next tick. The chunk may be unloaded by then. I have a possible solution in my head, but I haven't bothered implementing it, since I haven't been playing CC in a while. I'll probably start again and fix this one day. NOTE: The turn commands (including the batched turns) *ARE* guaranteed atomic and successful, though, so no worries there. Rely on those all you like. Changelog: 2013 May 03 v0.1 -- release v0.2 -- batched movement support v0.3 -- goHome() method v0.4 -- gotoZXYF() method v0.5 -- bugfix, localized private functions v0.6 -- added getters, doi v0.7 -- added turn() function turn(1) = turnLeft() turn(2) = turnAround() turn(3) = turnRight() --]] -- paths, you can change these local p = "apis/" local j = p.."journal/" -- bootstrap if not fs.exists(j) or not fs.isDir(j) then fs.makeDir(j) end -- BEGIN DEAD RECKONING SYSTEM local x, y, z, f = 0, 0, 0, 0 -- rotation local function saveFacing () local fh = fs.open(j.."facing", "w") fh.writeLine(f) fh.close() end function turnRight () -- can't fail -- saves facing while turning -- then yields to chunk unload f = (f + 1) % 4 saveFacing() turtle.turnRight() end function turnLeft () -- can't fail -- saves facing while turning -- then yields to chunk unload f = (f - 1) % 4 saveFacing() turtle.turnLeft() end function turnAround () -- batched actions can fail -- so we journal for a resume point local fh = fs.open(j.."turn", "w") fh.writeLine(1) fh.close() turnLeft() fs.delete(j.."turn") turnLeft() end function turn (d) ({ [1] = turnLeft, [2] = turnAround, [3] = turnRight })[d]() end function face (d) -- face a given direction -- 0 = as placed -- 1 = right -- 2 = back -- 3 = left -- convert requested direction to -- necessary action with a fancy -- transient lookup table and a -- little bit of math d = (f - d + 4) % 4 if 0 < d and 4 > d then turn(d) end end -- vertical movement local function saveDepth () local fh = fs.open(j.."depth", "w") fh.writeLine(z) fh.close() end local function tryUp () -- possible BUG - does turtle.up() -- return immediately upon failure? -- or yield and wait until the next -- tick to return? z = z - 1 saveDepth() local ret = turtle.up() if not ret then -- move failed, revert changes z = z + 1 saveDepth() end return ret end function up (times) times = times or 1 for i = times, 1, -1 do local fh = fs.open(j.."up") fh.writeLine(times) fh.close() while not tryUp() do turtle.digUp() turtle.attackUp() end end fs.delete(j.."up") end local function tryDown () -- possible BUG - does turtle.down() -- return immediately upon failure? -- or yield and wait until the next -- tick to return? z = z + 1 saveDepth() local ret = turtle.down() if not ret then -- move failed, revert changes z = z - 1 saveDepth() end return ret end function down (times) times = times or 1 for i = times, 1, -1 do local fh = fs.open(j.."down", "w") fh.writeLine(times) fh.close() while not tryDown() do turtle.digDown() turtle.attackDown() end end fs.delete(j.."down") end -- horizontal movement local function savePosition () local fh = fs.open(j.."pos", "w") fh.writeLine(x) fh.writeLine(y) fh.close() end local function tryMove () -- possible BUG - does -- turtle.forward() return -- immediately upon failure? or yield -- and wait until the next tick to -- return? -- this table needs to live a tiny -- bit longer than the last one. local coordUpdate = { [0] = function () x = x + 1 end, [1] = function () y = y + 1 end, [2] = function () x = x - 1 end, [3] = function () y = y - 1 end } coordUpdate[f]() savePosition() local ret = turtle.forward() if not ret then -- move failed, revert changes coordUpdate[(f + 2) % 4]() --sanityCheck() savePosition() end return ret end local digging = true function forward (times) times = times or 1 for i = times, 1, -1 do local fh = fs.open(j.."forward", "w") fh.writeLine(times) fh.close() repeat turtle.dig() turtle.attack() until tryMove() end fs.delete(j.."forward") end function gotoZYXF (dz, dy, dx, df) local fh = fs.open(j.."goto", "w") fh.writeLine(dz) fh.writeLine(dy) fh.writeLine(dx) fh.writeLine(df) fh.close() while dz < z do up() end while dz > z do down() end if dy ~= y then if dy > y then face(3) else face(1) end repeat forward() until 0 == y end if dx ~= x then if dx > x then face(2) else face(0) end repeat forward() until 0 == x end face(df) fs.delete(j.."goto") end function goHome () gotoZYXF(0, 0, 0, 0) end function hasState () if not fs.exists(j.."pos") or not fs.exists(j.."facing") or not fs.exists(j.."depth") then return false end return true end -- call this to pick up where you were function resume () if not hasState() then return false end -- resume aborted about-face if fs.exists(j.."turn") then -- currently only one option -- interrupted about-face so finish -- up with the final left-turn turnLeft() fs.delete(j.."turn") end -- resume aborted movements if fs.exists(j.."up") then local fh = fs.open(j.."up", "r") local times = fh.readLine() fh.close() up(times) end if fs.exists(j.."down") then local fh = fs.open(j.."down", "r") local times = fh.readLine() fh.close() down(times) end if fs.exists(j.."forward") then local fh = fs.open(j.."forward", "r") local times = fh.readLine() fh.close() forward(times) end if fs.exists(j.."goto") then local fh = fs.open(j.."goto", "r") local dz = fh.readLine() local dy = fh.readLine() local dx = fh.readLine() local df = fh.readLine() fh.close() gotoZYXF(dz, dy, dx, df) end end function cleanup () for file, file in ipairs({ j.."turn", j.."pos", j.."facing", j.."depth" }) do if fs.exists(file) then fs.delete(file) end end end print("Dead reckoning system loaded.") if hasState() then print("Dead reckoning has an idea where it might be.") print("To pick up where you left off, call reckon.resume()") end -- END DEAD RECKONING SYSTEM -- getter exports function getX () return x end function getY () return y end function getZ () return z end function getF () return f end