Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local tMap=setmetatable({
- {
- "01111111111111111101",
- "11111111111111111101",
- "11111111111111111101",
- "00000111111111111101",
- "11110110111101111101",
- "00000110111101111101",
- "01111110001101111101",
- "01111111101101111101",
- "01111111101101111101",
- "01111111101101111101",
- "01111111101101111101",
- "11111111111101111101",
- "11111111111100001101",
- "11111111111101111101",
- "11111111111101111101",
- "11111111111100000001",
- "11111111111111111111"
- },
- {
- "01111111111111111111",
- "01111111111111111111",
- "01111111111111111111",
- "01111111111111111111",
- "11111110000001111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "01111111101111111111",
- "01111111101111111111",
- "00001111101111111111",
- "11101111101111111111",
- "11001111101111111111",
- "11011111101111111111",
- "11011111101111111111"
- },
- {
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11111111111111111111",
- "11000000001111111111"
- }
- },{__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
- 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
- return string.sub(self[z][y],x,x) --retrieve the value that is [x] along the row
- end
- end})
- local function fPath(map,startx,starty,startz,targx,targy,targz) --the actual pathfinder. it expects the metatable to already be set
- --[VARS]
- 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
- local target={x=targx or 1,y=targy or 1,z=targz or 1}
- 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
- 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
- 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}
- 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
- 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
- for k,v in pairs(self) do
- if v[1]==direction then
- return v
- end
- end
- local tDirs={}
- for k,v in pairs(self) do
- if rawget(v,direction) then
- table.insert(tDirs,v)
- end
- end
- return tDirs
- else
- local x
- local y
- local z
- for k,v in pairs(self) do
- if (displacement.x>0 and v.x<0) or (displacement.x<0 and v.x>0) then
- x=v
- elseif (displacement.y>0 and v.y<0) or (displacement.y<0 and v.y>0) then
- y=v
- elseif (displacement.z>0 and v.z<0) or (displacement.z<0 and v.z>0) then
- z=v
- end
- end
- return x,y,z
- end
- end})
- for k,v in pairs(directions) do setmetatable(v,directions_mt) end
- --[FUNCTIONS]
- 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
- term.clear()
- for k,v in pairs(map[path.z]) do
- term.setCursorPos(1,k)
- print(({string.gsub(string.gsub(v,"0","."),"1","#")})[1])
- end
- print(path.x..", "..path.y..", "..path.z)
- term.setCursorPos(path.x,path.y)
- write("*")
- end
- 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
- local tArgs={...}
- return coroutine.wrap(function()
- for i=1,#tArgs do
- coroutine.yield(tArgs[i])
- end
- end)
- end
- --[EXECUTION]
- while true do
- local x,y,z=directions() --get the 3 directions we need to move in
- 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
- path.x=path.x+x.x
- path[1]=path[1]..x[1]
- table.insert(checked,{x=path.x,y=path.y,z=path.z})
- 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
- path.y=path.y+y.y
- path[1]=path[1]..y[1]
- table.insert(checked,{x=path.x,y=path.y,z=path.z})
- 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
- path.z=path.z+z.z
- path[1]=path[1]..z[1]
- table.insert(checked,{x=path.x,y=path.y,z=path.z})
- 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
- local moved=false
- local axis,smallest
- for item in list("x","y","z") do
- 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
- axis=item
- smallest=displacement[item]*(displacement[item]<0 and -1 or 1)
- end
- end
- local dirs=directions(axis)
- 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
- for k,v in ipairs(dirs) do
- 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
- moved=true
- path.x=path.x+v.x
- path.y=path.y+v.y
- path.z=path.z+v.z
- path[1]=path[1]..v[1]
- table.insert(checked,{x=path.x,y=path.y,z=path.z})
- break
- end
- end
- 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
- if path.x==1 and path.y==1 then
- term.clear()
- term.setCursorPos(1,1)
- print("no way to get to target...")
- break
- end
- path.x=path.x-directions(string.sub(path[1],-1)).x
- path.y=path.y-directions(string.sub(path[1],-1)).y
- path.z=path.z-directions(string.sub(path[1],-1)).z
- path[1]=string.sub(path[1],1,-2)
- end
- end
- term.setCursorPos(1,12) --after each movement loop check if we are at the target, if we are then end the loop
- if path.x==target.x and path.y==target.y and path.z==target.z then
- term.clear()
- term.setCursorPos(1,1)
- print("target found, path:")
- print(path[1])
- break
- end
- sleep(0.1)
- render()
- end
- end
- fPath(tMap,19,1,1)
Advertisement
Add Comment
Please, Sign In to add comment