-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmaze.lua
More file actions
189 lines (160 loc) · 6.68 KB
/
Copy pathmaze.lua
File metadata and controls
189 lines (160 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
-- maze generation
-- http://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search, recursive backtracker
-- representation of node coordinate (row,coloumn)=(i,j) -> (i-1)*n+j, i=1..n, j=1...m
-- representation of walls: below node k --> k, left of node k --> k+m.n
-- good overview of maze generation algorithms using javascript/html5
-- http://www.jamisbuck.org/presentations/rubyconf2011/index.html#recursive-backtracker
-- helper functions
--stack in lua
local stack={};
function stack.push(s,e) s[#s+1]=e end
function stack.pop(s) local r = s[#s];s[#s]=nil;return r end
--function table2string(s) local r = ""; for i,v in pairs(s) do r = r.. " ["..i.."]=".. v ; end return r end
function maze_deep_first_search(m,n,start,seed) -- returns a table of strings representing line renders
local steps,maxsteps; steps= 0; maxsteps = 999999;
local maze = {}
maze.m = m; maze.n = n;
maze.unvisited = {};maze.stack = {}; maze.walls = {};
maze.free = maze.m*maze.n;
local i,j,k
local nb,wall -- unvisited neighbbors, walls
--init structures
for i=1,maze.m do
for j =1,maze.n do
k=(i-1)*maze.n+j;maze.unvisited[k]=true -- initially all cells unvisited
maze.walls[k]=true;maze.walls[k+maze.n*maze.m]=true; -- walls are there
end
end
math.randomseed(seed)
maze.current = start
maze.unvisited [ maze.current ] = false;
maze.free = maze.free-1; maze.stack[1+#maze.stack] = maze.current
while maze.free>0 and steps<maxsteps do -- main loop
steps=steps+1
-- check current node neighbors
k=maze.current
j = k % maze.n;i=math.ceil(k/maze.n); -- get coords back from index
if j==0 then j = maze.n end
--print("coords current node "..k .. " = " .. i .. " " ..j)
nb={};wall={}-- check unvisited neighbors & wall removals
if i>1 then -- down
k=(i-2)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k+maze.n;nb[#nb+1]=k end
end
if i<maze.m then -- up
k=(i)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k;nb[#nb+1]=k end
end
if j<maze.n then --right
k=(i-1)*maze.n+j+1; if maze.unvisited[k] then wall[#wall+1]=k+maze.n*maze.m; nb[#nb+1]=k end
end
if j>1 then --left
k=(i-1)*maze.n+j-1; if maze.unvisited[k] then wall[#wall+1]=k+1+maze.n*maze.m;nb[#nb+1]=k end
end
--print(" unvisited neighbors " .. table2string(nb))
if (#nb)>0 then -- if unvisited neighbors, choose random one as next current node
stack.push(maze.stack,maze.current) -- remember previous current node
k=math.random(#nb); -- pick random unvisited neighbor
maze.walls[wall[k]]=false; -- remove wall
--print(" removed wall ".. wall[k])
k=nb[k];
maze.current = k; -- new current cell
maze.unvisited[k]=false; maze.free = maze.free-1 -- one less unvisited
--print("new explore " .. k);
elseif (#maze.stack)~=0 then -- no unvisited neighbors, backtrack using stack
maze.current = stack.pop(maze.stack)
--print("backtrack to "..maze.current)
else -- even stack is empty, just pick random unvisited cell
k = math.random(maze.free); j=1;
for i =1,maze.m*maze.n do
if maze.unvisited[i] then
if j==k then k=i; break end -- pick node
j=j+1
end
end
--print(" stack empty, random pick " ..k)
maze.current=k;maze.unvisited[k]=false; maze.free = maze.free -1;
end
end -- of do
-- render maze with chars, row by row
maze.ret = {};
local hor;local vert;
local wall = "1"
for i=1,maze.m do
hor="";vert="";
k= (i-1)*maze.n;
-- horizontal
for j = 1, maze.n do
k=k+1;
-- if maze.walls[k+maze.n*maze.m] then vert=vert.."X." else vert=vert.. "0." end
-- if maze.walls[k] then hor=hor.."XX" else hor=hor.."X0" end
if maze.walls[k+maze.n*maze.m] then vert=vert..wall.."0" else vert=vert.. "00" end
if maze.walls[k] then hor=hor..wall..wall else hor=hor..wall.."0" end
end
maze.ret[1+#maze.ret]=hor..wall;maze.ret[1+#maze.ret]=vert..wall;
end
maze.ret[1+#maze.ret] = string.rep(wall,2*maze.n+1)
return maze.ret
end
-- RUN PROGRAM
local maze=maze_deep_first_search(10,30,1,2015)
--for _,v in pairs(maze) do print(v) end
core.register_privilege("maze", "Can create mazes")
minetest.register_chatcommand("maze", {
description = "/maze (optional m,n,start,seed) generates maze at players position, directed towards positive x,z axix",
privs = {maze = true},
func = function(name, param)
local m,n,start, seed
if param == "" then m=10;n=10;start=1;seed=1 else
local words = {}; local word
for word in param:gmatch("%w+") do words[1+#words]= word end
if words[1]~=nil then m = tonumber(words[1]) else m = 10 end
if words[2]~=nil then n = tonumber(words[2]) else n = 10 end
if words[3]~=nil then start = tonumber(words[3]) else start = 1 end
if words[4]~=nil then seed = tonumber(words[4]) else seed = 1 end
end
if m*n>1000000 then minetest.chat_send_player(name, "limit maze size to 1000000 cells") return end
local player = minetest.env:get_player_by_name(name); if player==nil then return end
local pos = player:getpos();local p
local maze=maze_deep_first_search(m,n,start,seed) -- m,n,start,seed
local i,j,k;local p = {x=pos.x,y=pos.y,z=pos.z};
for i,v in pairs(maze) do
p.x = pos.x+i
for k = 1,string.len(v) do
p.z=pos.z+k
if string.sub(v,k,k)=="1" then
minetest.set_node(p,{name="games:stone_maze"})
else minetest.set_node(p,{name="air"})
end
end
end
end,
})
minetest.register_node("games:glass_maze", {
description = "maze_glass",
drawtype = "glasslike_framed_optional",
tiles = {"maze_glass.png"},
inventory_image = minetest.inventorycube("maze_glass.png"),
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {immortal = 1,disable_jump=1},
sounds = default.node_sound_glass_defaults(),
})
minetest.register_node("games:stone_maze", {
description = "maze_wall",
tiles = {"default_brick.png"},
is_ground_content = true,
groups = {immortal = 1,disable_jump=1},
legacy_mineral = true,
sounds = default.node_sound_stone_defaults(),
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
local name = player:get_player_name(); if name == nil then return end
if minetest.is_protected(pos, name) then
return
end
local player_inv = player:get_inventory()
if player_inv:room_for_item("main", {"games:stone_maze"}) then
player_inv:add_item("main", "games:stone_maze")
minetest.remove_node(pos) -- remove bones
end
end
})