Dimencia

Untitled

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