Advertisement
melzneni

TurtiLib smartGps

Aug 14th, 2023 (edited)
664
1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.75 KB | None | 1 0
  1. local pStorage
  2. local pSave
  3. local currentDirectionPrivate
  4.  
  5. local api = {}
  6.  
  7. turtle_unhooked_turnLeft = installPreExecutionHook(turtle, "turnLeft", "smartGPS", function()
  8.     if currentDirectionPrivate then
  9.         setCurrentDirection(getCurrentDirection() - 1)
  10.     end
  11. end)
  12.  
  13. turtle_unhooked_turnRight = installPreExecutionHook(turtle, "turnRight", "smartGPS", function()
  14.     if currentDirectionPrivate then
  15.         setCurrentDirection(getCurrentDirection() + 1)
  16.     end
  17. end)
  18.  
  19. -- based on https://de.wikipedia.org/wiki/A*-Algorithmus
  20. function api.moveTo(pos, direction)
  21.     local path = findPath(gpsAdapter.locate(), pos)
  22.     if not path then
  23.         error("no path found")
  24.     end
  25.     local cleared = false
  26.     while not followPath(path) do
  27.         path = findPath(gpsAdapter.locate(), pos)
  28.         while not path do
  29.             if not cleared then
  30.                 clearObstructedData()
  31.                 path = findPath(gpsAdapter.locate(), pos)
  32.                 cleared = true
  33.             else
  34.                 error("no path found")
  35.             end
  36.         end
  37.     end
  38.     turnTowardsDirection(direction)
  39.     pSave()
  40. end
  41.  
  42. function api.locate()
  43.     return toTurtiArray(gpsAdapter.locate())
  44. end
  45.  
  46. function api.getDirection()
  47.     return getCurrentDirection()
  48. end
  49.  
  50. function api.setDirection(direction)
  51.     turnTowardsDirection(direction)
  52. end
  53.  
  54. function faceTowardAny(positions)
  55.     local possiblePositions = {}
  56.     for i, pos in ipairs(positions) do
  57.         table.insert(possiblePositions, { pos[1] - 1, pos[2], pos[3] }) -- direction 0
  58.         table.insert(possiblePositions, { pos[1], pos[2], pos[3] - 1 }) -- direction 1
  59.         table.insert(possiblePositions, { pos[1] + 1, pos[2], pos[3] }) -- direction 2
  60.         table.insert(possiblePositions, { pos[1], pos[2], pos[3] + 1 }) -- direction 3
  61.     end
  62.  
  63.     local cleared = false
  64.  
  65.     while true do
  66.         local path, index = findPathToAny(gpsAdapter.locate(), possiblePositions)
  67.  
  68.         if path then
  69.             if followPath(path) then
  70.                 turnTowardsDirection((index - 1) % 4)
  71.                 return math.floor((index - 1) / 4)
  72.             end
  73.         else
  74.             if not cleared then
  75.                 clearObstructedData()
  76.                 cleared = true
  77.             else
  78.                 error("no path found")
  79.             end
  80.         end
  81.     end
  82. end
  83.  
  84. function api.faceToward(pos)
  85.     local possiblePositions = {
  86.         { pos[1] - 1, pos[2], pos[3] }, -- direction 0
  87.         { pos[1], pos[2], pos[3] - 1 }, -- direction 1
  88.         { pos[1] + 1, pos[2], pos[3] }, -- direction 2
  89.         { pos[1], pos[2], pos[3] + 1 }  -- direction 3
  90.     }
  91.  
  92.     while true do
  93.         local path, index = findPathToAny(gpsAdapter.locate(), possiblePositions)
  94.  
  95.         if path then
  96.             if followPath(path) then
  97.                 turnTowardsDirection(index - 1)
  98.                 return
  99.             end
  100.         else
  101.             if not cleared then
  102.                 clearObstructedData()
  103.                 cleared = true
  104.             else
  105.                 error("no path found")
  106.             end
  107.         end
  108.     end
  109. end
  110.  
  111. function api.faceTowardAny(...)
  112.     faceTowardAny({ ... })
  113. end
  114.  
  115. api.getPosInDirection = {
  116.     pars = { 2, 3 },
  117.     fct = function(pos, direction, count)
  118.         return toTurtiArray(getPosInDirection(pos, direction, count))
  119.     end
  120. }
  121.  
  122. function followPath(path)
  123.     for i = 2, #path do
  124.         local direction = getDirectionFromPoints(path[i - 1], path[i])
  125.         if not moveInDirection(direction) then
  126.             markAsObstructed(path[i])
  127.             return false
  128.         end
  129.         if isObstructed(getPosInDirection(path[i], direction)) and not turtle.detect() then
  130.             print("mark as not obstructed!")
  131.             markAsNotObstructed(getPosInDirection(path[i], direction))
  132.         end
  133.         if isObstructed(getPosInDirection(path[i], 4)) and not turtle.detectUp() then
  134.             print("mark as not obstructed!")
  135.             markAsNotObstructed(getPosInDirection(path[i], 4))
  136.         end
  137.         if isObstructed(getPosInDirection(path[i], 5)) and not turtle.detectDown() then
  138.             print("mark as not obstructed!")
  139.             markAsNotObstructed(getPosInDirection(path[i], 5))
  140.         end
  141.     end
  142.  
  143.     return true
  144. end
  145.  
  146. function getPosInDirection(pos, direction, count)
  147.     if count==nil then
  148.         count = 1
  149.     end
  150.     if direction == 0 then
  151.         return { pos[1] + count, pos[2], pos[3] }
  152.     elseif direction == 1 then
  153.         return { pos[1], pos[2], pos[3] + count }
  154.     elseif direction == 2 then
  155.         return { pos[1] - count, pos[2], pos[3] }
  156.     elseif direction == 3 then
  157.         return { pos[1], pos[2], pos[3] - count }
  158.     elseif direction == 4 then
  159.         return { pos[1], pos[2] + count, pos[3] }
  160.     elseif direction == 5 then
  161.         return { pos[1], pos[2] - count, pos[3] }
  162.     end
  163. end
  164.  
  165. function moveInDirection(direction)
  166.     if direction == 4 then
  167.         if turtle.up() then
  168.             return true
  169.         end
  170.     elseif direction == 5 then
  171.         if turtle.down() then
  172.             return true
  173.         end
  174.     else
  175.         turnTowardsDirection(direction)
  176.         if turtle.forward() then
  177.             return true
  178.         end
  179.     end
  180.  
  181.     return false
  182. end
  183.  
  184. function getCurrentDirection()
  185.     if currentDirectionPrivate then
  186.         return currentDirectionPrivate
  187.     end
  188.     local directionShift = 0
  189.     local currentPos = gpsAdapter.locate()
  190.     local testY = 3
  191.     local currentY = 0
  192.     for y = 1, testY do
  193.         for i = 0, 5 do
  194.             if turtle.forward() then
  195.                 currentDirectionPrivate = getDirectionFromPoints(currentPos, gpsAdapter.locate())
  196.                 turtle.back()
  197.                 break
  198.             end
  199.             turtle_unhooked_turnLeft()
  200.             directionShift = directionShift + 1
  201.         end
  202.         if currentDirectionPrivate then
  203.             break
  204.         end
  205.         if turtle.up() then
  206.             currentY = currentY + 1
  207.         else
  208.             break
  209.         end
  210.     end
  211.     while currentY > 0 do
  212.         turtle.down()
  213.         currentY = currentY - 1
  214.     end
  215.     if currentDirectionPrivate then
  216.         for _ = 1, directionShift do
  217.             turtle.turnRight()
  218.         end
  219.         return currentDirectionPrivate
  220.     end
  221.     for y = 1, testY do
  222.         for i = 0, 5 do
  223.             if turtle.forward() then
  224.                 currentDirectionPrivate = getDirectionFromPoints(currentPos, gpsAdapter.locate())
  225.                 turtle.back()
  226.                 break
  227.             end
  228.             turtle_unhooked_turnLeft()
  229.             directionShift = directionShift + 1
  230.         end
  231.         if currentDirectionPrivate then
  232.             break
  233.         end
  234.         if turtle.down() then
  235.             currentY = currentY - 1
  236.         else
  237.             break
  238.         end
  239.     end
  240.     while currentY < 0 do
  241.         turtle.up()
  242.         currentY = currentY + 1
  243.     end
  244.  
  245.     if not currentDirectionPrivate then
  246.         error("turtle is trapped!")
  247.     end
  248.     for _ = 1, directionShift do
  249.         turtle.turnRight()
  250.     end
  251.     return currentDirectionPrivate
  252. end
  253.  
  254. function setCurrentDirection(cD)
  255.     while cD > 3 do
  256.         cD = cD - 4
  257.     end
  258.     while cD < 0 do
  259.         cD = cD + 4
  260.     end
  261.     currentDirectionPrivate = cD
  262. end
  263.  
  264. function turnTowardsDirection(direction)
  265.     while direction > 3 do
  266.         direction = direction - 4
  267.     end
  268.     while direction < 0 do
  269.         direction = direction + 4
  270.     end
  271.     if direction == 4 or direction == 5 then
  272.         return
  273.     end
  274.     local turnLeftDirection = getCurrentDirection() + 3
  275.     if turnLeftDirection > 3 then
  276.         turnLeftDirection = turnLeftDirection - 4
  277.     end
  278.     if turnLeftDirection == direction then
  279.         turtle.turnLeft()
  280.     end
  281.  
  282.     while getCurrentDirection() ~= direction do
  283.         turtle.turnRight()
  284.     end
  285. end
  286.  
  287. function getDirectionFromPoints(pos1, pos2)
  288.     if pos2[1] == pos1[1] + 1 then
  289.         return 0
  290.     elseif pos2[3] == pos1[3] + 1 then
  291.         return 1
  292.     elseif pos2[1] == pos1[1] - 1 then
  293.         return 2
  294.     elseif pos2[3] == pos1[3] - 1 then
  295.         return 3
  296.     elseif pos2[2] == pos1[2] + 1 then
  297.         return 4
  298.     elseif pos2[2] == pos1[2] - 1 then
  299.         return 5
  300.     else
  301.         printTable(pos1)
  302.         printTable(pos2)
  303.         error("cannot determine direction from points")
  304.     end
  305. end
  306.  
  307. function yield()
  308.     os.queueEvent("randomEvent")
  309.     os.pullEvent()
  310. end
  311.  
  312. function findPathToAny(start, destinations)
  313.     local allOpenNodesQueues = {}
  314.     local allOpenNodes = {}
  315.     local allClosedNodes = {}
  316.     local activeRoutes = {}
  317.  
  318.     for i, destination in pairs(destinations) do
  319.         local openNodesQueue = {}
  320.         local openNodes = {}
  321.         table.insert(openNodesQueue, { pos = start, identifier = posToIdentifier(start), f = 0, g = 0, pre = nil })
  322.         openNodes[openNodesQueue[1].identifier] = openNodesQueue[1]
  323.  
  324.         table.insert(allOpenNodesQueues, openNodesQueue)
  325.         table.insert(allOpenNodes, openNodes)
  326.         table.insert(allClosedNodes, {})
  327.         table.insert(activeRoutes, true)
  328.     end
  329.  
  330.     while (true) do
  331.         local anyActive = false
  332.         for i = 1, #activeRoutes do
  333.             if activeRoutes[i] then
  334.                 anyActive = true
  335.                 local openNodesQueue = allOpenNodesQueues[i]
  336.                 local openNodes = allOpenNodes[i]
  337.                 local closedNodes = allClosedNodes[i]
  338.                 table.sort(openNodesQueue, function(a, b)
  339.                     return a.f < b.f
  340.                 end)
  341.                 yield()
  342.                 if #openNodesQueue > 1000 then
  343.                     activeRoutes[i] = false
  344.                 else
  345.                     local currentNode = table.remove(openNodesQueue, 1)
  346.  
  347.                     if not currentNode then
  348.                         activeRoutes[i] = false
  349.                     else
  350.                         if posEquals(currentNode.pos, destinations[i]) then
  351.                             return getPathFromNode(currentNode), i
  352.                         end
  353.  
  354.                         closedNodes[currentNode.identifier] = true
  355.  
  356.                         aStarExpandNode(currentNode, openNodesQueue, openNodes, closedNodes, destinations[i])
  357.                     end
  358.                 end
  359.             end
  360.         end
  361.         if not anyActive then
  362.             return false
  363.         end
  364.     end
  365.  
  366. end
  367.  
  368. function findPath(start, destination)
  369.     local openNodesQueue = {}
  370.     local openNodes = {}
  371.     local closedNodes = {}
  372.     table.insert(openNodesQueue, { pos = start, identifier = posToIdentifier(start), f = 0, g = 0, pre = nil })
  373.     openNodes[openNodesQueue[1].identifier] = openNodesQueue[1]
  374.  
  375.     while (true) do
  376.         table.sort(openNodesQueue, function(a, b)
  377.             return a.f < b.f
  378.         end)
  379.         if #openNodesQueue > 10000 then
  380.             return nil
  381.         end
  382.         yield()
  383.         local currentNode = table.remove(openNodesQueue, 1)
  384.  
  385.         if not currentNode then
  386.             return nil
  387.         end
  388.  
  389.         if posEquals(currentNode.pos, destination) then
  390.             return getPathFromNode(currentNode)
  391.         end
  392.  
  393.         closedNodes[currentNode.identifier] = true
  394.  
  395.         aStarExpandNode(currentNode, openNodesQueue, openNodes, closedNodes, destination)
  396.     end
  397. end
  398.  
  399. function getPathFromNode(endNode)
  400.     local path = {}
  401.     local currentNode = endNode
  402.     while currentNode do
  403.         table.insert(path, 1, currentNode.pos)
  404.         currentNode = currentNode.pre
  405.     end
  406.     return path
  407. end
  408.  
  409. function aStarExpandNode(currentNode, openNodesQueue, openNodes, closedNodes, destination)
  410.     for i, successor in ipairs(getSuccessors(currentNode)) do
  411.         local identifier = posToIdentifier(successor)
  412.         if not closedNodes[identifier] then
  413.             local g = currentNode.g + 1
  414.  
  415.             if openNodes[identifier] and g > openNodes[identifier].g then
  416.  
  417.             else
  418.                 local f = g + aStarH(successor, destination)
  419.                 if openNodes[identifier] then
  420.                     local entry = openNodes[identifier]
  421.                     entry.g = g
  422.                     entry.f = f
  423.                     entry.pre = currentNode
  424.                 else
  425.                     local entry = { pos = successor, identifier = identifier, f = f, g = g, pre = currentNode }
  426.                     table.insert(openNodesQueue, entry)
  427.                     openNodes[identifier] = entry
  428.                 end
  429.             end
  430.         end
  431.     end
  432. end
  433.  
  434. function aStarH(start, destination)
  435.     return math.abs(destination[1] - start[1]) + math.abs(destination[2] - start[2]) + math.abs(destination[3] - start[3])
  436. end
  437.  
  438. function getSuccessors(node)
  439.     local pos = node.pos
  440.     local x = pos[1]
  441.     local y = pos[2]
  442.     local z = pos[3]
  443.     local potentialSuccessors = {
  444.         { x + 1, y, z },
  445.         { x - 1, y, z },
  446.         { x, y + 1, z },
  447.         { x, y - 1, z },
  448.         { x, y, z + 1 },
  449.         { x, y, z - 1 }
  450.     }
  451.     local successors = {}
  452.     for i, v in ipairs(potentialSuccessors) do
  453.         if not isObstructed(v) then
  454.             table.insert(successors, v)
  455.         end
  456.     end
  457.     return successors
  458. end
  459.  
  460. function posToIdentifier(pos)
  461.     return pos[1] .. ":" .. pos[2] .. ":" .. pos[3]
  462. end
  463.  
  464. function posEquals(pos1, pos2)
  465.     return pos1[1] == pos2[1] and pos1[2] == pos2[2] and pos1[3] == pos2[3]
  466. end
  467.  
  468. function isObstructed(pos)
  469.     local blocks = pStorage.blocks
  470.     if not blocks then
  471.         return false
  472.     end
  473.     blocks = blocks[pos[1]]
  474.     if not blocks then
  475.         return false
  476.     end
  477.     blocks = blocks[pos[2]]
  478.     if not blocks then
  479.         return false
  480.     end
  481.     blocks = blocks[pos[3]]
  482.     if not blocks then
  483.         return false
  484.     end
  485.     return true
  486. end
  487.  
  488. function clearObstructedData()
  489.     print("reset environment knowledge")
  490.     pStorage.blocks = {}
  491. end
  492.  
  493. function markAsObstructed(pos)
  494.     local x = pos[1]
  495.     local y = pos[2]
  496.     local z = pos[3]
  497.     if not pStorage.blocks then
  498.         pStorage.blocks = {}
  499.     end
  500.     local blocksX = getAtKeyOrCreateTable(pStorage.blocks, x)
  501.     local blocksY = getAtKeyOrCreateTable(blocksX, y)
  502.     blocksY[z] = true
  503. end
  504.  
  505. function markAsNotObstructed(pos)
  506.     local blocks = pStorage.blocks
  507.     if not blocks then
  508.         return
  509.     end
  510.     blocks = blocks[pos[1]]
  511.     if not blocks then
  512.         return
  513.     end
  514.     blocks = blocks[pos[2]]
  515.     if not blocks then
  516.         return
  517.     end
  518.     table.remove(blocks, pos[3])
  519. end
  520.  
  521. function getAtKeyOrCreateTable(tbl, key)
  522.     local v = tbl[key]
  523.     if not v then
  524.         v = {}
  525.         tbl[key] = v
  526.     end
  527.     return v
  528. end
  529.  
  530. return {
  531.     name = 'smartGPS',
  532.     api = api,
  533.     onInitPersistentStorage = function(_pStorage, _pSave)
  534.         pStorage = _pStorage
  535.         pSave = _pSave
  536.     end
  537. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement