Advertisement
karelvysinka

turtle movement functions 1

Feb 19th, 2017
182
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.07 KB | None | 0 0
  1. -- This library provide high level turtle movement functions.
  2. --
  3. -- Before being able to use them, you should start the GPS with egps.startGPS()
  4. --    then get your current location with egps.setLocationFromGPS().
  5. -- egps.forward(), egps.back(), egps.up(), egps.down(), egps.turnLeft(), egps.turnRight()
  6. --    replace the standard turtle functions.
  7. -- If you need to use the standard functions, you
  8. --    should call egps.setLocationFromGPS() again before using any egps functions.
  9.  
  10. -- Gist at: https://gist.github.com/SquidLord/4741746
  11.  
  12. -- The MIT License (MIT)
  13.  
  14. -- Copyright (c) 2012 Alexander Williams
  15.  
  16. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  17. -- of this software and associated documentation files (the "Software"), to deal
  18. -- in the Software without restriction, including without limitation the rights
  19. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  20. -- copies of the Software, and to permit persons to whom the Software is
  21. -- furnished to do so, subject to the following conditions:
  22.  
  23. -- The above copyright notice and this permission notice shall be included in all
  24. -- copies or substantial portions of the Software.
  25.  
  26. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  27. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  28. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  29. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  30. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  31. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  32. -- SOFTWARE.
  33.  
  34. function empty(table)
  35.   for _, value in pairs(table) do
  36.     if value ~= nil then
  37.           return false
  38.     end
  39.   end
  40.   return true
  41. end
  42.  
  43. -- Cache of the current turtle position and direction
  44. local cachedX, cachedY, cachedZ, cachedDir
  45.  
  46. -- Directions
  47. North, West, South, East, Up, Down = 0, 1, 2, 3, 4, 5
  48. local shortNames = {[North] = "N", [West] = "W", [South] = "S",
  49.                                     [East] = "E", [Up] = "U", [Down] = "D" }
  50. local deltas = {[North] = {0, 0, -1}, [West] = {-1, 0, 0}, [South] = {0, 0, 1},
  51.                             [East] = {1, 0, 0}, [Up] = {0, 1, 0}, [Down] = {0, -1, 0}}
  52.  
  53. -- cache world geometry
  54. local cachedWorld = {}
  55.  
  56. -- A* parameters
  57. local stopAt, nilCost = 1500, 1000
  58.  
  59. ----------------------------------------
  60. -- detectAll
  61. --
  62. -- function: Detect in all possible directions
  63. --
  64.  
  65. function detectAll()
  66.   local F, U, D = deltas[cachedDir], deltas[Up], deltas[Down]
  67.  
  68.   cachedWorld[cachedX..":"..cachedY..":"..cachedZ] = 0
  69.   if not turtle.detect()         then cachedWorld[(cachedX + F[1])..":"..(cachedY + F[2])..":"..(cachedZ + F[3])] = 0 end
  70.   if not turtle.detectUp()   then cachedWorld[(cachedX + U[1])..":"..(cachedY + U[2])..":"..(cachedZ + U[3])] = 0 end
  71.   if not turtle.detectDown() then cachedWorld[(cachedX + D[1])..":"..(cachedY + D[2])..":"..(cachedZ + D[3])] = 0 end
  72. end
  73.  
  74. ----------------------------------------
  75. -- forward
  76. --
  77. -- function: Move the turtle forward if possible and put the result in cache
  78. -- return: boolean "success"
  79. --
  80.  
  81. function forward()
  82.   local D = deltas[cachedDir]
  83.   local x, y, z = cachedX + D[1], cachedY + D[2], cachedZ + D[3]
  84.   local idx_pos = x..":"..y..":"..z
  85.  
  86.   if turtle.forward() then
  87.     cachedX, cachedY, cachedZ = x, y, z
  88.     detectAll()
  89.     return true
  90.   else
  91.     cachedWorld[idx_pos] = (turtle.detect() and 1 or 0.5)
  92.     return false
  93.   end
  94. end
  95.  
  96. ----------------------------------------
  97. -- back
  98. --
  99. -- function: Move the turtle backward if possible and put the result in cache
  100. -- return: boolean "success"
  101. --
  102.  
  103. function back()
  104.   local D = deltas[cachedDir]
  105.   local x, y, z = cachedX - D[1], cachedY - D[2], cachedZ - D[3]
  106.   local idx_pos = x..":"..y..":"..z
  107.  
  108.   if turtle.back() then
  109.     cachedX, cachedY, cachedZ = x, y, z
  110.     detectAll()
  111.     return true
  112.   else
  113.     cachedWorld[idx_pos] = 0.5
  114.     return false
  115.   end
  116. end
  117.  
  118. ----------------------------------------
  119. -- up
  120. --
  121. -- function: Move the turtle up if possible and put the result in cache
  122. -- return: boolean "success"
  123. --
  124.  
  125. function up()
  126.   local D = deltas[Up]
  127.   local x, y, z = cachedX + D[1], cachedY + D[2], cachedZ + D[3]
  128.   local idx_pos = x..":"..y..":"..z
  129.  
  130.   if turtle.up() then
  131.     cachedX, cachedY, cachedZ = x, y, z
  132.     detectAll()
  133.     return true
  134.   else
  135.     cachedWorld[idx_pos] = (turtle.detectUp() and 1 or 0.5)
  136.     return false
  137.   end
  138. end
  139.  
  140. ----------------------------------------
  141. -- down
  142. --
  143. -- function: Move the turtle down if possible and put the result in cache
  144. -- return: boolean "success"
  145. --
  146.  
  147. function down()
  148.   local D = deltas[Down]
  149.   local x, y, z = cachedX + D[1], cachedY + D[2], cachedZ + D[3]
  150.   local idx_pos = x..":"..y..":"..z
  151.  
  152.   if turtle.down() then
  153.     cachedX, cachedY, cachedZ = x, y, z
  154.     detectAll()
  155.     return true
  156.   else
  157.     cachedWorld[idx_pos] = (turtle.detectDown() and 1 or 0.5)
  158.     return false
  159.   end
  160. end
  161.  
  162. ----------------------------------------
  163. -- turnLeft
  164. --
  165. -- function: Turn the turtle to the left and put the result in cache
  166. -- return: boolean "success"
  167. --
  168.  
  169. function turnLeft()
  170.   cachedDir = (cachedDir + 1) % 4
  171.   turtle.turnLeft()
  172.   detectAll()
  173.   return true
  174. end
  175.  
  176. ----------------------------------------
  177. -- turnRight
  178. --
  179. -- function: Turn the turtle to the right and put the result in cache
  180. -- return: boolean "success"
  181. --
  182.  
  183. function turnRight()
  184.   cachedDir = (cachedDir + 3) % 4
  185.   turtle.turnRight()
  186.   detectAll()
  187.   return true
  188. end
  189.  
  190. ----------------------------------------
  191. -- turnTo
  192. --
  193. -- function: Turn the turtle to the choosen direction and put the result in cache
  194. -- input: number _targetDir
  195. -- return: boolean "success"
  196. --
  197.  
  198. function turnTo(_targetDir)
  199.   if _targetDir == cachedDir then
  200.     return true
  201.   elseif ((_targetDir - cachedDir + 4) % 4) == 1 then
  202.     turnLeft()
  203.   elseif ((cachedDir - _targetDir + 4) % 4) == 1 then
  204.     turnRight()
  205.   else
  206.     turnLeft()
  207.     turnLeft()
  208.   end
  209.   return true
  210. end
  211.  
  212. ----------------------------------------
  213. -- clearWorld
  214. --
  215. -- function: Clear the world cache
  216. --
  217.  
  218. function clearWorld()
  219.   cachedWorld = {}
  220.   detectAll()
  221. end
  222.  
  223. ----------------------------------------
  224. -- heuristic_cost_estimate
  225. --
  226. -- function: A* heuristic
  227. -- input: X, Y, Z of the 2 points
  228. -- return: Manhattan distance between the 2 points
  229. --
  230.  
  231. local function heuristic_cost_estimate(x1, y1, z1, x2, y2, z2)
  232.   return math.abs(x2 - x1) + math.abs(y2 - y1) + math.abs(z2 - z1)
  233. end
  234.  
  235. ----------------------------------------
  236. -- reconstruct_path
  237. --
  238. -- function: A* path reconstruction
  239. -- input: A* visited nodes and goal
  240. -- return: List of movement to be executed
  241. --
  242.  
  243. local function reconstruct_path(_cameFrom, _currentNode)
  244.   if _cameFrom[_currentNode] ~= nil then
  245.     local dir, nextNode = _cameFrom[_currentNode][1], _cameFrom[_currentNode][2]
  246.     local path = reconstruct_path(_cameFrom, nextNode)
  247.     table.insert(path, dir)
  248.     return path
  249.   else
  250.     return {}
  251.   end
  252. end
  253.  
  254. ----------------------------------------
  255. -- a_star
  256. --
  257. -- function: A* path finding
  258. -- input: start and goal coordinates
  259. -- return: List of movement to be executed
  260. --
  261.  
  262. local function a_star(x1, y1, z1, x2, y2, z2, discover)
  263.   discover = discover or 1
  264.   local start, idx_start = {x1, y1, z1}, x1..":"..y1..":"..z1
  265.   local goal,  idx_goal  = {x2, y2, z2}, x2..":"..y2..":"..z2
  266.  
  267.   if (cachedWorld[idx_goal] or 0) == 0 then
  268.     local openset, closedset, cameFrom, g_score, f_score, tries = {}, {}, {}, {}, {}, 0
  269.  
  270.     openset[idx_start] = start
  271.     g_score[idx_start] = 0
  272.     f_score[idx_start] = heuristic_cost_estimate(x1, y1, z1, x2, y2, z2)
  273.  
  274.     while not empty(openset) do
  275.           local current, idx_current
  276.           local cur_f = 9999999
  277.            
  278.           for idx_cur, cur in pairs(openset) do
  279.             if cur ~= nil and f_score[idx_cur] <= cur_f then
  280.                   idx_current, current, cur_f = idx_cur, cur, f_score[idx_cur]
  281.             end
  282.           end
  283.           if idx_current == idx_goal then
  284.             return reconstruct_path(cameFrom, idx_goal)
  285.           end
  286.  
  287.           -- no more than 500 moves
  288.           if cur_f >= stopAt then
  289.             break
  290.           end
  291.            
  292.           openset[idx_current] = nil
  293.           closedset[idx_current] = true
  294.            
  295.           local x3, y3, z3 = current[1], current[2], current[3]
  296.  
  297.           for dir = 0, 5 do -- for all direction find the neighbor of the current position
  298.             local D = deltas[dir]
  299.             local x4, y4, z4 = x3 + D[1], y3 + D[2], z3 + D[3]
  300.             local neighbor, idx_neighbor = {x4, y4, z4}, x4..":"..y4..":"..z4
  301.             if (cachedWorld[idx_neighbor] or 0) == 0 then -- if its free or unknow
  302.                   if closedset[idx_neighbor] == nil then
  303.                     local tentative_g_score = g_score[idx_current] + ((cachedWorld[idx_neighbor] == nil) and discover or 1)
  304.                     if openset[idx_neighbor] == nil or tentative_g_score <= g_score[idx_neighbor] then
  305.                           cameFrom[idx_neighbor] = {dir, idx_current}
  306.                           g_score[idx_neighbor] = tentative_g_score
  307.                           f_score[idx_neighbor] = tentative_g_score + heuristic_cost_estimate(x4, y4, z4, x2, y2, z2)
  308.                           openset[idx_neighbor] = neighbor
  309.                     end
  310.                   end
  311.             end
  312.           end
  313.     end
  314.   end
  315.   print("no path found")
  316.   return {}
  317. end
  318.  
  319. ----------------------------------------
  320. -- moveTo
  321. --
  322. -- function: Move the turtle to the choosen coordinates in the world
  323. -- input: X, Y, Z and direction of the goal
  324. -- return: boolean "success"
  325. --
  326.  
  327. function moveTo(_targetX, _targetY, _targetZ, _targetDir, changeDir, discover)
  328.   changeDir = (changeDir == nil) and true or changeDir
  329.   while cachedX ~= _targetX or cachedY ~= _targetY or cachedZ ~= _targetZ do
  330.     local path = a_star(cachedX, cachedY, cachedZ, _targetX, _targetY, _targetZ, discover)
  331.     if #path == 0 then
  332.           return false
  333.     end
  334.     for i, dir in ipairs(path) do
  335.           if dir == Up then
  336.             if not up() then
  337.                   break
  338.             end
  339.           elseif dir == Down then
  340.             if not down() then
  341.                   break
  342.             end
  343.           else
  344.             turnTo(dir)
  345.             if not forward() then
  346.                   break
  347.             end
  348.           end
  349.     end
  350.   end
  351.   if changeDir then
  352.     turnTo(_targetDir)
  353.   end
  354.   return true
  355. end
  356.  
  357. ----------------------------------------
  358. -- discoverWorld
  359. --
  360. -- function: Move the turtle to the choosen coordinates in the world
  361. -- input: size of the cuboid to check
  362. --
  363.  
  364. function discoverWorld(_range)
  365.   local x, y, z, d = locate()
  366.  
  367.   -- Try to go to every location in the cuboid
  368.   for r = 1, _range do
  369.     for dx = -r, r do
  370.           for dy = -r, r do
  371.             for dz = -r, r do
  372.                   local idx_goal = (x+dx)..":"..(y+dy)..":"..(z+dz)
  373.                   if cachedWorld[idx_goal] == nil then
  374.                     moveTo(x+dx, y+dy, z+dz, cachedDir, false, nilCost)
  375.                     sleep(0.01)
  376.                   end
  377.             end
  378.           end
  379.     end
  380.   end
  381.  
  382.   -- Go back to the starting point  
  383.   moveTo(x, y, z, d)
  384. end
  385.  
  386. ----------------------------------------
  387. -- setLocation
  388. --
  389. -- function: Set the current X, Y, Z and direction of the turtle
  390. --
  391.  
  392. function setLocation(x, y, z, d)
  393.   cacheX, cacheY, cacheZ, cacheDir = x, y, z, d
  394.   return cacheX, cacheY, cacheZ, cacheDir
  395. end
  396.  
  397. ----------------------------------------
  398. -- startGPS
  399. --
  400. -- function: Open the rednet network
  401. -- return: boolean "success"
  402. --
  403.  
  404. function startGPS()
  405.     local netOpen, modemSide = false, nil
  406.  
  407.     for _, side in pairs(rs.getSides()) do    -- for all sides
  408.         if peripheral.getType(side) == "modem" then  -- find the modem
  409.             modemSide = side
  410.             if rednet.isOpen(side) then  -- check its status
  411.                 netOpen = true
  412.                 break
  413.             end
  414.         end
  415.     end
  416.  
  417.     if not netOpen then  -- if the rednet network is not open
  418.         if modemSide then  -- and we found a modem, open the rednet network
  419.             rednet.open(modemSide)
  420.         else
  421.             print("No modem found")
  422.             return false
  423.         end
  424.     end
  425.     return true
  426. end
  427.  
  428. ----------------------------------------
  429. -- setGPSLocation
  430. --
  431. -- function: Retrieve the turtle GPS position and direction (if possible)
  432. -- return: current X, Y, Z and direction of the turtle
  433. --
  434.  
  435. function setLocationFromGPS()
  436.   if startGPS() then
  437.     -- get the current position
  438.     cachedX, cachedY, cachedZ  = gps.locate(4, false)
  439.     cachedDir = nil
  440.  
  441.     -- determine the current direction
  442.     for tries = 0, 3 do  -- try to move in one direction
  443.           if turtle.forward() then
  444.             local newX, _, newZ = gps.locate(4, false) -- get the new position
  445.             turtle.back()                                                         -- and go back
  446.  
  447.             -- deduce the curent direction
  448.             if newZ < cachedZ then
  449.                   cachedDir = North -- North
  450.             elseif newZ > cachedZ then
  451.                   cachedDir = South -- South
  452.             elseif newX < cachedX then
  453.                   cachedDir = West -- West
  454.             elseif newX > cachedX then
  455.                   cachedDir = East -- East
  456.             end
  457.  
  458.             -- Cancel out the tries
  459.             turnTo((cachedDir - tries + 4) % 4)
  460.  
  461.             -- exit the loop
  462.             break
  463.  
  464.           else -- try in another direction
  465.             tries = tries + 1
  466.             turtle.turnLeft()
  467.           end
  468.     end
  469.  
  470.     if cachedDir == nil then
  471.           print("Could not determine direction")
  472.     end
  473.  
  474.     -- Return the current turtle position
  475.     return cachedX, cachedY, cachedZ, cachedDir
  476.   end
  477. end
  478.  
  479. ----------------------------------------
  480. -- getLocation
  481. --
  482. -- function: Retrieve the cached turtle position and direction
  483. -- return: cached X, Y, Z and direction of the turtle
  484. --
  485.  
  486. function getLocation()
  487.   return cachedX, cachedY, cachedZ, cachedDir
  488. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement