diff --git a/location.lua b/location.lua index 4638ddd..fc0ae7a 100644 --- a/location.lua +++ b/location.lua @@ -154,4 +154,21 @@ function new( x, y, z, h ) } setmetatable( l, lmetatable ) return l -end \ No newline at end of file +end + +local headings = { + [vector.new(0, 0, 1)] = 0, + [vector.new(-1, 0, 0)] = 1, + [vector.new(0, 0, -1)] = 2, + [vector.new(1, 0, 0)] = 3, + [vector.new(0, 1, 0)] = 4, + [vector.new(0, -1, 0)] = 5, +} + +function headingFromDelta(delta) + for vec, dir in pairs(headings) do + if aStar.vectorEquals(delta, vec) then + return dir + end + end +end diff --git a/netNav/netNav.lua b/netNav/netNav.lua index 53914d7..897b1b5 100644 --- a/netNav/netNav.lua +++ b/netNav/netNav.lua @@ -2,6 +2,7 @@ local apis = { "remoteMap", "aStar", "location", + "scanStrategy", } for _, api in ipairs(apis) do if not _G[api] then @@ -49,23 +50,6 @@ local function distanceFunc(a, b) return aStar.distance(a, b) -- we dont know anything useful so just calc the distance end -local directions = { - [vector.new(0, 0, 1)] = 0, - [vector.new(-1, 0, 0)] = 1, - [vector.new(0, 0, -1)] = 2, - [vector.new(1, 0, 0)] = 3, - [vector.new(0, 1, 0)] = 4, - [vector.new(0, -1, 0)] = 5, -} - -local function deltaToDirection(delta) - for vec, dir in pairs(directions) do - if aStar.vectorEquals(delta, vec) then - return dir - end - end -end - local function tryMove() for i = 1, 4 do if turtle.forward() then @@ -107,31 +91,16 @@ local function findPosition() error("no gps signal - phase 2") end - local direction = deltaToDirection(p1 - p2) - if direction and direction < 4 then - return location.new(p2.x, p2.y, p2.z, direction) + local currentDirection = location.headingFromDelta(p1 - p2) + if currentDirection and currentDirection < 4 then + return location.new(p2.x, p2.y, p2.z, currentDirection) else return false end end -local function detect(currPos, adjPos) - local direction = deltaToDirection(adjPos - currPos) - if direction then - position:setHeading(direction) - if direction == 4 then - return turtle.detectUp() - elseif direction == 5 then - return turtle.detectDown() - else - return turtle.detect() - end - end - return false -end - local function inspect(currPos, adjPos) - local direction = deltaToDirection(adjPos - currPos) + local direction = location.headingFromDelta(adjPos - currPos) if direction then position:setHeading(direction) if direction == 4 then @@ -145,81 +114,35 @@ local function inspect(currPos, adjPos) return false end -local function updateCoord(coord, isBlocked) +local function updateSessionMap(coord, isBlocked) if isBlocked then sessionMap:set(coord, SESSION_COORD_BLOCKED) - serverMap:set(coord, UPDATE_COORD_BLOCKED) else sessionMap:set(coord, SESSION_COORD_CLEAR) + end +end + +local function updateServerMap(coord, isBlocked) + if isBlocked then + serverMap:set(coord, UPDATE_COORD_BLOCKED) + else serverMap:set(coord, UPDATE_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({"left", "right"}) 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 - for _, blockInfo in ipairs(rawBlockInfo) do - local pos = currPos + vector.new(blockInfo.x, blockInfo.y, blockInfo.z) - local blockInfo = sortedBlockInfo:get(pos) - if not blockInfo.checked then - if blockInfo.type == "AIR" then - sessionMap:set(pos, SESSION_COORD_CLEAR) - else - sessionMap:set(pos, SESSION_COORD_BLOCKED) - end - end - end +local function scan(currentPosition) + local strategy = scanStrategy.getBest() + if strategy then + strategy.execute(currentPosition, updateSessionMap, updateServerMap) + serverMap:check() + serverMap:pushUpdates() else - detectAll(currPos) + -- throw error ? end - serverMap:check() - serverMap:pushUpdates() end local function move(currPos, adjPos) - local direction = deltaToDirection(adjPos - currPos) + local direction = location.headingFromDelta(adjPos - currPos) if direction then position:setHeading(direction) if direction == 4 then @@ -356,3 +279,7 @@ function getPosition() return position:value() end end + +function addScanStrategy(strategy) + scanStrategy.add(strategy) +end diff --git a/netNav/scanStrategy.lua b/netNav/scanStrategy.lua new file mode 100644 index 0000000..5918f4d --- /dev/null +++ b/netNav/scanStrategy.lua @@ -0,0 +1,84 @@ +local strategies = {} + +local function compareStrategies(a, b) + return a.priority > b.priority +end + +function add(strategy) + table.insert(strategies, strategy) + table.sort(strategies, compareStrategies) +end + +function getBest() + for _, strategy in ipairs(strategies) do + if strategy.isAvailable() then + return strategy + end + end + return false +end + +--===== SET UP VANILLA SCAN STRATEGY =====-- +local function detect(currPos, adjPos) + local heading = location.headingFromDelta(adjPos - currPos) + if heading then + currPos:setHeading(heading) + if heading == 4 then + return turtle.detectUp() + elseif heading == 5 then + return turtle.detectDown() + else + return turtle.detect() + end + end + return false +end + +local vanillaScanStrategy = { + priority = 0, -- lowest priority + isAvailable = function() return true end, -- always available + execute = function(currentPosition, updateSessionMap, updateServerMap) + for _, pos in ipairs(aStar.adjacent(currentPosition)) do -- find better order of checking directions + local isBlocked = detect(currentPosition, pos) + updateSessionMap(pos, isBlocked) + updateServerMap(pos, isBlocked) + end + end, +} +add(vanillaScanStrategy) + +--===== LOAD STRATEGIES FROM NET NAV STRATEGY FOLDER +local function loadStrategy(path) + local name = fs.getName(path) + if name:sub(-4) == ".lua" then + name = name:sub(1, -5) + end + + local tEnv = {} + setmetatable(tEnv, {__index = _G}) + local fnAPI, err = loadfile(path, tEnv) + if fnAPI then + local ok, err = pcall(fnAPI) + if not ok then + return printError("Failed to load strategy " .. name .. " due to " .. err, 1) + end + else + return printError("Failed to load strategy " .. name .. " due to " .. err, 1) + end + + if type(tEnv.new) == "function" then + local strategy = tEnv.new() + add(strategy) + else + return printError("Failed to find constructor for strategy " .. name) + end +end + +local STRATEGY_FOLDER = "netNavScanStrategies" + +if fs.exists(STRATEGY_FOLDER) and fs.isDir(STRATEGY_FOLDER) then + for _, file in ipairs(fs.list(STRATEGY_FOLDER)) do + local path = fs.combine(STRATEGY_FOLDER, file) + loadStrategy(path) + end +end