迷宫生成
1、生成数组
首先,设置一个储存宽高的数组,名称rect,将这个储存宽高的数组,可以通过x*y得出所有内部分割方块的坐标;
let rect = [5, 5];
let side = 495 / 5;
let arr = Array.from({ length: rect[0] }, (_, x) => {
return Array.from({ length: rect[1] }, (_, y) => {
return {
visited: false,
axis: [x, y],
path: [true, true, true, true]
}
})
})
通过Array.from 设置类数组对象转化为多维数组,以此得到每一个位置的坐标
2、编辑canvas绘画方法
let myCanvas = document.getElementById("myCanvas");
let ctx = myCanvas.getContext("2d");
let draw = {
top ([x, y]) {
ctx.moveTo(x * side, y * side);
ctx.lineTo((x + 1) * side, y * side);
ctx.stroke();
},
right ([x, y]) {
ctx.moveTo((x + 1) * side, y * side);
ctx.lineTo((x + 1) * side, (y + 1) * side);
ctx.stroke();
},
bottom ([x, y]) {
ctx.moveTo((x + 1) * side, (y + 1) * side);
ctx.lineTo((x) * side, (y + 1) * side);
ctx.stroke();
},
left ([x, y]) {
ctx.moveTo((x) * side, (y + 1) * side);
ctx.lineTo((x) * side, (y) * side);
ctx.stroke();
}
}
arr.forEach(arrIndex => {
arrIndex.forEach(item => {
if (item.path[0]) draw.top(item.axis)
if (item.path[1]) draw.right(item.axis)
if (item.path[2]) draw.bottom(item.axis)
if (item.path[3]) draw.left(item.axis)
})
})
获取canvas,思路是通过对每一个分割出来的方块画出其边长,形成一个x*y的宫格,然后对其拆除墙体,实现迷宫
画出方块的对象方法,是根据上右下左来进行书写的,最后通过两轮循环,对所有的位置进行绘画。path 如果为false就代表不需要绘画,path数据初始化为[true,true,true,true],后续对其进行修改,以做到分割单元格的目的
3、进行迷宫生成
-
我们找到当前位置的四个的相邻元素
-
相邻元素是否可以访问,是否没有被遍历过
-
如果没有被遍历过,就作为可访问数组来确定下一次走到的位置,
-
将走到的位置加入stack内,我们不停的遍历入栈后这个元素的所有边
-
可以形成一条通路到最后一个元素
3-1、设置初始位置
我们选择[0,0]作为初始位置开始生成
let current = { axis: [0, 0], dir: undefined };
arr[0][0].visited = true;
3-2、寻找可选方向
寻找四个方向,如果到墙壁就返回,如果被访问过的也返回,我们的目的是将所有没有返回过的数组进行操作,并记录他相对于初始位置的方向,便于消除墙壁
function find_position ([x, y], dir) {
switch (dir) {
case 0:
if (y - 1 < 0) return
if (arr[x][y - 1].visited) return
return { axis: [x, y - 1], dir }
case 1:
if (x + 1 >= rect[0]) return
if (arr[x + 1][y].visited) return
return { axis: [x + 1, y], dir }
case 2:
if (y + 1 >= rect[1]) return
if (arr[x][y + 1].visited) return
return { axis: [x, y + 1], dir }
case 3:
if (x - 1 < 0) return
if (arr[x - 1][y].visited) return
return { axis: [x - 1, y], dir }
}
}
3-3、将可选方向作为数组返回
[0,1,2,3]分别对应上右下左,存在能进行下一步操作的元素才进行收集
function find_neighbor ([x, y]) {
let res = []
let top = find_position([x, y], 0);
let right = find_position([x, y], 1);
let bottom = find_position([x, y], 2);
let left = find_position([x, y], 3);
if (top) res.push(top);
if (right) res.push(right);
if (bottom) res.push(bottom);
if (left) res.push(left);
return res
}
3-4、随机当前元素的可选地址于上一个元素的墙壁进行联通
let random = Math.floor(Math.random() * find_res.length);
clear_rect(current, find_res[random]);
消除墙壁,当下一个元素和当前元素的墙壁消掉后,将其可访问值设置为true,使其下一次不会被选中
function clear_rect (now_pos, next_pos) {
let now = arr[now_pos.axis[0]][now_pos.axis[1]];
let next = arr[next_pos.axis[0]][next_pos.axis[1]];
next.visited = true
switch (next_pos.dir) {
case 0:
next.path[2] = false
now.path[0] = false;
break;
case 1:
next.path[3] = false;
now.path[1] = false;
break;
case 2:
now.path[2] = false;
next.path[0] = false
break;
case 3:
next.path[1] = false;
now.path[3] = false;
break;
}
3-5、使用堆栈对其进行遍历
function create_maze (arr) {
let current = { axis: [0, 0], dir: undefined };
arr[0][0].visited = true;
let stack = [current]
while (stack.length > 0) {
let find_res = find_neighbor(current.axis);
if (find_res.length > 0) {
let random = Math.floor(Math.random() * find_res.length);
clear_rect(current, find_res[random]);
current = find_res[random];
stack.push(current);
} else {
current = stack.pop()
}
}
}
设置一个初始位置为初始栈,只要当前栈还有可选位置元素,就会对当前位置进行四个边的查找,没有对应的元素就会让其退出栈,直到所有元素都被访问过,才会退出循环,我们查找到下一次的边如果还存在可选边就会将其加入栈内,如果随机到一个位置恰好四个边都被访问,但还没有完全遍历整个迷宫,我们就会将其出栈,使其作为新的可选位置元素,使用pop出栈,他会从最后一位元素开始重新查找