Advertisement
melzneni

Light GPS

Sep 16th, 2023 (edited)
1,046
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 31.44 KB | None | 0 0
  1. local pStorage
  2. local pSave
  3.  
  4. local api = {}
  5. local NAN = 0 / 0
  6. local INDEX_DIMENSIONS = 1
  7. local INDEX_ID = 2
  8. local INDEX_ADJACENT = 3
  9. local INDEX_CHILDREN = 4
  10. local INDEX_PARENT = 5
  11. local INDEX_IS_LEAF = 6
  12. local NODE_INDEX_POS = 1
  13. local NODE_INDEX_CUBOID = 2
  14. local currentDirectionPrivate
  15. local turtle_unhooked_turnLeft
  16. local turtle_unhooked_turnRight
  17. local logCuboids
  18. local DODGE_PROTOCOL = "TURTI_LIGHT_GPS_DODGE_PROTOCOL"
  19.  
  20. -- region utility
  21. local function isNan(v)
  22.     return v ~= v
  23. end
  24.  
  25. local function posEquals(p1, p2)
  26.     return p1[1] == p2[1] and p1[2] == p2[2] and p1[3] == p2[3]
  27. end
  28.  
  29. local function removeValue(tbl, value)
  30.     for i, v in pairs(tbl) do
  31.         if v == value then
  32.             table.remove(tbl, i)
  33.             return
  34.         end
  35.     end
  36. end
  37.  
  38. local function isValueInRange(v, min, max)
  39.     return (min ~= min or min <= v) and
  40.             (max ~= max or v < max)
  41. end
  42.  
  43. local function putValueInRange(v, min, max)
  44.     if not isNan(min) and v < min then
  45.         return min
  46.     elseif not isNan(max) and v >= max then
  47.         return max - 1
  48.     else
  49.         return v
  50.     end
  51. end
  52.  
  53. local function rangesOverlap(p1x1, p1x2, p2x1, p2x2)
  54.     if isNan(p1x1) and isNan(p1x2) then
  55.         return true
  56.     elseif isNan(p2x1) and isNan(p2x2) then
  57.         return true
  58.     elseif isNan(p1x1) and isNan(p2x1) then
  59.         return true
  60.     elseif isNan(p1x2) and isNan(p2x2) then
  61.         return true
  62.     end
  63.  
  64.     if isNan(p1x1) then
  65.         p1x1 = p2x1
  66.     elseif isNan(p2x1) then
  67.         p2x1 = p1x1
  68.     end
  69.     if isNan(p1x2) then
  70.         p1x2 = p2x2
  71.     elseif isNan(p2x2) then
  72.         p2x2 = p1x2
  73.     end
  74.  
  75.     return math.max(p1x2, p2x2) - math.min(p1x1, p2x1) < p2x2 - p2x1 + p1x2 - p1x1
  76. end
  77.  
  78. local function isPointInCuboid(pos, cuboid)
  79.     local dimensions = cuboid[INDEX_DIMENSIONS]
  80.     return isValueInRange(pos[1], dimensions[1], dimensions[4]) and
  81.             isValueInRange(pos[2], dimensions[2], dimensions[5]) and
  82.             isValueInRange(pos[3], dimensions[3], dimensions[6])
  83. end
  84.  
  85. local function isEmptyCuboid(cuboid)
  86.     local dimensions = cuboid[INDEX_DIMENSIONS]
  87.     return dimensions[1] == dimensions[4] or dimensions[2] == dimensions[5] or dimensions[3] == dimensions[6]
  88. end
  89.  
  90. local function normalizeDirection(direction)
  91.     while direction < 0 do
  92.         direction = direction + 4
  93.     end
  94.     return direction % 4
  95. end
  96.  
  97. local function getDirectionFromPoints(pStart, pEnd)
  98.     local x1, y1, z1 = table.unpack(pStart)
  99.     local x2, y2, z2 = table.unpack(pEnd)
  100.     if x1 ~= x2 then
  101.         if y1 == y2 and z1 == z2 then
  102.             if x1 < x2 then
  103.                 return 0
  104.             else
  105.                 return 2
  106.             end
  107.         end
  108.     elseif y1 ~= y2 then
  109.         if z1 == z2 then
  110.             if y1 < y2 then
  111.                 return 4
  112.             else
  113.                 return 5
  114.             end
  115.         end
  116.     elseif z1 ~= z2 then
  117.         if z1 < z2 then
  118.             return 1
  119.         else
  120.             return 3
  121.         end
  122.     end
  123.     printPoly(pStart[1], pStart[2], pStart[3], ",", pEnd[1], pEnd[2], pEnd[3])
  124.     error("cannot determine direction from points")
  125. end
  126.  
  127. local function getPosInDirection(pos, direction, count)
  128.     if not count then
  129.         count = 1
  130.     end
  131.     if direction == 0 then
  132.         return { pos[1] + count, pos[2], pos[3] }
  133.     elseif direction == 1 then
  134.         return { pos[1], pos[2], pos[3] + count }
  135.     elseif direction == 2 then
  136.         return { pos[1] - count, pos[2], pos[3] }
  137.     elseif direction == 3 then
  138.         return { pos[1], pos[2], pos[3] - count }
  139.     elseif direction == 4 then
  140.         return { pos[1], pos[2] + count, pos[3] }
  141.     elseif direction == 5 then
  142.         return { pos[1], pos[2] - count, pos[3] }
  143.     end
  144. end
  145.  
  146. local function arePointsInLine(p1, p2)
  147.     local num = 0
  148.     if p1[1] == p2[1] then
  149.         num = num + 1
  150.     end
  151.     if p1[2] == p2[2] then
  152.         num = num + 1
  153.     end
  154.     if p1[3] == p2[3] then
  155.         num = num + 1
  156.     end
  157.     return num == 2
  158. end
  159.  
  160. local function arePointsInSurface(p1, p2)
  161.     local num = 0
  162.     if p1[1] == p2[1] then
  163.         num = num + 1
  164.     end
  165.     if p1[2] == p2[2] then
  166.         num = num + 1
  167.     end
  168.     if p1[3] == p2[3] then
  169.         num = num + 1
  170.     end
  171.     return num == 1
  172. end
  173.  
  174. -- endregion
  175.  
  176. local function resetCuboids()
  177.     print("reset navigation data!")
  178.     pStorage.currentCuboidIndex = 2
  179.     pStorage.cuboids = {}
  180.     -- dimensions, id in cuboids, adjacent cuboids, children cuboids, parent cuboid, is leaf
  181.     local infiniteCuboid = {}
  182.     infiniteCuboid[INDEX_DIMENSIONS] = { NAN, NAN, NAN, NAN, NAN, NAN }
  183.     infiniteCuboid[INDEX_ID] = 1
  184.     infiniteCuboid[INDEX_CHILDREN] = {}
  185.     infiniteCuboid[INDEX_ADJACENT] = {}
  186.     infiniteCuboid[INDEX_IS_LEAF] = true
  187.     pStorage.cuboids[1] = infiniteCuboid
  188. end
  189.  
  190. local function getCuboid(id)
  191.     return pStorage.cuboids[id]
  192. end
  193.  
  194. local function findCuboidContainingPos(pos, suppressError)
  195.     local currentCuboid = pStorage.cuboids[1]
  196.     while not currentCuboid[INDEX_IS_LEAF] do
  197.         local targetChild
  198.         for _, childId in pairs(currentCuboid[INDEX_CHILDREN]) do
  199.             local child = getCuboid(childId)
  200.             if isPointInCuboid(pos, child) then
  201.                 targetChild = child
  202.                 break
  203.             end
  204.         end
  205.         if targetChild == nil then
  206.             if suppressError then
  207.                 return nil
  208.             end
  209.             print("corrupted cuboid data, resetting storage")
  210.             resetCuboids()
  211.             return nil
  212.         end
  213.         currentCuboid = targetChild
  214.     end
  215.     return currentCuboid
  216. end
  217.  
  218. local function logCuboidStructure(cuboid, prefix)
  219.     local dimensions = cuboid[INDEX_DIMENSIONS]
  220.     printPoly(prefix, dimensions[1], dimensions[2], dimensions[3],
  221.             dimensions[4], dimensions[5], dimensions[6])
  222.  
  223.     for _, id in pairs(cuboid[INDEX_CHILDREN]) do
  224.         logCuboidStructure(getCuboid(id), prefix .. " ")
  225.     end
  226. end
  227.  
  228. function logCuboids()
  229.     logCuboidStructure(pStorage.cuboids[1], "")
  230. end
  231.  
  232. local function registerCuboid(cuboid)
  233.     if cuboid[INDEX_ID] ~= nil then
  234.         return
  235.     end
  236.     local index = pStorage.currentCuboidIndex
  237.     pStorage.currentCuboidIndex = index + 1
  238.     pStorage.cuboids[index] = cuboid
  239.     cuboid[INDEX_ID] = index
  240. end
  241.  
  242. local function areCuboidsAdjacent(c1, c2)
  243.     local dim1 = c1[INDEX_DIMENSIONS]
  244.     local dim2 = c2[INDEX_DIMENSIONS]
  245.     local sum1 = 0
  246.     local sum2 = 0
  247.     if rangesOverlap(dim1[1], dim1[4], dim2[1], dim2[4]) then
  248.         sum1 = sum1 + 1
  249.     elseif dim1[1] == dim2[4] or dim1[4] == dim2[1] then
  250.         sum2 = sum2 + 1
  251.     end
  252.     if rangesOverlap(dim1[2], dim1[5], dim2[2], dim2[5]) then
  253.         sum1 = sum1 + 1
  254.     elseif dim1[2] == dim2[5] or dim1[5] == dim2[2] then
  255.         sum2 = sum2 + 1
  256.     end
  257.     if rangesOverlap(dim1[3], dim1[6], dim2[3], dim2[6]) then
  258.         sum1 = sum1 + 1
  259.     elseif dim1[3] == dim2[6] or dim1[6] == dim2[3] then
  260.         sum2 = sum2 + 1
  261.     end
  262.     if sum1 > 2 then
  263.         printTable(dim1)
  264.         printTable(dim2)
  265.         error("impossible case " .. sum1 .. ", " .. sum2)
  266.     end
  267.     return sum1 == 2 and sum2 == 1
  268. end
  269.  
  270. -- region transform cuboids
  271.  
  272. local function splitCuboidAtPos(cuboid, pos)
  273.     local x1, y1, z1, x2, y2, z2 = table.unpack(cuboid[INDEX_DIMENSIONS])
  274.     local pX, pY, pZ = table.unpack(pos)
  275.  
  276.     local cuboidYM = { x1, y1, z1, x2, pY, z2 }
  277.     local cuboidYP = { x1, pY + 1, z1, x2, y2, z2 }
  278.     local cuboidXM = { x1, pY, z1, pX, pY + 1, z2 }
  279.     local cuboidXP = { pX + 1, pY, z1, x2, pY + 1, z2 }
  280.     local cuboidZM = { pX, pY, z1, pX + 1, pY + 1, pZ }
  281.     local cuboidZP = { pX, pY, pZ + 1, pX + 1, pY + 1, z2 }
  282.     local allCuboids = { cuboidYM, cuboidYP, cuboidXM, cuboidXP, cuboidZM, cuboidZP }
  283.  
  284.     local cuboids = {}
  285.     for _, dimensions in pairs(allCuboids) do
  286.         local newCuboid = {}
  287.         newCuboid[INDEX_IS_LEAF] = true
  288.         newCuboid[INDEX_ADJACENT] = {}
  289.         newCuboid[INDEX_CHILDREN] = {}
  290.         newCuboid[INDEX_DIMENSIONS] = dimensions
  291.         if not isEmptyCuboid(newCuboid) then
  292.             table.insert(cuboids, newCuboid)
  293.         end
  294.     end
  295.  
  296.     return cuboids
  297. end
  298.  
  299. local function markAsObstructed(pos)
  300.     local cuboid = findCuboidContainingPos(pos)
  301.     if cuboid == nil then
  302.         return
  303.     end
  304.     local newCuboids = splitCuboidAtPos(cuboid, pos)
  305.  
  306.     if not cuboid[INDEX_IS_LEAF] then
  307.         error("corrupted cuboid data (can't split non-leaf node)")
  308.     end
  309.  
  310.     -- register new cuboids
  311.     for _, c in pairs(newCuboids) do
  312.         registerCuboid(c)
  313.         c[INDEX_IS_LEAF] = true
  314.         c[INDEX_PARENT] = cuboid[INDEX_ID]
  315.         table.insert(cuboid[INDEX_CHILDREN], c[INDEX_ID])
  316.     end
  317.     cuboid[INDEX_IS_LEAF] = false
  318.  
  319.     -- add adjacent relations between child cuboids
  320.     for _, newCuboid in pairs(newCuboids) do
  321.         for _, otherNewCuboid in pairs(newCuboids) do
  322.             if otherNewCuboid[INDEX_ID] ~= newCuboid[INDEX_ID] then
  323.                 if areCuboidsAdjacent(newCuboid, otherNewCuboid) then
  324.                     table.insert(newCuboid[INDEX_ADJACENT], otherNewCuboid[INDEX_ID])
  325.                     table.insert(otherNewCuboid[INDEX_ADJACENT], newCuboid[INDEX_ID])
  326.                 end
  327.             end
  328.         end
  329.     end
  330.  
  331.     -- remove adjacent relations of parent cuboid and add new adjacent relations for child cuboids
  332.     for _, adjacentCuboidId in pairs(cuboid[INDEX_ADJACENT]) do
  333.         local adjacentCuboid = getCuboid(adjacentCuboidId)
  334.         removeValue(adjacentCuboid[INDEX_ADJACENT], cuboid[INDEX_ID])
  335.         for _, newCuboid in pairs(newCuboids) do
  336.             if areCuboidsAdjacent(newCuboid, adjacentCuboid) then
  337.                 table.insert(newCuboid[INDEX_ADJACENT], adjacentCuboid[INDEX_ID])
  338.                 table.insert(adjacentCuboid[INDEX_ADJACENT], newCuboid[INDEX_ID])
  339.             end
  340.         end
  341.     end
  342. end
  343.  
  344. -- endregion
  345.  
  346. -- region A*
  347.  
  348. local function getPointDistance(p1, p2)
  349.     return math.abs(p2[1] - p1[1]) + math.abs(p2[2] - p1[2]) + math.abs(p2[3] - p1[3])
  350. end
  351.  
  352. local function getClosestPositionInCuboid(pos, cuboid)
  353.     local dim = cuboid[INDEX_DIMENSIONS]
  354.     return { putValueInRange(pos[1], dim[1], dim[4]), putValueInRange(pos[2], dim[2], dim[5]), putValueInRange(pos[3], dim[3], dim[6]) }
  355. end
  356.  
  357. local function nodeToIdentifier(node)
  358.     local pos = node[NODE_INDEX_POS]
  359.     local cuboid = node[NODE_INDEX_CUBOID]
  360.     return pos[1] .. "_" .. pos[2] .. "_" .. pos[3] .. "_" .. cuboid[INDEX_ID]
  361. end
  362.  
  363. local function getSuccessorNodes(currentNode, destinationNode)
  364.     local pos = currentNode[NODE_INDEX_POS]
  365.     local cuboid = currentNode[NODE_INDEX_CUBOID]
  366.     local successors = {}
  367.     for _, adjacentCuboidId in ipairs(cuboid[INDEX_ADJACENT]) do
  368.         local adjacentCuboid = getCuboid(adjacentCuboidId)
  369.         if adjacentCuboid~=nil then
  370.             local successor = {}
  371.             successor[NODE_INDEX_POS] = getClosestPositionInCuboid(pos, adjacentCuboid)
  372.             successor[NODE_INDEX_CUBOID] = adjacentCuboid
  373.  
  374.             table.insert(successors, successor)
  375.             if adjacentCuboid[INDEX_ID] == destinationNode[NODE_INDEX_CUBOID][INDEX_ID] then
  376.                 table.insert(successors, destinationNode)
  377.             end
  378.         end
  379.     end
  380.     return successors
  381. end
  382.  
  383. local function aStarExpandNode(currentEntry, openNodesQueue, openNodes, closedNodes, destinationNode)
  384.     for _, successorNode in ipairs(getSuccessorNodes(currentEntry.node, destinationNode)) do
  385.         local identifier = nodeToIdentifier(successorNode)
  386.         if not closedNodes[identifier] then
  387.             local g = currentEntry.g + getPointDistance(currentEntry.node[NODE_INDEX_POS], successorNode[NODE_INDEX_POS])
  388.             if openNodes[identifier] and g > openNodes[identifier].g then
  389.  
  390.             else
  391.                 local f = g + getPointDistance(successorNode[NODE_INDEX_POS], destinationNode[NODE_INDEX_POS])
  392.                 if openNodes[identifier] then
  393.                     local entry = openNodes[identifier]
  394.                     entry.g = g
  395.                     entry.f = f
  396.                     entry.pre = currentEntry
  397.                 else
  398.                     local entry = { node = successorNode, identifier = identifier, f = f, g = g, pre = currentEntry }
  399.                     table.insert(openNodesQueue, entry)
  400.                     openNodes[identifier] = entry
  401.                 end
  402.             end
  403.         end
  404.     end
  405. end
  406.  
  407. local function addIntermediatePathPoints(pos1, pos2, path)
  408.     if arePointsInSurface(pos1, pos2) then
  409.         local intermediatePos = { pos1[1], pos1[2], pos1[3] }
  410.         for i1 = 1, 3 do
  411.             if pos1[i1] ~= pos2[i1] then
  412.                 intermediatePos[i1] = pos2[i1]
  413.                 break
  414.             end
  415.         end
  416.         table.insert(path, intermediatePos)
  417.     elseif not arePointsInLine(pos1, pos2) then
  418.         table.insert(path, { pos2[1], pos1[2], pos1[3] })
  419.         table.insert(path, { pos2[1], pos2[2], pos1[3] })
  420.     end
  421. end
  422.  
  423. local function getPathFromNode(node)
  424.     local nodes = {}
  425.     while node do
  426.         table.insert(nodes, 1, node)
  427.         node = node.pre
  428.     end
  429.     local path = { nodes[1].node[NODE_INDEX_POS] }
  430.     for i = 2, #nodes do
  431.         local node1 = nodes[i - 1]
  432.         local node2 = nodes[i]
  433.         local pos1 = node1.node[NODE_INDEX_POS]
  434.         local pos2 = node2.node[NODE_INDEX_POS]
  435.         local cuboid1 = node1.node[NODE_INDEX_CUBOID]
  436.         local cuboid2 = node2.node[NODE_INDEX_CUBOID]
  437.  
  438.         if arePointsInLine(pos1, pos2) then
  439.             table.insert(path, pos2)
  440.         elseif cuboid1 == cuboid2 then
  441.             addIntermediatePathPoints(pos1, pos2, path)
  442.             table.insert(path, pos2)
  443.         else
  444.             local closestInC1 = getClosestPositionInCuboid(pos2, cuboid1)
  445.             if not posEquals(pos1, closestInC1) then
  446.                 addIntermediatePathPoints(pos1, closestInC1, path)
  447.                 table.insert(path, closestInC1)
  448.             end
  449.             local closestInC2 = getClosestPositionInCuboid(closestInC1, cuboid2)
  450.             addIntermediatePathPoints(closestInC1, closestInC2, path)
  451.             table.insert(path, closestInC2)
  452.             if not posEquals(closestInC2, pos2) then
  453.                 addIntermediatePathPoints(closestInC2, pos2, path)
  454.                 table.insert(path, pos2)
  455.             end
  456.         end
  457.     end
  458.     return path
  459. end
  460.  
  461. local function getPathFromPointsWithinCuboid(pos1, pos2)
  462.     local path = { pos1 }
  463.     addIntermediatePathPoints(pos1, pos2, path)
  464.     table.insert(path, pos2)
  465.     return path
  466. end
  467.  
  468. local function findPath(start, destination)
  469.     local startNode = { }
  470.     startNode[NODE_INDEX_POS] = start
  471.     startNode[NODE_INDEX_CUBOID] = findCuboidContainingPos(start)
  472.     if startNode[NODE_INDEX_CUBOID] == nil then
  473.         return nil
  474.     end
  475.     local destinationNode = { }
  476.     destinationNode[NODE_INDEX_POS] = destination
  477.     local destinationCuboid = findCuboidContainingPos(destination, true)
  478.     if destinationCuboid == nil then
  479.         -- destination obstructed
  480.         return nil
  481.     end
  482.     destinationNode[NODE_INDEX_CUBOID] = destinationCuboid
  483.  
  484.     if startNode[NODE_INDEX_CUBOID][INDEX_ID] == destinationNode[NODE_INDEX_CUBOID][INDEX_ID] then
  485.         return getPathFromPointsWithinCuboid(startNode[NODE_INDEX_POS], destinationNode[NODE_INDEX_POS])
  486.     end
  487.  
  488.     local openNodesQueue = {}
  489.     local openNodes = {}
  490.     local closedNodes = {}
  491.     table.insert(openNodesQueue, {
  492.         node = startNode,
  493.         identifier = nodeToIdentifier(startNode),
  494.         f = 0, g = 0, pre = nil
  495.     })
  496.     openNodes[openNodesQueue[1].identifier] = openNodesQueue[1]
  497.  
  498.     while true do
  499.         table.sort(openNodesQueue, function(a, b)
  500.             return a.f > b.f
  501.         end)
  502.         if #openNodesQueue > 10000 then
  503.             return nil
  504.         end
  505.         yield()
  506.         local currentNode = table.remove(openNodesQueue)
  507.  
  508.         if not currentNode then
  509.             return nil
  510.         end
  511.         if posEquals(currentNode.node[NODE_INDEX_POS], destination) then
  512.             return getPathFromNode(currentNode)
  513.         end
  514.  
  515.         closedNodes[currentNode.identifier] = true
  516.  
  517.         aStarExpandNode(currentNode, openNodesQueue, openNodes, closedNodes, destinationNode)
  518.     end
  519. end
  520.  
  521. local function findPathToAny(start, destinations)
  522.     local startNode = {}
  523.     startNode[NODE_INDEX_POS] = start
  524.     startNode[NODE_INDEX_CUBOID] = findCuboidContainingPos(start)
  525.     if startNode[NODE_INDEX_CUBOID] == nil then
  526.         return nil
  527.     end
  528.  
  529.     local destinationNodes = {}
  530.     for i, destination in ipairs(destinations) do
  531.         local destinationNode = {}
  532.         destinationNode[NODE_INDEX_POS] = destination
  533.         destinationNode[NODE_INDEX_CUBOID] = findCuboidContainingPos(destination, true)
  534.  
  535.         if destinationNode[NODE_INDEX_CUBOID] ~= nil then
  536.             table.insert(destinationNodes, destinationNode)
  537.             if startNode[NODE_INDEX_CUBOID][INDEX_ID] == destinationNode[NODE_INDEX_CUBOID][INDEX_ID] then
  538.                 return getPathFromPointsWithinCuboid(startNode[NODE_INDEX_POS], destinationNode[NODE_INDEX_POS]), i
  539.             end
  540.         end
  541.     end
  542.     if #destinationNodes == 0 then
  543.         return nil
  544.     end
  545.     local allOpenNodesQueues = {}
  546.     local allOpenNodes = {}
  547.     local allClosedNodes = {}
  548.     local activeRoutes = {}
  549.     for _, _ in ipairs(destinationNodes) do
  550.         local startEntry = {
  551.             node = startNode,
  552.             identifier = nodeToIdentifier(startNode),
  553.             f = 0, g = 0, pre = nil
  554.         }
  555.         local openNodes = {}
  556.         openNodes[startEntry.identifier] = startEntry
  557.         table.insert(allOpenNodesQueues, { startEntry })
  558.         table.insert(allOpenNodes, openNodes)
  559.         table.insert(allClosedNodes, {})
  560.         table.insert(activeRoutes, true)
  561.     end
  562.  
  563.     while true do
  564.         local anyActive = false
  565.         for i = 1, #activeRoutes do
  566.             if activeRoutes[i] then
  567.                 anyActive = true
  568.                 local openNodesQueue = allOpenNodesQueues[i]
  569.                 local openNodes = allOpenNodes[i]
  570.                 local closedNodes = allClosedNodes[i]
  571.                 table.sort(openNodesQueue, function(a, b)
  572.                     return a.f > b.f
  573.                 end)
  574.                 yield()
  575.                 if #openNodesQueue > 10000 then
  576.                     activeRoutes[i] = false
  577.                 else
  578.                     local currentNode = table.remove(openNodesQueue)
  579.                     if not currentNode then
  580.                         activeRoutes[i] = false
  581.                     else
  582.                         if posEquals(currentNode.node[NODE_INDEX_POS], destinations[i]) then
  583.                             return getPathFromNode(currentNode), i
  584.                         end
  585.                         closedNodes[currentNode.identifier] = true
  586.  
  587.                         aStarExpandNode(currentNode, openNodesQueue, openNodes, closedNodes, destinationNodes[i])
  588.                     end
  589.                 end
  590.             end
  591.         end
  592.         if not anyActive then
  593.             return nil
  594.         end
  595.     end
  596. end
  597.  
  598. -- endregion
  599.  
  600. local function getDirection()
  601.     if currentDirectionPrivate then
  602.         return currentDirectionPrivate
  603.     end
  604.     local directionShift = 0
  605.     local currentPos = gpsAdapter.locate()
  606.     local currentY = 0
  607.     for dir = 1, 2 do
  608.         local fUp = turtle.up
  609.         local fDown = turtle.down
  610.         if dir == 2 then
  611.             fUp = turtle.down
  612.             fDown = turtle.up
  613.         end
  614.  
  615.         for _ = 1, 256 do
  616.             for _ = 1, 4 do
  617.                 if turtle.forward() then
  618.                     local newPos = gpsAdapter.locate()
  619.                     currentDirectionPrivate = getDirectionFromPoints(
  620.                             { currentPos[1], newPos[2], currentPos[3] }, newPos)
  621.                     turtle.back()
  622.                     break
  623.                 end
  624.                 turtle_unhooked_turnLeft()
  625.                 directionShift = directionShift + 1
  626.             end
  627.             if currentDirectionPrivate then
  628.                 break
  629.             end
  630.             if fUp() then
  631.                 currentY = currentY + 1
  632.             else
  633.                 break
  634.             end
  635.         end
  636.         while currentY > 0 do
  637.             fDown()
  638.             currentY = currentY - 1
  639.         end
  640.         if currentDirectionPrivate then
  641.             for _ = 1, directionShift do
  642.                 turtle.turnRight()
  643.             end
  644.             return currentDirectionPrivate
  645.         end
  646.     end
  647.     if not currentDirectionPrivate then
  648.         error("turtle is trapped!")
  649.     end
  650. end
  651.  
  652. local function turnTowardDirection(direction)
  653.     if direction == 4 or direction == 5 then
  654.         return
  655.     end
  656.     direction = normalizeDirection(direction)
  657.     local turnLeftDirection = normalizeDirection(getDirection() + 3)
  658.     if turnLeftDirection == direction then
  659.         turtle.turnLeft()
  660.     end
  661.     while getDirection() ~= direction do
  662.         turtle.turnRight()
  663.     end
  664. end
  665.  
  666. local function moveInDirection(direction, count)
  667.     if count == 0 then
  668.         return 0, nil
  669.     end
  670.     if direction == 4 then
  671.         for i = 1, count do
  672.             if turtle.inspectUp() then
  673.                 return i - 1
  674.             end
  675.             if not turtle.up() then
  676.                 return i - 1
  677.             end
  678.         end
  679.     elseif direction == 5 then
  680.         for i = 1, count do
  681.             if turtle.inspectDown() then
  682.                 return i - 1
  683.             end
  684.             if not turtle.down() then
  685.                 return i - 1
  686.             end
  687.         end
  688.     else
  689.         turnTowardDirection(direction)
  690.         for i = 1, count do
  691.             if turtle.inspect() then
  692.                 return i - 1
  693.             end
  694.             if not turtle.forward() then
  695.                 return i - 1
  696.             end
  697.         end
  698.     end
  699.     return count
  700. end
  701.  
  702. local function getReverseDirection(direction)
  703.     while direction < 6 do
  704.         direction = direction + 6
  705.     end
  706.     direction = direction % 6
  707.     if direction < 4 then
  708.         return (direction + 2) % 4
  709.     elseif direction == 4 then
  710.         return 5
  711.     else
  712.         return 4
  713.     end
  714. end
  715.  
  716. local function receiveRednet(idFilter, msgFilter, protocolFilter, timeout)
  717.     print("waiting on ", idFilter, msgFilter, protocolFilter, timeout)
  718.     local start = os.clock()
  719.     local stop = os.clock() + timeout
  720.     while stop > start do
  721.         local id, msg = rednet.receive(protocolFilter, stop - start)
  722.         if id == idFilter and msg == msgFilter then
  723.             print("received!")
  724.             return id
  725.         end
  726.         start = os.clock()
  727.     end
  728.     print("not received")
  729.     return nil
  730. end
  731.  
  732. local function dodgeProtocol()
  733.     local opponent = peripheral.wrap("front")
  734.     if not opponent then
  735.         return
  736.     end
  737.     local opponentId = opponent.getID()
  738.     local ownId = os.getComputerID()
  739.     if opponentId < ownId then
  740.         local lastDirection = (getDirection() + 2) % 4
  741.         for originalDirection = 0, 5 do
  742.             local direction = originalDirection
  743.             if originalDirection == lastDirection then
  744.                 direction = 5
  745.             elseif originalDirection == 5 then
  746.                 direction = lastDirection
  747.             end
  748.  
  749.             local moved = moveInDirection(direction, 1)
  750.             if moved > 0 then
  751.                 sleep(2)
  752.                 local movedBack = moveInDirection(getReverseDirection(direction), 1)
  753.                 turnTowardDirection(lastDirection)
  754.                 if movedBack then
  755.                     return true
  756.                 end
  757.             end
  758.         end
  759.         return false
  760.     else
  761.         sleep(1)
  762.         return true
  763.     end
  764. end
  765.  
  766. local function followPath(path, filterMethod)
  767.     for i = 2, #path do
  768.         local pStart = path[i - 1]
  769.         local pEnd = path[i]
  770.         local steps = math.max(math.abs(pEnd[1] - pStart[1]), math.abs(pEnd[2] - pStart[2]), math.abs(pEnd[3] - pStart[3]))
  771.         if steps ~= 0 then
  772.             local direction = getDirectionFromPoints(pStart, pEnd)
  773.             local toMove = steps
  774.             local try = 0
  775.             while toMove > 0 do
  776.                 local moved = moveInDirection(direction, toMove)
  777.                 if moved > 0 then
  778.                     try = 0
  779.                 end
  780.                 if moved ~= toMove then
  781.                     local isBlock = false
  782.                     local inspectResult
  783.                     if direction == 4 then
  784.                         isBlock, inspectResult = turtle.inspectUp()
  785.                     elseif direction == 5 then
  786.                         isBlock, inspectResult = turtle.inspectDown()
  787.                     else
  788.                         isBlock, inspectResult = turtle.inspect()
  789.                     end
  790.                     if inspectResult then
  791.                         if inspectResult.name and inspectResult.name:sub(1, 21) == "computercraft:turtle_" and try < 10 then
  792.                             print("dodging (attempt "..tostring(try+1).."/10)")
  793.                             if not dodgeProtocol() then
  794.                                 return false, true
  795.                             end
  796.                         elseif filterMethod then
  797.                             local filterResult = invokeHook(filterMethod.name, inspectResult.name, direction)
  798.                             if filterResult then
  799.                                 if direction == 4 then
  800.                                     turtle.digUp()
  801.                                 elseif direction == 5 then
  802.                                     turtle.digDown()
  803.                                 else
  804.                                     turtle.dig()
  805.                                 end
  806.                             else
  807.                                 markAsObstructed(getPosInDirection(gpsAdapter.locate(), direction))
  808.                                 return false, isBlock
  809.                             end
  810.                         else
  811.                             markAsObstructed(getPosInDirection(gpsAdapter.locate(), direction))
  812.                             return false, isBlock
  813.                         end
  814.                     end
  815.                 end
  816.                 try = try + 1
  817.                 toMove = toMove - moved
  818.             end
  819.         end
  820.     end
  821.     return true
  822. end
  823.  
  824. local function moveFwd(steps)
  825.     for _ = 1, steps do
  826.         while not turtle.forward() do
  827.             if not turtle.dig() then
  828.                 turtle.attack()
  829.             end
  830.         end
  831.     end
  832. end
  833.  
  834. local function moveUp(steps)
  835.     for _ = 1, steps do
  836.         while not turtle.up() do
  837.             if not turtle.digUp() then
  838.                 turtle.attackUp()
  839.             end
  840.         end
  841.     end
  842. end
  843.  
  844. local function moveDown(steps)
  845.     for _ = 1, steps do
  846.         while not turtle.down() do
  847.             if not turtle.digDown() then
  848.                 turtle.attackDown()
  849.             end
  850.         end
  851.     end
  852. end
  853.  
  854. api.moveToRuthless = {
  855.     path = { 1, 2 },
  856.     fct = function(destination, direction)
  857.         local start = gpsAdapter.locate()
  858.  
  859.         local diffX = destination[1] - start[1]
  860.         local diffY = destination[2] - start[2]
  861.         local diffZ = destination[3] - start[3]
  862.  
  863.         if diffX < 0 then
  864.             turnTowardDirection(2)
  865.             moveFwd(-diffX)
  866.         elseif diffX > 0 then
  867.             turnTowardDirection(0)
  868.             moveFwd(diffX)
  869.         end
  870.  
  871.         if diffY > 0 then
  872.             moveUp(diffY)
  873.         elseif diffY < 0 then
  874.             moveDown(-diffY)
  875.         end
  876.  
  877.         if diffZ < 0 then
  878.             turnTowardDirection(3)
  879.             moveFwd(-diffZ)
  880.         elseif diffZ > 0 then
  881.             turnTowardDirection(1)
  882.             moveFwd(diffZ)
  883.         end
  884.  
  885.         if direction ~= nil then
  886.             turnTowardDirection(direction)
  887.         end
  888.     end
  889. }
  890.  
  891. api.moveTo = {
  892.     pars = { 1, 3 },
  893.     fct = function(pos, direction, filterMethod)
  894.         if filterMethod then
  895.             resetCuboids()
  896.         end
  897.         local path
  898.  
  899.         local cleared = false
  900.         local savedLast = os.clock()
  901.         while true do
  902.             local success = false
  903.             if path then
  904.                 success = followPath(path, filterMethod)
  905.             end
  906.             if success then
  907.                 break
  908.             end
  909.             path = findPath(gpsAdapter.locate(), pos)
  910.             while not path do
  911.                 if not cleared then
  912.                     resetCuboids()
  913.                     path = findPath(gpsAdapter.locate(), pos)
  914.                     cleared = true
  915.                 else
  916.                     print("no path found, retrying in 5s")
  917.                     sleep(5)
  918.                     cleared = false
  919.                 end
  920.             end
  921.             if os.clock() - savedLast > 10 then
  922.                 pSave()
  923.                 savedLast = os.clock()
  924.             end
  925.         end
  926.         if direction ~= nil then
  927.             turnTowardDirection(direction)
  928.         end
  929.  
  930.         pSave()
  931.     end
  932. }
  933.  
  934. function api.locate()
  935.     return toTurtiArray(gpsAdapter.locate())
  936. end
  937.  
  938. function api.getDirection()
  939.     return getDirection()
  940. end
  941.  
  942. function api.setDirection(direction)
  943.     turnTowardDirection(direction)
  944. end
  945.  
  946. api.faceToward = {
  947.     pars = { 1, 2 },
  948.     fct = function(pos, filterMethod)
  949.         if filterMethod then
  950.             resetCuboids()
  951.         end
  952.         local possiblePositions = {
  953.             { pos[1] - 1, pos[2], pos[3] },
  954.             { pos[1], pos[2], pos[3] - 1 },
  955.             { pos[1] + 1, pos[2], pos[3] },
  956.             { pos[1], pos[2], pos[3] + 1 }
  957.         }
  958.  
  959.         local path
  960.         local index
  961.         local savedLast = os.clock()
  962.         while true do
  963.             local success = false
  964.             if path then
  965.                 success = followPath(path, filterMethod)
  966.             end
  967.             if success then
  968.                 turnTowardDirection(index - 1)
  969.                 break
  970.             end
  971.             path, index = findPathToAny(gpsAdapter.locate(), possiblePositions)
  972.             local tries = 0
  973.             while not path do
  974.                 if tries < 5 then
  975.                     if tries == 3 then
  976.                         resetCuboids()
  977.                     end
  978.                     path, index = findPathToAny(gpsAdapter.locate(), possiblePositions)
  979.                 else
  980.                     print("no path found, retrying in 5s")
  981.                     sleep(5)
  982.                 end
  983.                 tries = tries + 1
  984.             end
  985.             if os.clock() - savedLast > 10 then
  986.                 pSave()
  987.                 savedLast = os.clock()
  988.             end
  989.         end
  990.     end
  991. }
  992.  
  993. api.getPosInDirection = {
  994.     pars = { 2, 3 },
  995.     fct = function(pos, direction, count)
  996.         return toTurtiArray(getPosInDirection(pos, direction, count))
  997.     end
  998. }
  999.  
  1000. turtle_unhooked_turnLeft = installPreExecutionHook(turtle, "turnLeft", "smartGPS", function()
  1001.     if currentDirectionPrivate then
  1002.         currentDirectionPrivate = normalizeDirection(getDirection() - 1)
  1003.     end
  1004. end)
  1005.  
  1006. turtle_unhooked_turnRight = installPreExecutionHook(turtle, "turnRight", "smartGPS", function()
  1007.     if currentDirectionPrivate then
  1008.         currentDirectionPrivate = normalizeDirection(getDirection() + 1)
  1009.     end
  1010. end)
  1011.  
  1012. return {
  1013.     name = "lightGPS",
  1014.     api = api,
  1015.     onInitPersistentStorage = function(_pStorage, _pSave)
  1016.         pStorage = _pStorage
  1017.         if not pStorage.cuboids then
  1018.             resetCuboids()
  1019.         end
  1020.         pSave = _pSave
  1021.     end
  1022. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement