Dimencia

Untitled

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