Dimencia

Untitled

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