Dimencia

Untitled

Mar 24th, 2021
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.70 KB | None | 0 0
  1.  
  2. -- This is a base turtle implementation that should allow it to pathfind using A* pathfinding
  3. -- Any movement or turning causes it to scan its environment and store data, allowing it to 'remember' where obstacles are
  4.  
  5. -- This is honestly doable. If I need a refresher later: https://www.raywenderlich.com/3016-introduction-to-a-pathfinding
  6. if not fs.exists("vec3.lua") then shell.run("wget", "https://raw.githubusercontent.com/Dimencia/Minecraft-Turtles/main/vec3.lua", "vec3.lua") end
  7. if not fs.exists("json.lua") then shell.run("wget", "https://raw.githubusercontent.com/Dimencia/Minecraft-Turtles/main/dkjson.lua", "json.lua") end
  8. if not fs.exists("heap.lua") then shell.run("wget", "https://gist.githubusercontent.com/H2NCH2COOH/1f929775db0a355ca6b6088a4662fe95/raw/1ccc4fc1d99ee6943fc66475f3feac6de8c83c31/heap.lua", "heap.lua") end
  9.  
  10. vec3 = require("vec3")
  11. json = require("json")
  12. minheap = require("heap")
  13.  
  14. logFile = fs.open("Logfile", "w")
  15.  
  16. -- First, check if we've already overwritten these funcs
  17.  
  18.  
  19. function newPrint(...)
  20. local result = ""
  21. for k,v in pairs(arg) do
  22. if k ~= "n" then
  23. result = result .. getDisplayString(v) .. " "
  24. end
  25. end
  26. oldPrint(result)
  27. logFile.writeLine(result)
  28. logFile.flush()
  29. end
  30. if not turtle.methodsOverwritten then -- Unsure if turtle.methodsOverwritten doesn't persist, or if print resets itself, something is wrong
  31. oldPrint = print -- If you run it, then terminate and run it again, it doesn't log anymore
  32. print = newPrint
  33. end
  34.  
  35.  
  36.  
  37. function getDisplayString(object)
  38. local result = ""
  39. if type(object) == "string" then
  40. result = result .. object
  41. elseif type(object) == "table" then
  42. if object.x then -- IDK how else to make sure it's a vec3
  43. result = result .. vectorToString(object)
  44. else
  45. for k,v in pairs(object) do
  46. result = result .. getDisplayString(k) .. ":" .. getDisplayString(v) .. " "
  47. end
  48. end
  49. elseif type(object) == "boolean" then
  50. if object then result = result .. "true" else result = result .. "false" end
  51. elseif object ~= nil then
  52. result = result .. object
  53. else
  54. result = result .. "nil"
  55. end
  56. return result
  57. end
  58.  
  59. occupiedPositions = {} -- The key is the vec3, and the value is true if occupied, or nil/false if not
  60. local initialOrientation = vec3(1,0,0)
  61. local initialPosition = vec3(0,0,0)
  62.  
  63. orientations = { vec3(1,0,0),
  64. vec3(0,0,1),
  65. vec3(-1,0,0),
  66. vec3(0,0,-1)} -- Where going higher in the list is turning right
  67. orientationIndex = 1
  68.  
  69. adjacentVectors = { vec3(1,0,0),
  70. vec3(0,1,0),
  71. vec3(0,0,1),
  72. vec3(-1,0,0),
  73. vec3(0,-1,0),
  74. vec3(0,0,-1)} -- When looking for adjacents, we can iterate over this and add it to the position
  75.  
  76. turtle.orientation = initialOrientation
  77. turtle.position = initialPosition
  78.  
  79. function vectorToString(vec)
  80. return vec.x .. "," .. vec.y .. "," .. vec.z
  81. end
  82.  
  83. function SaveData()
  84. -- Updates our datafile with the turtle's position, orientation, and occupiedPositions (and maybe more later)
  85. local dataFile = fs.open("PathData", "w")
  86. local allData = {position=turtle.position, orientation=turtle.orientation, occupiedPositions=occupiedPositions}
  87. local dataString = json.encode(allData)
  88. dataFile.write(dataString)
  89. dataFile.flush()
  90. dataFile.close()
  91. end
  92.  
  93. function LoadData()
  94. local f = fs.open("PathData", "r")
  95. local allData = json.decode(f.readAll())
  96. if allData and allData.position and allData.orientation and allData.occupiedPositions then
  97. turtle.position = vec3(allData.position)
  98. turtle.orientation = vec3(allData.orientation)
  99. for k,v in ipairs(orientations) do
  100. if vectorToString(v) == vectorToString(turtle.orientation) then
  101. orientationIndex = k
  102. break
  103. end
  104. end
  105.  
  106. occupiedPositions = allData.occupiedPositions
  107. end
  108. f.close()
  109. end
  110.  
  111. function stringSplit (inputstr, sep)
  112. if sep == nil then
  113. sep = "%s"
  114. end
  115. local t={}
  116. for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
  117. table.insert(t, str)
  118. end
  119. return t
  120. end
  121.  
  122. function listLen(list)
  123. local count = 0
  124. for k,v in pairs(list) do
  125. if v ~= nil then count = count + 1 end
  126. end
  127. return count
  128. end
  129.  
  130. if fs.exists("PathData") then
  131. LoadData() -- Load before opening our write handle, which will erase everything
  132. end
  133.  
  134. SaveData() -- Make sure it's not empty if we don't make it to the next tick
  135.  
  136. if not turtle.methodsOverwritten then
  137. baseDig = turtle.dig
  138. turtle.dig = function() -- We may have to pause a tick to wait for gravel to fall...
  139. baseDig()
  140. detectBlocks() -- Check all occupied things after we dig
  141. end
  142.  
  143. baseForward = turtle.forward
  144. turtle.forward = function()
  145. detectBlocks()
  146. if baseForward() then
  147. local newPosition = turtle.position + turtle.orientation
  148. print("Moved forward from " .. vectorToString(turtle.position) .. " to " .. vectorToString(newPosition))
  149. turtle.position = newPosition
  150. detectBlocks()
  151. return true
  152. end
  153. return false
  154. end
  155.  
  156. baseUp = turtle.up
  157. turtle.up = function()
  158. detectBlocks()
  159. if baseUp() then
  160. local newPosition = turtle.position + vec3(0,1,0)
  161. print("Moved up from " .. vectorToString(turtle.position) .. " to " .. vectorToString(newPosition))
  162. turtle.position = newPosition
  163. detectBlocks()
  164. return true
  165. end
  166. return false
  167. end
  168.  
  169. baseDown = turtle.down
  170. turtle.down = function()
  171. detectBlocks()
  172. if baseDown() then
  173. local newPosition = turtle.position + vec3(0,-1,0)
  174. print("Moved down from " .. vectorToString(turtle.position) .. " to " .. vectorToString(newPosition))
  175. turtle.position = newPosition
  176. detectBlocks()
  177. return true
  178. end
  179. return false
  180. end
  181.  
  182. baseTurnLeft = turtle.turnLeft
  183. turtle.turnLeft = function()
  184. baseTurnLeft()
  185. local oldOrientation = turtle.orientation:clone()
  186. updateTurtleOrientationLeft()
  187. print("Turned left from " .. vectorToString(oldOrientation) .. " to " .. vectorToString(turtle.orientation))
  188. detectBlocks()
  189. end
  190.  
  191. baseTurnRight = turtle.turnRight
  192. turtle.turnRight = function()
  193. baseTurnRight()
  194. local oldOrientation = turtle.orientation:clone()
  195. updateTurtleOrientationRight()
  196. print("Turned right from " .. vectorToString(oldOrientation) .. " to " .. vectorToString(turtle.orientation))
  197. detectBlocks()
  198. end
  199. end
  200. turtle.methodsOverwritten = true
  201.  
  202.  
  203. function updateTurtleOrientationLeft()
  204.  
  205. orientationIndex = orientationIndex-1
  206. if orientationIndex < 1 then
  207. orientationIndex = #orientations
  208. end
  209. turtle.orientation = orientations[orientationIndex]
  210. end
  211.  
  212. function updateTurtleOrientationRight()
  213. orientationIndex = orientationIndex+1
  214. if orientationIndex > #orientations then
  215. orientationIndex = 1
  216. end
  217. turtle.orientation = orientations[orientationIndex]
  218. end
  219.  
  220.  
  221. --
  222. -- Pathfinding Stuff Below
  223. --
  224.  
  225. function turnToAdjacent(adjacentPosition) -- Only use on adjacent ones...
  226. print("Calculating turn from " .. vectorToString(turtle.position) .. " to " .. vectorToString(adjacentPosition))
  227. local newOrientation = adjacentPosition-turtle.position
  228. newOrientation.y = 0
  229. -- Now determine how to get from current, to here
  230. -- First, if it was y only, we're done
  231. if newOrientation == vec3() or newOrientation == turtle.orientation then return true end
  232.  
  233. -- Then iteration through orientations forward, if it's <=2 to the target we can go right, otherwise left
  234. for i=1,4 do
  235. local t = orientationIndex + i
  236. if t > #orientations then t = t - #orientations end
  237. if orientations[t] == newOrientation then
  238. if i < 2 then
  239. turtle.turnRight()
  240. return true
  241. elseif i == 2 then
  242. turtle.turnRight()
  243. turtle.turnRight()
  244. return true
  245. else
  246. turtle.turnLeft()
  247. return true
  248. end
  249. end
  250. end
  251. return false
  252. end
  253.  
  254. function detectBlocks()
  255. -- Detects all blocks and stores the data
  256. occupiedPositions[vectorToString(turtle.position+turtle.orientation)] = turtle.detect()
  257. occupiedPositions[vectorToString(turtle.position+vec3(0,1,0))] = turtle.detectUp()
  258. occupiedPositions[vectorToString(turtle.position+vec3(0,-1,0))] = turtle.detectDown()
  259. SaveData()
  260. end
  261.  
  262. function ComputeSquare(aSquare, currentSquare, targetPosition)
  263. aSquare.parent = currentSquare
  264. aSquare.G = currentSquare.G+1
  265. aSquare.H = (targetPosition-aSquare.position):len()*1.5
  266. aSquare.score = aSquare.G + aSquare.H
  267. end
  268.  
  269. function getAdjacentWalkableSquares(currentSquare)
  270. local results = {}
  271. for k,v in pairs(adjacentVectors) do
  272. local targetVec = currentSquare.position + v
  273. if not occupiedPositions[vectorToString(targetVec)] then -- I am unsure that this works, at least not reliably, it's weird
  274. results[targetVec] = {position=targetVec}
  275. end
  276. end
  277. return results
  278. end
  279.  
  280.  
  281. function GetPath(targetPosition)
  282. print("Getting path for turtle position " .. vectorToString(turtle.position))
  283. local currentSquare = {position=turtle.position,G=0,H=(targetPosition-turtle.position):len()*1.5}
  284. currentSquare.score = currentSquare.G + currentSquare.H -- Manually set these first, the rest rely on a parent
  285.  
  286. local openList = { } -- I guess this is a generic object, which has fields .position
  287. openList[vectorToString(currentSquare.position)] = currentSquare -- This makes it easier to add/remove
  288. local openHeap = minheap.new()
  289. openHeap:push(currentSquare,currentSquare.score)
  290. -- Suppose they also have a .score, .G, and .H, and .parent
  291. local closedList = {}
  292.  
  293. local tickCount = 1
  294.  
  295. local finalMove = nil
  296. repeat
  297. -- Get the square with the lowest score
  298. local currentSquare = openHeap:pop()
  299.  
  300. -- Add this to the closed list, no longer consider it for future moves
  301. closedList[vectorToString(currentSquare.position)] = true
  302. openList[vectorToString(currentSquare.position)] = nil -- Remove from open list
  303.  
  304. if currentSquare.position == targetPosition then
  305. -- We found the path target and put it in the list, we're done.
  306. finalMove = currentSquare
  307. break
  308. end
  309.  
  310. local adjacentSquares = getAdjacentWalkableSquares(currentSquare) -- Should never return occupied squares
  311. -- Returns us a list where the keys are positions, and values just have a position field. We add more fields to the values
  312. for pos,aSquare in pairs(adjacentSquares) do
  313. if not closedList[vectorToString(pos)] then -- Using vectors as keys doesn't work right, have to convert to string
  314. if not openList[vectorToString(pos)] then
  315. -- Compute G, H, and F, and set them on the square
  316. ComputeSquare(aSquare, currentSquare, targetPosition)
  317. -- Add for consideration in next step
  318. openList[vectorToString(pos)] = aSquare
  319. openHeap:push(aSquare,aSquare.score)
  320. elseif openList[vectorToString(pos)] then -- aSquare is already in the list, so it already has these params
  321. aSquare = openList[vectorToString(pos)] -- Use the existing object
  322. if currentSquare.G+1 < aSquare.G then
  323. -- Our path to aSquare is shorter, use our values, replaced into the object - which is already in the heap and list
  324. ComputeSquare(aSquare, currentSquare, targetPosition)
  325. end
  326. end
  327. end
  328. end
  329. tickCount = tickCount + 1
  330. if tickCount % 500 == 0 then
  331. print("Checking 500th position " .. vectorToString(currentSquare.position) .. " with score " .. currentSquare.score)
  332. sleep(0.1)
  333. end
  334.  
  335. until listLen(openList) == 0
  336.  
  337. local curSquare = finalMove -- We set this above when we found it, start at the end
  338. -- Each one gets inserted in front of the previous one
  339. local finalMoves = {}
  340. while curSquare ~= nil do
  341. table.insert(finalMoves, 1, curSquare)
  342. curSquare = curSquare.parent
  343. end
  344. return finalMoves
  345. end
  346.  
  347. function followPath(moveList)
  348. for k,v in ipairs(moveList) do
  349. print("Performing move to adjacent square from " .. vectorToString(turtle.position) .. " to " .. vectorToString(v.position))
  350. local targetVector = v.position - turtle.position
  351. local success
  352. if v.position ~= turtle.position then
  353. if targetVector.y ~= 0 then
  354. -- Just go up or down
  355. if targetVector.y > 0 then
  356. success = turtle.up()
  357. if not success then occupiedPositions[vectorToString(v.position)] = true end
  358. else
  359. success = turtle.down()
  360. if not success then occupiedPositions[vectorToString(v.position)] = true end
  361. end
  362. else
  363. turnToAdjacent(v.position)
  364. success = turtle.forward()
  365. if not success then occupiedPositions[vectorToString(v.position)] = true end
  366. end
  367.  
  368. if not success then -- We were blocked for some reason, re-pathfind
  369. -- Find the target...
  370. print("Obstacle detected, calculating and following new path")
  371. --print("Occupied Positions: ", occupiedPositions)
  372. -- SO, this is really weird and really annoying.
  373. -- If this happens, we seem to often path back to the same spot, even though it's occupied
  374. -- But only sometimes, not always, it's wild.
  375. local lastTarget = nil
  376. for k2, v2 in ipairs(moveList) do
  377. lastTarget = v2
  378. end
  379. local newPath = GetPath(lastTarget.position)
  380. followPath(newPath)
  381. return
  382. end
  383. end
  384. end
  385. print("Path successfully followed, final position: " .. vectorToString(turtle.position))
  386. end
  387.  
  388.  
  389. -- K after this is whatever we want it to do...
  390.  
  391. -- Alright, let's call this a training routine.
  392. -- It should start facing the 'home' chest, which contains coal or fuel, and that's 0,0,0
  393.  
  394. -- Note that the chest ends up being 1,0,0, when we want to turn to face it while standing at 0,0,0
  395. repeat
  396. turnToAdjacent(vec3(1,0,0))
  397. turtle.select(1)
  398. turtle.suck()
  399. turtle.refuel()
  400. -- Generate some random coords. Stay within 16 or so blocks on each to keep it somewhat reasonable
  401. local target = vec3(math.random(0,16),math.random(0,16),math.random(0,16))
  402. print("Getting path to target")
  403. local path = GetPath(target)
  404. followPath(path)
  405. print("Returning to base")
  406. target = vec3()
  407. path = GetPath(target)
  408. followPath(path)
  409. until 1 == 0
Advertisement
Add Comment
Please, Sign In to add comment