Dimencia

Untitled

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