--[[
Dead-reckoning system v0.7
by Pegasus Epsilon
<pegasus@pimpninjas.org>
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