Dimencia

Untitled

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