KaoSDlanor

3D pathfinder

Feb 6th, 2013
282
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.96 KB | None | 0 0
  1. local tMap=setmetatable({
  2. {
  3. "01111111111111111101",
  4. "11111111111111111101",
  5. "11111111111111111101",
  6. "00000111111111111101",
  7. "11110110111101111101",
  8. "00000110111101111101",
  9. "01111110001101111101",
  10. "01111111101101111101",
  11. "01111111101101111101",
  12. "01111111101101111101",
  13. "01111111101101111101",
  14. "11111111111101111101",
  15. "11111111111100001101",
  16. "11111111111101111101",
  17. "11111111111101111101",
  18. "11111111111100000001",
  19. "11111111111111111111"
  20. },
  21. {
  22. "01111111111111111111",
  23. "01111111111111111111",
  24. "01111111111111111111",
  25. "01111111111111111111",
  26. "11111110000001111111",
  27. "11111111111111111111",
  28. "11111111111111111111",
  29. "11111111111111111111",
  30. "11111111111111111111",
  31. "11111111111111111111",
  32. "01111111101111111111",
  33. "01111111101111111111",
  34. "00001111101111111111",
  35. "11101111101111111111",
  36. "11001111101111111111",
  37. "11011111101111111111",
  38. "11011111101111111111"
  39. },
  40. {
  41. "11111111111111111111",
  42. "11111111111111111111",
  43. "11111111111111111111",
  44. "11111111111111111111",
  45. "11111111111111111111",
  46. "11111111111111111111",
  47. "11111111111111111111",
  48. "11111111111111111111",
  49. "11111111111111111111",
  50. "11111111111111111111",
  51. "11111111111111111111",
  52. "11111111111111111111",
  53. "11111111111111111111",
  54. "11111111111111111111",
  55. "11111111111111111111",
  56. "11111111111111111111",
  57. "11000000001111111111"
  58. }
  59. },{__call=function(self,x,y,z) --a metatable that adds the __call index so if we call map(x,y,z) it will return whatever is at those co-ordinates
  60.     if self[z] and self[z][y] and x<=#self[z][y] then --first check if layer z exists, then if layer y exists within that and then that the x value is not past the end of the row. if you do not implement this check map(2,3,200) will error because map[200]==nil so map[200][3] is indexing a nil value
  61.         return string.sub(self[z][y],x,x) --retrieve the value that is [x] along the row
  62.     end
  63. end})
  64.  
  65. local function fPath(map,startx,starty,startz,targx,targy,targz) --the actual pathfinder. it expects the metatable to already be set
  66.  
  67.     --[VARS]
  68.     local path={x=startx or 1,y=starty or 1,z=startz or 1,""} --the starting point, it sets the position but defaults to 1,1,1 if no position was set
  69.     local target={x=targx or 1,y=targy or 1,z=targz or 1}
  70.     local displacement=setmetatable({},{__index=function(self,index) if path[index] and target[index] then return path[index]-target[index] end end,__newindex=function() return nil end}) --this is an empty table that is set with metatable __index so whenever we try to access something that is nil in this table __index is called instead of returning nil. index returns the difference between currentposition[index] and targetposition[index] so essentially displacement[x] returns the total x distance from the target
  71.     local checked=setmetatable({{x=1,y=1,z=1}},{__call=function(self,x,y,z) for k,v in pairs(self) do if v.x==x and v.y==y and v.z==z then return true end end return false end}) --checked is a list of nodes we have already visited. this is the hardest part of the system to perfect. call checked(x,y,z) and it will look through the checked nodes and return true if x,y,z has been checked
  72.     local directions_mt={__index=function(self,index) if index=="x" or index=="y" or index=="z" then return 0 end end} --this is set to each direction in directions so that if I did not set direction.x or direction.y or direction.z then they default to 0 so I only hav to actually manually write out the changes. {x=1} means {x=1,y=0,z=0}
  73.     local directions=setmetatable({{x=1,"r"},{x=-1,"l"},{y=1,"d"},{y=-1,"u"},{z=1,"f"},{z=-1,"b"}},{__call=function(self,direction) --this is just a list of all possible directions we can move in, their displacements and what to add to the string that will be returned if we move in that direction
  74.         if direction then --this is harder to explain. if you call direction(string) it will first check if string is "x"/"y"/"z" and if it is any of them return the direction table for them (directions("x") would return both directions that change your x-position), however if we do not specify string then it returns three params, each one if the direction we need to move in in each axis to get to the target
  75.             for k,v in pairs(self) do
  76.                 if v[1]==direction then
  77.                     return v
  78.                 end
  79.             end
  80.             local tDirs={}
  81.             for k,v in pairs(self) do
  82.                 if rawget(v,direction) then
  83.                     table.insert(tDirs,v)
  84.                 end
  85.             end
  86.             return tDirs
  87.         else
  88.             local x
  89.             local y
  90.             local z
  91.             for k,v in pairs(self) do
  92.                 if (displacement.x>0 and v.x<0) or (displacement.x<0 and v.x>0) then
  93.                     x=v
  94.                 elseif (displacement.y>0 and v.y<0) or (displacement.y<0 and v.y>0) then
  95.                     y=v
  96.                 elseif (displacement.z>0 and v.z<0) or (displacement.z<0 and v.z>0) then
  97.                     z=v
  98.                 end
  99.             end
  100.             return x,y,z
  101.         end
  102.     end})
  103.     for k,v in pairs(directions) do setmetatable(v,directions_mt) end
  104.  
  105.     --[FUNCTIONS]
  106.     local render=function() --this is the function that we use to show what we are doing so the user can see that things are working
  107.         term.clear()
  108.         for k,v in pairs(map[path.z]) do
  109.             term.setCursorPos(1,k)
  110.             print(({string.gsub(string.gsub(v,"0","."),"1","#")})[1])
  111.         end
  112.         print(path.x..", "..path.y..", "..path.z)
  113.         term.setCursorPos(path.x,path.y)
  114.         write("*")
  115.     end
  116.  
  117.     local list=function(...) --this is like the pairs function except it just returns the items one by one. for k,v in list("q","w","e","r","t","y") do print(v) sleep(1) end would print each argument, one by one
  118.         local tArgs={...}
  119.         return coroutine.wrap(function()
  120.             for i=1,#tArgs do
  121.                 coroutine.yield(tArgs[i])
  122.             end
  123.         end)
  124.     end
  125.     --[EXECUTION]
  126.  
  127.     while true do
  128.         local x,y,z=directions() --get the 3 directions we need to move in
  129.         if x and map(path.x+x.x,path.y,path.z)=="0" and not checked(path.x+x.x,path.y,path.z) then --if we check the block closer to the target in the x axis and find that it is empty (=="0") and has not been checked yet then move there and add it to the checked list
  130.             path.x=path.x+x.x
  131.             path[1]=path[1]..x[1]
  132.             table.insert(checked,{x=path.x,y=path.y,z=path.z})
  133.         elseif y and map(path.x,path.y+y.y,path.z)=="0" and not checked(path.x,path.y+y.y,path.z) then --if we could not move in the x axis check if we can move closer in the y axis
  134.             path.y=path.y+y.y
  135.             path[1]=path[1]..y[1]
  136.             table.insert(checked,{x=path.x,y=path.y,z=path.z})
  137.         elseif z and map(path.x,path.y,path.z+z.z)=="0" and not checked(path.x,path.y,path.z+z.z) then --then check the z axis
  138.             path.z=path.z+z.z
  139.             path[1]=path[1]..z[1]
  140.             table.insert(checked,{x=path.x,y=path.y,z=path.z})
  141.         else --if we cannot move closer to the target see if we can move at all in case there is a winding passage etc. always move in the axis that is closer to the terger so if we are at 1,1,1 and the target is at 1,5,7 move in the x axis. this way we avoid mapping up 2 of the axis and neglecting the close one
  142.             local moved=false
  143.             local axis,smallest
  144.             for item in list("x","y","z") do
  145.                 if not smallest or displacement[item]*(displacement[item]<0 and -1 or 1)<smallest or (displacement[item]==smallest and math.random(1,2)==1) then --go through all of the axis, find the displacement and select the one with the smallest displacement. if any are tied randomly choose between then once again to avoid neglecting an axis
  146.                         axis=item
  147.                         smallest=displacement[item]*(displacement[item]<0 and -1 or 1)
  148.                 end
  149.             end
  150.             local dirs=directions(axis)
  151.             for k,v in pairs(directions) do table.insert(dirs,v) end --get the primary directions in the smallest displacement axis and then add the other directions as well so that if we are stuck in that dimension we will try all directions
  152.             for k,v in ipairs(dirs) do
  153.                 if v~=x and v~=y and v~=z and map(path.x+v.x,path.y+v.y,path.z+v.z)=="0" and not checked(path.x+v.x,path.y+v.y,path.z+v.z) then --check if that position is empty and that it has not been moved to yet
  154.                     moved=true
  155.                     path.x=path.x+v.x
  156.                     path.y=path.y+v.y
  157.                     path.z=path.z+v.z
  158.                     path[1]=path[1]..v[1]
  159.                     table.insert(checked,{x=path.x,y=path.y,z=path.z})
  160.                     break
  161.                 end
  162.             end
  163.             if not moved then --if after checking EVERY direction we still have not moved then we have reached a deead-end and need to retrace our steps until we can take a different path, if we have been forced to re-trace all the way back to the beginning then we cannot get to the target. this is written slightly incorrectly and is fixed in the API version
  164.                 if path.x==1 and path.y==1 then
  165.                     term.clear()
  166.                     term.setCursorPos(1,1)
  167.                     print("no way to get to target...")
  168.                     break
  169.                 end
  170.                 path.x=path.x-directions(string.sub(path[1],-1)).x
  171.                 path.y=path.y-directions(string.sub(path[1],-1)).y
  172.                 path.z=path.z-directions(string.sub(path[1],-1)).z
  173.                 path[1]=string.sub(path[1],1,-2)
  174.             end
  175.         end
  176.         term.setCursorPos(1,12) --after each movement loop check if we are at the target, if we are then end the loop
  177.         if path.x==target.x and path.y==target.y and path.z==target.z then
  178.             term.clear()
  179.             term.setCursorPos(1,1)
  180.             print("target found, path:")
  181.             print(path[1])
  182.             break
  183.         end
  184.         sleep(0.1)
  185.         render()
  186.     end
  187. end
  188.  
  189. fPath(tMap,19,1,1)
Advertisement
Add Comment
Please, Sign In to add comment