Dimencia

Untitled

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