Advertisement
CloneTrooper1019

Maze.lua

Jan 22nd, 2019
382
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.30 KB | None | 0 0
  1. local Vector2 = {}
  2.  
  3. local argAssert = "'%s' must be a %s"
  4. local floor = math.floor
  5.  
  6. local function assertType(value, label, expectedType)
  7.     assert(type(value) == expectedType, argAssert:format(label, expectedType))
  8. end
  9.  
  10. local function assertVector2(v2)
  11.     if type(v2) == "number" then
  12.         v2 = Vector2.new(v2, v2)
  13.     end
  14.    
  15.     assertType(v2, "input", "table")
  16.     assert(getmetatable(v2) == Vector2, argAssert:format("input", "Vector2"))
  17.    
  18.     return v2
  19. end
  20.  
  21. function Vector2.new(x, y)
  22.     local x = x or 0
  23.     assertType(x, 'x', "number")
  24.    
  25.     local y = y or 0
  26.     assertType(y, 'y', "number")
  27.    
  28.     local v2 = {}
  29.     v2.X = x
  30.     v2.Y = y
  31.    
  32.     return setmetatable(v2, Vector2)
  33. end
  34.  
  35. function Vector2:__index(k)
  36.     if k == "Magnitude" then
  37.         return math.sqrt(self.X ^ 2 + self.Y ^ 2)
  38.     elseif k == "Unit" then
  39.         return self / self.Magnitude
  40.     elseif k:sub(1, 2) ~= "__" then
  41.         return rawget(self, k)
  42.     end
  43. end
  44.  
  45. function Vector2:__newindex(k,v)
  46.     error("Vector2 is read-only")
  47. end
  48.  
  49. function Vector2:__tostring()
  50.     return self.X .. ", " .. self.Y
  51. end
  52.  
  53. function Vector2:__add(other)
  54.     self = assertVector2(self)
  55.     other = assertVector2(other)
  56.    
  57.     return Vector2.new(self.X + other.X, self.Y + other.Y)
  58. end
  59.    
  60. function Vector2:__sub(other)
  61.     self = assertVector2(self)
  62.     other = assertVector2(other)
  63.    
  64.     return Vector2.new(self.X - other.X, self.Y - other.Y)
  65. end    
  66.    
  67. function Vector2:__mul(other)
  68.     self = assertVector2(self)
  69.     other = assertVector2(other)
  70.    
  71.     return Vector2.new(self.X * other.X, self.Y * other.Y)
  72. end
  73.    
  74. function Vector2:__div(other)
  75.     self = assertVector2(self)
  76.     other = assertVector2(other)
  77.    
  78.     return Vector2.new(self.X / other.X, self.Y / other.Y)
  79. end
  80.  
  81. function Vector2:__eq(other)
  82.     self = assertVector2(self)
  83.     other = assertVector2(other)
  84.    
  85.     return self.X == other.X and self.Y == other.Y
  86. end
  87.  
  88. ---------------------------------------------------------------------------------------------------------------------
  89.  
  90. local Maze =
  91. {
  92.     Width = 119;
  93.     Height = 19;
  94.    
  95.     Wall = utf8.char(0x2588);
  96.     Open = ' ';
  97.  
  98.     Directions =
  99.     {
  100.         ['←'] = Vector2.new(-1,  0);
  101.         ['→'] = Vector2.new( 1,  0);
  102.         ['↑'] = Vector2.new( 0, -1);
  103.         ['↓'] = Vector2.new( 0,  1);
  104.     }
  105. }
  106.    
  107. function Maze:Generate(seed)
  108.     self.Grid = {}
  109.    
  110.     self.Width = (math.floor(self.Width / 2) * 2) + 1
  111.     self.Height = (math.floor(self.Height / 2) * 2) + 1
  112.    
  113.     for y = 1, self.Height do
  114.         self.Grid[y] = {}
  115.         for x = 1, self.Width do
  116.             self.Grid[y][x] = self.Wall
  117.         end
  118.     end
  119.    
  120.     if seed then
  121.         math.randomseed(seed)
  122.     end
  123.    
  124.     local function visit(from, to)
  125.         local x, y = to.X, to.Y
  126.        
  127.         if x >= 1 and x <= self.Width then
  128.             if y >= 1 and y <= self.Height then
  129.                 if self.Grid[y][x] == self.Wall then
  130.                     local mid = (from + to) / 2
  131.                     self.Grid[mid.Y][mid.X] = self.Open
  132.                     self.Grid[y][x] = self.Open
  133.                    
  134.                     local dirs = {}
  135.                    
  136.                     for _, dir in pairs(self.Directions) do
  137.                         table.insert(dirs, dir * 2)
  138.                     end
  139.                    
  140.                     while #dirs > 0 do
  141.                         local index = math.random(1, #dirs)
  142.                         local dir = table.remove(dirs, index)
  143.                         visit(to, to + dir)
  144.                     end
  145.                 end
  146.             end
  147.         end
  148.     end
  149.    
  150.     local start = Vector2.new(2, 2)
  151.     visit(start, start)
  152. end    
  153.  
  154. function Maze:Solve()
  155.     local startPos = Vector2.new(1, 2)
  156.     self.Grid[startPos.Y][startPos.X] = 'S'
  157.    
  158.     local endPos = Vector2.new(self.Width, self.Height - 1)
  159.     self.Grid[endPos.Y][endPos.X] = 'E'
  160.    
  161.     local visited = {}
  162.     local path = {}
  163.     local g = 0
  164.    
  165.     for x = 1, self.Width do
  166.         visited[x] = {}
  167.         for y = 1, self.Height do
  168.             visited[x][y] = (self.Grid[y][x] == self.Wall)
  169.         end
  170.     end
  171.    
  172.     local function aStar(pos)
  173.         visited[pos.X][pos.Y] = true
  174.        
  175.         if pos == endPos then
  176.             return true
  177.         end
  178.        
  179.         while true do
  180.             local lowestF, bestId = math.huge      
  181.        
  182.             for id, dir in pairs(self.Directions) do
  183.                 local new = pos + dir        
  184.                 if new.X >= 1 and new.X <= self.Width then
  185.                     if new.Y >= 1 and new.Y <= self.Height then
  186.                         if not visited[new.X][new.Y] then
  187.                             local h = (endPos - new).Magnitude
  188.                             local f = g + h
  189.                            
  190.                             if f < lowestF then
  191.                                 lowestF = f
  192.                                 bestId = id
  193.                             end
  194.                         end
  195.                     end
  196.                 end
  197.             end
  198.            
  199.             if bestId then
  200.                 local bestDir = self.Directions[bestId]
  201.                 g = g + bestDir.Magnitude
  202.                 table.insert(path, bestId)
  203.                
  204.                 local found = aStar(pos + bestDir)
  205.                 if found then
  206.                     return true
  207.                 else
  208.                     g = g - 1
  209.                     table.remove(path)
  210.                 end
  211.             else
  212.                 return false
  213.             end
  214.         end
  215.     end
  216.    
  217.     aStar(startPos, startPos)
  218.    
  219.     local prev = startPos
  220.    
  221.     for i, nodeId in ipairs(path) do
  222.         if nodeId ~= startPos then
  223.             local node = prev + self.Directions[nodeId]
  224.             if node ~= endPos then
  225.                 local nextId = path[i + 1]
  226.                 self.Grid[node.Y][node.X] = nextId
  227.                 prev = node
  228.             end
  229.         end
  230.     end
  231.    
  232.     return path
  233. end
  234.  
  235. function Maze:Render()
  236.     for y = 1, self.Height do
  237.         local row = ""
  238.         for x = 1, self.Width do
  239.             row = row .. self.Grid[y][x]
  240.         end
  241.         print(row)
  242.     end
  243. end
  244.  
  245. Maze:Generate()
  246. Maze:Solve()
  247. Maze:Render()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement