Prime演算法生成迷宮
阿新 • • 發佈:2021-02-04
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:68
let 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>