291 lines
7.6 KiB
Plaintext
291 lines
7.6 KiB
Plaintext
if not tinyMap then
|
|
if not os.loadAPI("tinyMap") then
|
|
error("could not load tinyMap API")
|
|
end
|
|
end
|
|
if not aStar then
|
|
if not os.loadAPI("aStar") then
|
|
error("could not load aStar API")
|
|
end
|
|
end
|
|
if not location then
|
|
if not os.loadAPI("location") then
|
|
error("could not load location API")
|
|
end
|
|
end
|
|
|
|
local position
|
|
|
|
local SESSION_MAX_DISTANCE_DEFAULT = 32
|
|
|
|
local SESSION_COORD_CLEAR = 1
|
|
local SESSION_COORD_BLOCKED = 2
|
|
local SESSION_COORD_DISTANCE = math.huge
|
|
local MAIN_COORD_CLEAR = nil
|
|
local MAIN_COORD_BLOCKED = 1
|
|
local MAIN_COORD_DISTANCE = 1024
|
|
|
|
local sessionMidPoint
|
|
local sessionMaxDistance
|
|
local sessionMap
|
|
local mainMap = tinyMap.new("starNavMaps")
|
|
|
|
local function sup_norm(a, b)
|
|
return math.max(math.abs(a.x - b.x), math.abs(a.y - b.y), math.abs(a.z - b.z))
|
|
end
|
|
|
|
local function distanceFunc(a, b)
|
|
local sessionMapA, sessionMapB = sessionMap:get(a), sessionMap:get(b)
|
|
if sup_norm(a, sessionMidPoint) > sessionMaxDistance then
|
|
return SESSION_COORD_DISTANCE -- first coord is outside the search region
|
|
elseif sup_norm(b, sessionMidPoint) > sessionMaxDistance then
|
|
return SESSION_COORD_DISTANCE -- second coord is outside the search region
|
|
elseif sessionMapA == SESSION_COORD_BLOCKED or sessionMapB == SESSION_COORD_BLOCKED then
|
|
return SESSION_COORD_DISTANCE -- one of the coords has been found to be blocked this during this session
|
|
elseif sessionMapA == SESSION_COORD_CLEAR and sessionMapA == SESSION_COORD_CLEAR then
|
|
return aStar.distance(a, b) -- both coords has been found to be clear this during this session
|
|
elseif mainMap:get(a) or mainMap:get(b) then
|
|
return MAIN_COORD_DISTANCE
|
|
end
|
|
return aStar.distance(a, b) -- we are assuming both coords are clear
|
|
end
|
|
|
|
local function getHeading()
|
|
local i = 0
|
|
while turtle.detect() do
|
|
if i == 4 then
|
|
if turtle.up() then
|
|
i = 0
|
|
else
|
|
error("help I'm trapped in a ridiculous place")
|
|
end
|
|
else
|
|
turtle.turnRight()
|
|
i = i + 1
|
|
end
|
|
end
|
|
local p1 = {gps.locate()}
|
|
if #p1 == 3 then
|
|
p1 = vector.new(unpack(p1))
|
|
else
|
|
error("no gps signal - phase 1")
|
|
end
|
|
i = 0
|
|
while not turtle.forward() do
|
|
if i > 5 then error("couldn't move to determine direction") end
|
|
i = i + 1
|
|
sleep(1)
|
|
end
|
|
local p2 = {gps.locate()}
|
|
turtle.back()
|
|
if #p2 == 3 then
|
|
p2 = vector.new(unpack(p2))
|
|
else
|
|
error("no gps signal - phase 2")
|
|
end
|
|
local dir = p2 - p1
|
|
if dir.x == 1 then
|
|
return 3
|
|
elseif dir.x == -1 then
|
|
return 1
|
|
elseif dir.z == 1 then
|
|
return 0
|
|
elseif dir.z == -1 then
|
|
return 2
|
|
else
|
|
error("could not determine direction - phase 3")
|
|
end
|
|
end
|
|
|
|
local function detect(currPos, adjPos)
|
|
local dir = adjPos - currPos
|
|
if dir.y == 1 then
|
|
return turtle.detectUp()
|
|
elseif dir.y == -1 then
|
|
return turtle.detectDown()
|
|
elseif dir.x == 1 then
|
|
position:setHeading(3)
|
|
return turtle.detect()
|
|
elseif dir.x == -1 then
|
|
position:setHeading(1)
|
|
return turtle.detect()
|
|
elseif dir.z == 1 then
|
|
position:setHeading(0)
|
|
return turtle.detect()
|
|
elseif dir.z == -1 then
|
|
position:setHeading(2)
|
|
return turtle.detect()
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function inspect(currPos, adjPos)
|
|
local dir = adjPos - currPos
|
|
if dir.y == 1 then
|
|
return turtle.inspectUp()
|
|
elseif dir.y == -1 then
|
|
return turtle.inspectDown()
|
|
elseif dir.x == 1 then
|
|
position:setHeading(3)
|
|
return turtle.inspect()
|
|
elseif dir.x == -1 then
|
|
position:setHeading(1)
|
|
return turtle.inspect()
|
|
elseif dir.z == 1 then
|
|
position:setHeading(0)
|
|
return turtle.inspect()
|
|
elseif dir.z == -1 then
|
|
position:setHeading(2)
|
|
return turtle.inspect()
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function updateCoord(coord, isBlocked)
|
|
if isBlocked then
|
|
sessionMap:set(pos, SESSION_COORD_BLOCKED)
|
|
mainMap:set(pos, MAIN_COORD_BLOCKED)
|
|
else
|
|
sessionMap:set(pos, SESSION_COORD_CLEAR)
|
|
mainMap:set(pos, MAIN_COORD_CLEAR)
|
|
end
|
|
end
|
|
|
|
local function detectAll(currPos)
|
|
for _, pos in ipairs(aStar.adjacent(currPos)) do -- better order of checking directions
|
|
updateCoord(pos, detect(currPos, pos))
|
|
end
|
|
end
|
|
|
|
local function findSensor()
|
|
for _, side in ipairs(peripheral.getNames()) do
|
|
if peripheral.getType(side) == "turtlesensorenvironment" then
|
|
return side
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function scan(currPos)
|
|
local sensorSide = findSensor()
|
|
if sensorSide then
|
|
local rawBlockInfo = peripheral.call(sensorSide, "sonicScan")
|
|
local sortedBlockInfo = aStar.newMap()
|
|
for _, blockInfo in ipairs(rawBlockInfo) do
|
|
sortedBlockInfo:set(currPos + vector.new(blockInfo.x, blockInfo.y, blockInfo.z), blockInfo)
|
|
end
|
|
local toCheckQueue = {}
|
|
for _, pos in ipairs(aStar.adjacent(currPos)) do
|
|
if sortedBlockInfo:get(pos) then
|
|
table.insert(toCheckQueue, pos)
|
|
end
|
|
end
|
|
while toCheckQueue[1] do
|
|
local pos = table.remove(toCheckQueue, 1)
|
|
local blockInfo = sortedBlockInfo:get(pos)
|
|
if blockInfo.type == "AIR" then
|
|
for _, pos2 in ipairs(aStar.adjacent(pos)) do
|
|
local blockInfo2 = sortedBlockInfo:get(pos2)
|
|
if blockInfo2 and not blockInfo2.checked then
|
|
table.insert(toCheckQueue, pos2)
|
|
end
|
|
end
|
|
updateCoord(pos, false)
|
|
else
|
|
updateCoord(pos, true)
|
|
end
|
|
blockInfo.checked = true
|
|
end
|
|
else
|
|
detectAll(currPos)
|
|
end
|
|
mainMap:save()
|
|
end
|
|
|
|
local function move(currPos, adjPos)
|
|
if not detect(position, adjPos) then
|
|
local dir = adjPos - currPos
|
|
if dir.y == 1 then
|
|
return position:up()
|
|
elseif dir.y == -1 then
|
|
return position:down()
|
|
else
|
|
return position:forward()
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function goto(x, y, z, maxDistance)
|
|
if turtle.getFuelLevel() == 0 then
|
|
return false, "ran out of fuel"
|
|
end
|
|
if not position then
|
|
local heading = getHeading()
|
|
local currPos = {gps.locate()}
|
|
if #currPos == 3 then
|
|
local x, y, z = unpack(currPos)
|
|
position = location.new(x, y, z, heading)
|
|
else
|
|
return false, "couldn't determine location"
|
|
end
|
|
end
|
|
|
|
local goal = vector.new(tonumber(x), tonumber(y), tonumber(z))
|
|
|
|
sessionMap = aStar.newMap()
|
|
sessionMidPoint = vector.new(math.floor((goal.x + position.x)/2), math.floor((goal.y + position.y)/2), math.floor((goal.z + position.z)/2))
|
|
sessionMaxDistance = (type(maxDistance) == "number" and maxDistance) or math.max(2*sup_norm(sessionMidPoint, goal), SESSION_MAX_DISTANCE_DEFAULT)
|
|
|
|
local path = aStar.compute(distanceFunc, position, goal)
|
|
if not path then
|
|
return false, "no known path to goal"
|
|
end
|
|
|
|
while not aStar.vectorEquals(position, goal) do
|
|
local movePos = table.remove(path)
|
|
while not move(position, movePos) do
|
|
local blockPresent, blockData = inspect(position, movePos)
|
|
local recalculate, isTurtle = false, false
|
|
if blockPresent and (blockData.name == "ComputerCraft:CC-TurtleAdvanced" or blockData.name == "ComputerCraft:CC-Turtle") then -- there is a turtle in the way
|
|
sleep(math.random(0, 3))
|
|
local blockPresent2, blockData2 = inspect(position, movePos)
|
|
if blockPresent2 and (blockData2.name == "ComputerCraft:CC-TurtleAdvanced" or blockData2.name == "ComputerCraft:CC-Turtle") then -- the turtle is still there
|
|
sessionMap:set(movePos, SESSION_COORD_BLOCKED)
|
|
recalculate, isTurtle = true, true
|
|
end
|
|
elseif blockPresent then
|
|
scan(position) -- update map info
|
|
recalculate = true
|
|
elseif turtle.getFuelLevel() == 0 then
|
|
return false, "ran out of fuel"
|
|
else
|
|
sleep(1)
|
|
end
|
|
if recalculate then
|
|
if sessionMap:get(goal) == SESSION_COORD_BLOCKED then return false, "goal is blocked" end
|
|
path = aStar.compute(distanceFunc, position, goal)
|
|
if not path then
|
|
return false, "no known path to goal"
|
|
end
|
|
movePos = table.remove(path)
|
|
if isTurtle then
|
|
sessionMap:set(movePos, nil)
|
|
end
|
|
end
|
|
end
|
|
if mainMap:get(movePos) then
|
|
mainMap:set(movePos, MAIN_COORD_CLEAR)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function getPosition()
|
|
if position then
|
|
return position:value()
|
|
end
|
|
end |