prime迷宫生成
- 结果展示
- 算法解析
- 实现代码
结果展示
算法解析
参考链接
Prime迷宫生成算法的原理:
(1)初始地图所有位置均设为墙
(2)任意插入一个墙体进墙队列
(3)判断此时墙体是否可以设置为路(判断依据在于上下左右四个位置是否只有一个位置是路)
(4)若设置为路,则将该位置周围(上下左右)的所有墙插入队列,接着执行(5);若无法设置为路,直接执行(5)
(5)从墙队列中删去当前位置所在节点
(6)若墙队列不为空,则从队列中随机选取一面墙重新执行(3),直到墙队列为空
实现代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>迷宫</title><style>table {margin: 0 auto;border-collapse: collapse;border: 1px solid #ddd;}td {width: 18px;height: 18px;box-sizing: border-box;border:1px solid #eee}td.wall {background: #eee;border: 1px solid #eee;}.now {background: #ffbd2a;}.now.success {background: #05dd8c}.map{width:100%;padding:20px;}</style>
</head>
<body>
<div class="map"></div>
<br>
<p style="text-align: center">控制台输入loadMap(10,10)初始化地图</p>
<script>let mapEl = document.querySelector('.map');loadMap(20, 20);//初始化/**@param w :横宽*@param h :纵高*/function loadMap(w, h) {let i, j, k;//1) 随机二维数组作为原始地图,实际上迷宫的地图要小一圈let mapData = [],//原始地图数据wallQueue = [];//墙块队列for (i = 0; i < h; i++) {mapData[i] = [];for (j = 0; j < w; j++) {k = i + '-' + j;mapData[i][j] = {x: j, y: i,k: k};//除最外层均标记为墙mapData[i][j].val = (i == 0 || i == h - 1 || j == 0 || j == w - 1)?1:0;}}//2)指定需要挖掉的墙作为入口,这里均以左侧墙上随机随机位置let exitBlock = mapData[h - 2][2 + ~~(Math.random() * (w - 4))];wallQueue.push(exitBlock);//3)开始挖路let lastRoad,//最后一块挖出的路ri, wall;while (wallQueue.length > 0) {ri = ~~(Math.random() * wallQueue.length);//从墙队列中随机一个wall = wallQueue[ri];//随机墙//检测上下左右是否有路,true=>设置为路,移除;false=>移除let mark = 0,y = wall.y,//检测墙的纵坐标x = wall.x,//检测墙的横坐标//周围需要检测的点 //[[-1,0],[0,1],[1,0],[-1,0]] 上下左右 [y,x]relArr = [[(y - 1), x], [y, (x + 1)], [(y + 1), x], [y, (x - 1)]],relIndex,relBlock;for (i = 0; (relIndex = relArr[i]) != null; i++) {relBlock = mapData[relIndex[0]][relIndex[1]];if (relBlock && relBlock.val == 1) {mark++}}wallQueue.splice(ri, 1);//移除当前块if (mark == 1) {wall.val = 1;lastRoad = wall;//如果当前块为路,将周围块加入队列for (i = 0; (relIndex = relArr[i]) != null; i++) {relBlock = mapData[relIndex[0]][relIndex[1]];if (relBlock.val == 0) {wallQueue.push(relBlock);}}}}/**设置出口,prime算法只是在特定的地图内挖出若干条线,但不会挖通地图,因此可以有两种模式:* 1)入口和出口,出口需要遍历边缘打通* 2)唯一的缺口作为出口,最后一个打通的块作为出发点*///4) 打印let resultStr = '';for (i = 0; i < h; i++) {resultStr += '<tr>';for (j = 0; j < w; j++) {if (i > 0 && i < h - 1 && j > 0 && j < w - 1) {let b = mapData[i][j];resultStr += '<td class="' + (b.val == 1 ? (lastRoad.k == b.k ? 'now' : '') : 'wall') + '" loc="' + b.k + '"></td>';}}resultStr += '</tr>'}mapEl.innerHTML = '<table>' + resultStr + '</table>';//移动document.addEventListener('keyup', move);function move(e) {//w:87 a:65 s:83 d:68let direct = {w: [-1, 0],//上移d: [0, 1],//右移s: [1, 0],//下移a: [0, -1]//左移}[e.key];if (direct) {//获取当前位置let moveFlag = 0,locNow = document.querySelector('.now').getAttribute('loc').split('-');locNow[0] = +locNow[0] + direct[0];locNow[1] = +locNow[1] + direct[1];if (locNow[0] == exitBlock.y && locNow[1] == exitBlock.x) {moveFlag = 1;document.removeEventListener('keyup', move);} else {if (locNow[0] > 1 && locNow[0] < h - 2 && locNow[1] > 1 && locNow[1] < w - 2) {moveFlag = 2;}}if (moveFlag) {//移动if (mapData[locNow[0]][locNow[1]].val == 1) {let target = document.querySelector('[loc = "' + locNow[0] + '-' + locNow[1] + '"]');document.querySelector('.now').classList.remove('now');target.classList.add('now');if (moveFlag == 1) {target.classList.add('now', 'success');}}}}}}</script></body>
</html>