Advertisement
BombBloke

Turtle Pathfinder (ComputerCraft)

Jun 7th, 2014
999
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.24 KB | None | 0 0
  1. -- Path Finder
  2. -- ----------------------------------------------------------
  3.  
  4. -- By Jeffrey Alexander, aka Bomb Bloke.
  5. -- Tries to get from A to B.
  6.  
  7. -- If a GPS is available, will prompt for the co-ords of its
  8. -- destination and attempt to pathfind there.
  9.  
  10. -- Otherwise, it will search its inventory for a block, and
  11. -- if anything is present it will attempt to find a match for
  12. -- it within the world.
  13.  
  14. -- Failing both of the above, the turtle will wander until
  15. -- its exploration database overloads...
  16.  
  17. -- ----------------------------------------------------------
  18.  
  19. -- Initialise important values:
  20.  
  21. -- ----------------------------------------------------------
  22.  
  23. local facing = {"north","east","south","west","up","down"}
  24. local node, direction, curnode, x, y, z, targetX, targetY, targetZ, pathList, gpsAccess, comparing = {}
  25.  
  26. -- ----------------------------------------------------------
  27.  
  28. -- Functions and stuff:
  29.  
  30. -- ----------------------------------------------------------
  31.  
  32. -- :)
  33. local function win()
  34.     print("Looks like I made it out. Done! I'M FREE!")
  35.     error()
  36. end
  37.  
  38. -- Returns true if the turtle is at the specified node.
  39. local function atNode(tnode) return (x == node[tnode][1] and y == node[tnode][2] and z == node[tnode][3]) end
  40.  
  41. -- Returns the index of a given value if it exists in a given table, or nil if it does not.
  42. local function whereIn(targetTable, targetValue) for i=1,#targetTable do if targetTable[i] == targetValue then return i end end end
  43.  
  44. -- Returns the best direction to explore in from a given node. Sorta.
  45. local function bestDirection(thisNode)
  46.     if gpsAccess then
  47.         local validDir = {}
  48.         for i=1,6 do validDir[facing[i]] = whereIn(node[thisNode].explore, facing[i]) end
  49.        
  50.         if z < node[1][3] and validDir["up"]    then return table.remove(node[thisNode].explore, validDir["up"])    end
  51.         if z > node[1][3] and validDir["down"]  then return table.remove(node[thisNode].explore, validDir["down"])  end
  52.         if x < node[1][1] and validDir["east"]  then return table.remove(node[thisNode].explore, validDir["east"])  end
  53.         if x > node[1][1] and validDir["west"]  then return table.remove(node[thisNode].explore, validDir["west"])  end
  54.         if y < node[1][2] and validDir["south"] then return table.remove(node[thisNode].explore, validDir["south"]) end
  55.         if y > node[1][2] and validDir["north"] then return table.remove(node[thisNode].explore, validDir["north"]) end
  56.     end
  57.    
  58.     return table.remove(node[thisNode].explore)
  59. end
  60.  
  61. -- Accepts strings representing compass-facings to turn the turtle.
  62. local function faceDirection(targetdirection)
  63.     local tardir
  64.     for i=1,4 do if targetdirection == facing[i] then
  65.         tardir = i
  66.         break
  67.     end end
  68.    
  69.     if tardir < direction then
  70.         if tardir == 1 and direction == 4 then while not turtle.turnRight() do end
  71.         else for i=1,direction-tardir do while not turtle.turnLeft() do end end end
  72.     elseif tardir > direction then
  73.         if tardir == 4 and direction == 1 then while not turtle.turnLeft() do end
  74.         else for i=1,tardir-direction do while not turtle.turnRight() do end end end
  75.     end
  76.    
  77.     direction = tardir
  78. end
  79.  
  80. -- Travel to a co-ordinate.
  81. local function goToPos(targetx,targety,targetz)
  82.     while x ~= targetx or y ~= targety or z ~= targetz do
  83.         if z > targetz then if turtle.down() then z = z - 1 end
  84.         elseif z < targetz then if turtle.up() then z = z + 1 end end
  85.        
  86.         if x > targetx then
  87.             if direction ~= 4 then faceDirection("west") end
  88.             if turtle.forward() then x = x - 1 end
  89.         elseif x < targetx then
  90.             if direction ~= 2 then faceDirection("east") end
  91.             if turtle.forward() then x = x + 1 end
  92.         end
  93.        
  94.         if y > targety then
  95.             if direction ~= 1 then faceDirection("north") end
  96.             if turtle.forward() then y = y - 1 end
  97.         elseif y < targety then
  98.             if direction ~= 3 then faceDirection("south") end
  99.             if turtle.forward() then y = y + 1 end
  100.         end
  101.     end
  102. end
  103.  
  104. -- Travel directly to a node and update the node tracker.
  105. local function goToNode(desnode)
  106.     goToPos(node[desnode][1],node[desnode][2],node[desnode][3])
  107.     curnode = desnode
  108. end
  109.  
  110. -- Used by the next function to determine the cheapest route to a given destination.
  111. local function checkNode(lastNode,thisNode, desnode)
  112.     local hopDistance = math.abs(node[thisNode][1] - node[lastNode][1]) + math.abs(node[thisNode][2] - node[lastNode][2]) + math.abs(node[thisNode][3] - node[lastNode][3])
  113.    
  114.     if pathList.bestDistance[thisNode] and pathList.bestDistance[thisNode] <= pathList.curDistance + hopDistance then return end
  115.    
  116.     os.queueEvent("Wake up!")
  117.     coroutine.yield()
  118.    
  119.     pathList.curDistance = pathList.curDistance + hopDistance
  120.     pathList.bestDistance[thisNode] = pathList.curDistance
  121.     pathList[#pathList+1] = thisNode
  122.    
  123.     -- If this is the target node, then record the path.
  124.     if thisNode == desnode then
  125.         pathList.bestPath = {}
  126.         for i=2,#pathList do pathList.bestPath[i-1] = pathList[i] end
  127.        
  128.         pathList[#pathList] = nil
  129.         pathList.curDistance = pathList.curDistance - hopDistance
  130.         return
  131.     end
  132.    
  133.     -- Do a quick search in case the target is right next to us:
  134.     for i=4,#node[thisNode] do if node[thisNode][i] == desnode then
  135.         checkNode(thisNode,node[thisNode][i],desnode)
  136.        
  137.         pathList[#pathList] = nil
  138.         pathList.curDistance = pathList.curDistance - hopDistance
  139.         return
  140.     end end
  141.    
  142.     -- Check the validity of all attached nodes:
  143.     for i=4,#node[thisNode] do
  144.         alreadyChecked = false
  145.        
  146.         for j=1,#pathList do if pathList[j] == node[thisNode][i] then
  147.             alreadyChecked = true
  148.             break
  149.         end end
  150.        
  151.         if not alreadyChecked then checkNode(thisNode,node[thisNode][i],desnode) end
  152.     end
  153.    
  154.     pathList[#pathList] = nil
  155.     pathList.curDistance = pathList.curDistance - hopDistance
  156.     return
  157. end
  158.  
  159. -- Travels to a given node, pathing from the current node.
  160. local function pathToNode(desnode)
  161.     goToNode(curnode)
  162.     if curnode == desnode then return end
  163.    
  164.     -- Quick check:
  165.     for i=4,#node[curnode] do if node[curnode][i] == desnode then
  166.         goToNode(desnode)
  167.         return
  168.     end end
  169.  
  170.     -- Long check:
  171.     pathList = {curnode, ["curDistance"] = 0, ["bestDistance"] = {}}
  172.     for i=4,#node[curnode] do checkNode(curnode,node[curnode][i],desnode) end
  173.     if not pathList.bestPath then error("I couldn't determine any path from nodes "..curnode.." to "..desnode..".") end
  174.     for i=1,#pathList.bestPath do goToNode(pathList.bestPath[i]) end
  175. end
  176.  
  177. -- Travels in a given direction, until such time as a new node is discovered.
  178. local function goExplore(targetDirection)
  179.     local nextX, nextY, nextZ, newExplore, reverse = x, y, z
  180.    
  181.     if targetDirection == "up" then reverse = "down"
  182.     elseif targetDirection == "down" then reverse = "up"
  183.     elseif targetDirection == "north" then reverse = "south"
  184.     elseif targetDirection == "south" then reverse = "north"
  185.     elseif targetDirection == "east" then reverse = "west"
  186.     elseif targetDirection == "west" then reverse = "east" end
  187.    
  188.     repeat
  189.         if targetDirection == "up" then nextZ = nextZ + 1
  190.         elseif targetDirection == "down" then nextZ = nextZ - 1
  191.         elseif targetDirection == "north" then nextY = nextY - 1
  192.         elseif targetDirection == "south" then nextY = nextY + 1
  193.         elseif targetDirection == "east" then nextX = nextX + 1
  194.         elseif targetDirection == "west" then nextX = nextX - 1 end
  195.        
  196.         goToPos(nextX,nextY,nextZ)
  197.        
  198.         newExplore = {}
  199.        
  200.         if comparing and (turtle.compareUp() or turtle.compareDown()) then win() end
  201.        
  202.         for i=direction,direction+3 do
  203.             faceDirection(facing[bit.band(i-1,3)+1])
  204.             if comparing and turtle.compare() then win() end
  205.             if facing[bit.band(i-1,3)+1] ~= reverse and not turtle.detect() then newExplore[#newExplore+1] = facing[bit.band(i-1,3)+1] end
  206.         end
  207.        
  208.         if reverse ~= "up" and not turtle.detectUp() then newExplore[#newExplore+1] = "up" end
  209.         if reverse ~= "down" and not turtle.detectDown() then newExplore[#newExplore+1] = "down" end
  210.        
  211.         -- Have we found a junction?
  212.         if #newExplore > 1 or newExplore[1] ~= targetDirection then
  213.             for i=1,table.maxn(node) do if node[i] and atNode(i) then
  214.                 for j=1,#node[i].explore do if node[i].explore[j] == reverse then
  215.                     table.remove(node[i].explore,j)
  216.                     break
  217.                 end end
  218.                
  219.                 node[i][#node[i]+1] = curnode
  220.                 node[curnode][#node[curnode]+1] = i
  221.                 curnode = i
  222.                
  223.                 return
  224.             end end
  225.            
  226.             if #node[curnode] > 3 or #node[curnode].explore > 0 then
  227.                 local newnode = #node+1
  228.                 node[newnode] = {x,y,z,curnode,["explore"] = newExplore}
  229.                 node[curnode][#node[curnode]+1] = newnode
  230.                 curnode = newnode
  231.             else node[curnode] = {x,y,z,["explore"] = newExplore} end
  232.            
  233.             return
  234.         end
  235.     until #newExplore == 0
  236.    
  237.     goToNode(curnode)
  238. end
  239.  
  240. -- ----------------------------------------------------------
  241.  
  242. -- I've just booted up. Where am I? Who am I? etc...
  243.  
  244. -- ----------------------------------------------------------
  245.  
  246. print("I've been supplied with "..turtle.getFuelLevel().." fuel.")
  247.  
  248. -- Determine method of travel (target GPS location / hunt for a block / random exploration):
  249. do
  250.     local tempx, tempz, tempy
  251.    
  252.     for i=1,2 do
  253.         tempx, tempz, tempy = gps.locate(5)
  254.        
  255.         if tempy then
  256.             gpsAccess = true
  257.             break
  258.         end
  259.        
  260.         print("GPS cluster not answering; pinging again...")
  261.         sleep(5)
  262.     end
  263.    
  264.     if gpsAccess then
  265.         print("So... Where are we going? (or mash enter to quit)")
  266.        
  267.         write("X: ")
  268.         targetX = tonumber(read())
  269.        
  270.         write("Y: ")
  271.         targetZ = tonumber(read())  -- I use Z for depth, like a sensible person.
  272.        
  273.         write("Z: ")
  274.         targetY = tonumber(read())
  275.        
  276.         print("")
  277.        
  278.         if not (targetX and targetY and targetZ) then
  279.             print("At least one of those co-ords was invalid. Exiting...")
  280.             error()
  281.         end
  282.        
  283.         local moved, goingUp = false, true
  284.        
  285.         while not moved do
  286.             for i=1,4 do if turtle.forward() then
  287.                 moved = true
  288.                 break
  289.             else turtle.turnLeft() end end
  290.            
  291.             if not moved then
  292.                 if goingUp then
  293.                     if not turtle.up() then
  294.                         goingUp = false
  295.                         turtle.down()
  296.                     end
  297.                 elseif not turtle.down() then
  298.                     print("Cannot determine facing: I'm boxed in.")
  299.                     print("I'M BOXED IN.")
  300.                     print("CAN'T BREATH!")
  301.                     print("AAAAUUUUUGGGHHH")
  302.                     error("Suffocated")
  303.                 end
  304.             end
  305.         end
  306.        
  307.         x, z, y = gps.locate(5)
  308.        
  309.         if x < tempx then direction = 4
  310.         elseif x > tempx then direction = 2
  311.         elseif y < tempy then direction = 1
  312.         else direction = 3 end
  313.        
  314.         print("I'm at "..x..","..z..","..y.." and I'm facing "..facing[direction]..".")
  315.  
  316.         -- Register target as a node:
  317.         node[1] = {targetX,targetY,targetZ,["explore"]={}}
  318.     else
  319.         x, z, y, direction = 0, 0, 0, 1
  320.  
  321.         write("I can't find a valid GPS cluster; I don't really know where I am. ")
  322.  
  323.         -- Check to see if there's an item in our inventory we can search for in the maze:
  324.         for i=1,16 do if turtle.getItemCount(i) > 0 then
  325.             print("But since there's something in my inventory, I'll assume I should search for a match for it within a maze.")
  326.  
  327.             comparing = true
  328.             turtle.select(i)
  329.             break
  330.         end end
  331.  
  332.         if not comparing then print("And since there's nothing in my inventory, that means I'll be flying blind. Pickaxe me or something if I ever make it out of here.") end
  333.     end
  334. end
  335.  
  336. -- Register starting position as a node:
  337. node[2] = {x,y,z,["explore"]={}}
  338.  
  339. for i=direction,direction+3 do
  340.     faceDirection(facing[bit.band(i-1,3)+1])
  341.     if not turtle.detect() then node[2].explore[#node[2].explore+1] = facing[bit.band(i-1,3)+1] end
  342. end
  343.  
  344. if not turtle.detectUp() then node[2].explore[#node[2].explore+1] = "up" end
  345. if not turtle.detectDown() then node[2].explore[#node[2].explore+1] = "down" end
  346.  
  347. curnode = 2
  348.  
  349. print("")
  350. print("Exploring in progress...")
  351. print("")
  352.  
  353. -- ----------------------------------------------------------
  354.  
  355. -- Main program loop:
  356.  
  357. -- ----------------------------------------------------------
  358.  
  359. while true do
  360.     local nextnode = #node[curnode].explore == 0 and table.maxn(node) or curnode
  361.    
  362.     while node[nextnode] == nil or #node[nextnode].explore == 0 do
  363.         nextnode = nextnode - 1
  364.         if nextnode == 0 then error("Hrm... Doesn't look like there's any way out of here...") end
  365.     end
  366.    
  367.     pathToNode(nextnode)
  368.     goExplore(bestDirection(nextnode))
  369.    
  370.     if gpsAccess and atNode(1) then win() end
  371.    
  372.     while #node[curnode].explore == 0 and #node[curnode] == 4 do
  373.         local deadnode = curnode
  374.         goToNode(node[curnode][4])
  375.        
  376.         for i=4,#node[curnode] do if node[curnode][i] == deadnode then
  377.             table.remove(node[curnode],i)
  378.             break
  379.         end end
  380.        
  381.         node[deadnode] = nil
  382.     end
  383. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement