MYBLOG

欢迎来到小马哥的个人博客~

[转载]用JS写的一个随机的迷宫

2020-03-08学海无涯

公司需要写一个迷宫游戏,时间又有大把,就自己写一个不在网上找了。网上查了了几种算法,也实验了,觉得这种比较
 
好。但是缺点就是,格数多了,绘制需要很长时间+_+,其他的还行吧。
具体算法是这样的:
‘首先将迷宫分成若干个正方形的单元格,并随机选中一个作为起始点(start)。
将正被访问的单元格标记为已访问,得到它所有相邻单元格。在这些相邻的单元格中随机选择一个,如果这个被选中的单
 
元格没有被访问过, 那么移掉正被访问单元格和被选中单元格之间的墙体,并将这个被选中单元格作为正被访问单元格。
 
如果正被访问单元格的所有相邻单元格都被访问过。 那么在所有被访问过的单元格(这里指迷宫中所有已被访问过的单元
 

格)中随机选中一个作为正被访问单元格,如此递归下去,直到迷宫中所有的单元格都被访问过为止。


<!DOCTYPE html> 
<html> 
<head lang="en"> 
    <meta charset="UTF-8"> 
    <meta name="viewport" content="width=device-width,  initial-scale=1.0, maximum-scale=1.0,  user-scalable=no"> 
    <meta name="format-detection" content="telephone=no" /> 
    <meta name="apple-mobile-web-app-capable" content="yes" /> 
    <meta name="apple-mobile-web-app-status-bar-style" content="black" /> 
    <title></title> 
    <style> 
html {  -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; font-size: 62.5%; } 
 
body {font-family: helvetica, sans-serif; font-size:1.2rem;margin: 0; line-height: 20px; color: #000; background-color: #fff; height: 100%; overflow-x: hidden; -webkit-overflow-scrolling: touch; } 
 
h1, h2, h3, h4, h5, h6, p, figure, form, blockquote { margin: 0; } 
ul, ol, li, dl, dd { margin: 0; padding: 0; } 
ul, ol { list-style: none; } 
input,button,select,textarea{outline:none;border:none;} 
   body{ 
    position: absolute; 
    top: 0; 
    bottom: 0; 
    width: 100%; 
    background-color: yellow; 
} 
#labyrinthDiv{ 
    position: relative; 
} 
#labyrinthDiv>canvas{ 
    position: absolute; 
    left: 0; 
    top:0; 
} 
#labyrinth{ 
    z-index: 2; 
} 
#people{ 
    z-index: 1; 
    background-color: #fff; 
} 
#loading{ 
    position: absolute; 
    padding: 1rem; 
    box-shadow: 0 0 5px 1px #333; 
    z-index: 3; 
    top: 50%; 
    left: 50%; 
    transform: translate(-50%,-50%); 
    font-size: 20px; 
    background-color: #fff; 
    color: red; 
} 
#btn{ 
    margin: 1rem; 
    overflow: hidden; 
} 
#btn>li{ 
    float: left; 
    height: 3rem; 
    margin-left: 1.5rem; 
} 
#btn input{ 
    width: 3.5rem; 
    padding-left: 1.5rem; 
    height: 100%; 
} 
#btn #confirm{ 
    width: 5rem; 
    height: 100%; 
    background-color: red; 
    cursor: pointer; 
} 
   </style> 
</head> 
<body> 
<section class="body"> 
    <div id="loading">地图绘制中,并没有卡死...</div> 
    <ul id="btn"> 
        <li>难度(number):<input id="level" type="number"/></li> 
        <li><button id="confirm">绘制</button></li> 
    </ul> 
<div id="labyrinthDiv"> 
    <canvas id="labyrinth" width='600' height='600'></canvas> 
    <canvas id="people" width='600' height='600'></canvas> 
</div> 
</section> 
<script src="http://zeptojs.com/zepto.min.js"></script> 
<script src="http://www.css88.com/doc/underscore/underscore.js"></script> 
<script> 
    /** 
 * Created by Administrator on 2016/1/4. 
 */ 
var timeout=setTimeout(function(){ 
 
 
var labyrinthDom=$("#labyrinth")[0], 
    labyrinthObj=labyrinthDom.getContext('2d'),//迷宫图层 
    peopleDom=$("#people")[0], 
    peopleObj=peopleDom.getContext('2d'),//人物图层 
    labyrinthWidth=document.body.offsetWidth>600?600:document.body.offsetWidth,//canvas宽度; 
    NUM=20; 
while(labyrinthWidth%NUM!==0){//NUM*NUM的矩阵 
    labyrinthWidth--; 
} 
//大小设置好 
canvasInit(labyrinthDom); 
canvasInit(peopleDom); 
//@作者:詹真琦 @日期:2016/1/8 15:44 @描述:开始绘制 
$("#confirm").on('click',function(){ 
    $("#loading").show(); 
    NUM=+$("#level").val()||20; 
    var timer=setTimeout(function(){ 
        init(); 
        clearTimeout(timer); 
    },3000); 
}); 
//开始 
init(); 
var roomArr,takeRooms,takeRoomsIndex,visitRooms,index,wid; 
function init(){ 
        labyrinthDomlabyrinthDom.width=labyrinthDom.width; 
        peopleDompeopleDom.width=peopleDom.width; 
        roomArr=map(NUM),//所有房间存放,纵横NUM个,一共NUM*NUM个 
        takeRooms=[1],//走过的房间路径 
        takeRoomsIndex=0,//当前在走过的房间的第几个 
        visitRooms=[1],//哪些房间访问过 
        index= 1,//当前人在第几个房间的位置; 
        wid=labyrinthWidth/NUM;//一格的宽高 
 
roomArr[1].isVisit=true;//默认在第一个房间 
drawPeople(index);//画出人物 
//出口入口标示 
labyrinthObj.fillStyle='green'; 
labyrinthObj.font = ""+wid/2+"px Arial"; 
labyrinthObj.textAlign='left'; 
labyrinthObj.fillText('入口',0,wid-3); 
labyrinthObj.fillText('出口',((NUM*NUM-1)%NUM)*wid,NUM*wid-3); 
 
do{ 
    chooseWall(takeRooms[takeRoomsIndex]); 
    if(visitRooms.length===NUM*NUM){//绘制完成,跳出循环 
        drawWall(roomArr); 
        $("#loading").hide(); 
        break; 
    } 
} 
while(1);//开始绘制 
} 
//@作者:詹真琦 @日期:2016/1/6 14:25 @描述:canvas尺寸 
function canvasInit(dom){ 
 dom.style.marginLeft=(document.body.offsetWidth-labyrinthWidth)/2+'px';//居中 
    dom.width=labyrinthWidth; 
    dom.height=labyrinthWidth; 
} 
//@作者:詹真琦 @日期:2016/1/5 10:42 @描述:创建所有格子 1代表有墙,0代表没得墙 
function map(x){ 
    var arr=new Array(x*x);//总共x*x个格子 
    for(var i=0;i<x*x;i++){ 
        arr[i+1]={ 
            index:i+1,//当前坐标 
            isVisit:false,//是否访问过 
            allVisite:false,//周围的是否都访问过 
            top:1,//上面的墙 
            left:1,//左边的墙 
            bottom:1,//下面的墙 
            right:1//右边的墙 
        }; 
    } 
    return arr; 
} 
 
//@作者:詹真琦 @日期:2016/1/5 11:30 @描述:随机选择一个没有访问过的区域 
function chooseWall(i){ 
var near=nearVisite(i); 
    if(roomArr[i].allVisite||near.isNearVisitive){//周围都是访问过的房间,返回上一个房间 
        takeRoomsIndex=parseInt(Math.random()*(takeRooms.length-1));//随机选择之前的一条路 
        return false; 
    } 
    var dir=near.dirArr[parseInt(Math.random()*near.dirArr.length)]; 
    hitWall(dir,i); 
} 
 
//@作者:詹真琦 @日期:2016/1/6 16:52 @描述:打通下一个房间墙壁 
function hitWall(dir,i){ 
    var furtherRoom= 0,//下一个房间 
        furtherRoomWall= '';//下一个房间该打通的墙壁 
    switch (dir){//判断下一个房间位置,以及需要打通的墙壁位置 
        case 'top': 
            i-NUM>0?furtherRoom=i-NUM:furtherRoom=0; 
            furtherRoomWall='bottom'; 
            break; 
        case 'left': 
            (i-1)%NUM!==0?furtherRoom=i-1:furtherRoom=0; 
            furtherRoomWall='right'; 
            break; 
        case 'bottom': 
            i+NUM<=NUM*NUM?furtherRoom=i+NUM:furtherRoom=0; 
            furtherRoomWall='top'; 
            break; 
        case 'right': 
            i%NUM!==0?furtherRoom=i+1:furtherRoom=0; 
            furtherRoomWall='left'; 
            break; 
    } 
    //进入下一个房间,并且打通墙壁 
    roomArr[i][dir]=0; 
    roomArr[furtherRoom][furtherRoomWall]=0; 
    roomArr[furtherRoom].isVisit=true; 
    if(!nearVisite(furtherRoom).isNearVisitive){//如果进入这个房间之后,附近的都访问过,就不保存 
        takeRooms.push(furtherRoom);//这个房间存入走过的房间 
        takeRoomsIndex++;//位于走过的房间的此位置 
    } 
    //访问过的送油房间集合(不重复) 
    visitRooms= _.union(visitRooms,[furtherRoom]); 
} 
//@作者:詹真琦 @日期:2016/1/5 14:38 @描述:判断周围是否都是访问过的房间 
function nearVisite(i){ 
    var top,bottom,left,right,dir=[]; 
    if(roomArr[i-NUM]&&roomArr[i-NUM].isVisit||i-NUM<=0){ 
        top=true; 
    }else{ 
        dir.push('top'); 
    } 
    if(roomArr[i+NUM]&&roomArr[i+NUM].isVisit||i+NUM>NUM*NUM){ 
        bottom=true; 
    }else{ 
        dir.push('bottom'); 
    } 
    if(roomArr[i-1]&&roomArr[i-1].isVisit||(i-1)%NUM===0){ 
        left=true; 
    }else{ 
        dir.push('left'); 
    } 
    if(roomArr[i+1]&&roomArr[i+1].isVisit||i%NUM===0){ 
        right=true; 
    }else{ 
        dir.push('right'); 
    } 
    if(top&&bottom&&left&&right){ 
        roomArr[i].allVisite=true; 
        return {isNearVisitive:true,dirArr:dir}; 
    }else{ 
        return {isNearVisitive:false,dirArr:dir}; 
    } 
} 
//@作者:詹真琦 @日期:2016/1/6 10:48 @描述:绘制墙壁 
function drawWall(room){ 
    var row=1,//第几行  1开始 
        column=1;//第几列  1开始 
    for(var i=1;i<room.length;i++){ 
        row=Math.ceil(i/NUM); 
        i%NUM===0?column=(i-1)%NUM+1:column=i%NUM; 
        if (room[i].top){ 
            labyrinthObj.moveTo((column-1)*wid,(row-1)*wid); 
            labyrinthObj.lineTo(column*wid,(row-1)*wid); 
        } 
        if(room[i].bottom){ 
            labyrinthObj.moveTo(column*wid,row*wid); 
            labyrinthObj.lineTo((column-1)*wid,row*wid); 
        } 
        if(room[i].left) { 
            labyrinthObj.moveTo((column-1)*wid,row*wid); 
            labyrinthObj.lineTo((column-1)*wid,(row-1)*wid); 
         } 
        if(room[i].right){ 
            labyrinthObj.moveTo(column*wid,(row-1)*wid); 
            labyrinthObj.lineTo(column*wid,row*wid); 
        } 
    } 
    labyrinthObj.stroke(); 
} 
 
//@作者:詹真琦 @日期:2016/1/6 14:35 @描述:绑定键盘事件 
$(document).on('keydown',function(e){ 
    move(e.keyCode,index); 
}); 
 
//@作者:詹真琦 @日期:2016/1/6 14:35 @描述:行走 
function move(keyCode,i){ 
    switch (keyCode){ 
        case 37: 
            if(!roomArr[index].left){ 
                i--; 
            } 
            break; 
        case 38: 
            if(!roomArr[index].top){ 
                i-=NUM; 
            } 
            break; 
        case 39: 
            if(!roomArr[index].right){ 
                i++; 
            } 
            break; 
        case 40: 
            if(!roomArr[index].bottom){ 
                i+=NUM; 
            } 
            break; 
    } 
    iindex=i; 
    drawPeople(index); 
    if(index===NUM*NUM){ 
        alert('通关成功!') 
    } 
} 
 
//@作者:詹真琦 @日期:2016/1/6 14:39 @描述:画人 
function drawPeople(i){ 
    var row=Math.ceil(i/NUM), 
        column=1; 
    i%NUM===0?column=(i-1)%NUM+1:column=i%NUM; 
    peopleDompeopleDom.width=peopleDom.width; 
    peopleObj.fillStyle='skyblue'; 
    peopleObj.fillRect((column-1)*wid,(row-1)*wid,wid,wid) 
} 
clearTimeout(timeout); 
}) 
</script> 
</body> 
</html> 
javascript 代码片段



/** 
 * Created by Administrator on 2016/1/4. 
 */ 
var labyrinthDom=$("#labyrinth")[0], 
    labyrinthObj=labyrinthDom.getContext('2d'),//迷宫图层 
    peopleDom=$("#people")[0], 
    peopleObj=peopleDom.getContext('2d'),//人物图层 
    labyrinthWidth=document.body.offsetWidth>600?600:document.body.offsetWidth,//canvas宽度; 
    NUM=20; 
while(labyrinthWidth%NUM!==0){//NUM*NUM的矩阵 
    labyrinthWidth--; 
} 
//大小设置好 
canvasInit(labyrinthDom); 
canvasInit(peopleDom); 
//@作者:詹真琦 @日期:2016/1/8 15:44 @描述:开始绘制 
$("#confirm").on('click',function(){ 
    $("#loading").show(); 
    NUM=+$("#level").val()||20; 
    var timer=setTimeout(function(){ 
        init(); 
        clearTimeout(timer); 
    },3000); 
}); 
//开始 
init(); 
var roomArr,takeRooms,takeRoomsIndex,visitRooms,index,wid; 
function init(){ 
        labyrinthDom.width=labyrinthDom.width; 
        peopleDom.width=peopleDom.width; 
        roomArr=map(NUM),//所有房间存放,纵横NUM个,一共NUM*NUM个 
        takeRooms=[1],//走过的房间路径 
        takeRoomsIndex=0,//当前在走过的房间的第几个 
        visitRooms=[1],//哪些房间访问过 
        index= 1,//当前人在第几个房间的位置; 
        wid=labyrinthWidth/NUM;//一格的宽高 
 
roomArr[1].isVisit=true;//默认在第一个房间 
drawPeople(index);//画出人物 
//出口入口标示 
labyrinthObj.fillStyle='green'; 
labyrinthObj.font = ""+wid/2+"px Arial"; 
labyrinthObj.textAlign='left'; 
labyrinthObj.fillText('入口',0,wid-3); 
labyrinthObj.fillText('出口',((NUM*NUM-1)%NUM)*wid,NUM*wid-3); 
 
do{ 
    chooseWall(takeRooms[takeRoomsIndex]); 
    if(visitRooms.length===NUM*NUM){//绘制完成,跳出循环 
        drawWall(roomArr); 
        $("#loading").hide(); 
        break; 
    } 
} 
while(1);//开始绘制 
} 
//@作者:詹真琦 @日期:2016/1/6 14:25 @描述:canvas尺寸 
function canvasInit(dom){ 
    dom.style.marginLeft=(document.body.offsetWidth-labyrinthWidth)/2+'px';//居中 
    dom.width=labyrinthWidth; 
    dom.height=labyrinthWidth; 
} 
//@作者:詹真琦 @日期:2016/1/5 10:42 @描述:创建所有格子 1代表有墙,0代表没得墙 
function map(x){ 
    var arr=new Array(x*x);//总共x*x个格子 
    for(var i=0;i<x*x;i++){ 
        arr[i+1]={ 
            index:i+1,//当前坐标 
            isVisit:false,//是否访问过 
            allVisite:false,//周围的是否都访问过 
            top:1,//上面的墙 
            left:1,//左边的墙 
            bottom:1,//下面的墙 
            right:1//右边的墙 
        }; 
    } 
    return arr; 
} 
 
//@作者:詹真琦 @日期:2016/1/5 11:30 @描述:随机选择一个没有访问过的区域 
function chooseWall(i){ 
var near=nearVisite(i); 
    if(roomArr[i].allVisite||near.isNearVisitive){//周围都是访问过的房间,返回上一个房间 
        takeRoomsIndex=parseInt(Math.random()*(takeRooms.length-1));//随机选择之前的一条路 
        return false; 
    } 
    var dir=near.dirArr[parseInt(Math.random()*near.dirArr.length)]; 
    hitWall(dir,i); 
} 
 
//@作者:詹真琦 @日期:2016/1/6 16:52 @描述:打通下一个房间墙壁 
function hitWall(dir,i){ 
    var furtherRoom= 0,//下一个房间 
        furtherRoomWall= '';//下一个房间该打通的墙壁 
    switch (dir){//判断下一个房间位置,以及需要打通的墙壁位置 
        case 'top': 
            i-NUM>0?furtherRoom=i-NUM:furtherRoom=0; 
            furtherRoomWall='bottom'; 
            break; 
        case 'left': 
            (i-1)%NUM!==0?furtherRoom=i-1:furtherRoom=0; 
            furtherRoomWall='right'; 
            break; 
        case 'bottom': 
            i+NUM<=NUM*NUM?furtherRoom=i+NUM:furtherRoom=0; 
            furtherRoomWall='top'; 
            break; 
        case 'right': 
            i%NUM!==0?furtherRoom=i+1:furtherRoom=0; 
            furtherRoomWall='left'; 
            break; 
    } 
    //进入下一个房间,并且打通墙壁 
    roomArr[i][dir]=0; 
    roomArr[furtherRoom][furtherRoomWall]=0; 
    roomArr[furtherRoom].isVisit=true; 
    if(!nearVisite(furtherRoom).isNearVisitive){//如果进入这个房间之后,附近的都访问过,就不保存 
        takeRooms.push(furtherRoom);//这个房间存入走过的房间 
        takeRoomsIndex++;//位于走过的房间的此位置 
    } 
    //访问过的送油房间集合(不重复) 
    visitRooms= _.union(visitRooms,[furtherRoom]); 
} 
//@作者:詹真琦 @日期:2016/1/5 14:38 @描述:判断周围是否都是访问过的房间 
function nearVisite(i){ 
    var top,bottom,left,right,dir=[]; 
    if(roomArr[i-NUM]&&roomArr[i-NUM].isVisit||i-NUM<=0){ 
        top=true; 
    }else{ 
        dir.push('top'); 
    } 
    if(roomArr[i+NUM]&&roomArr[i+NUM].isVisit||i+NUM>NUM*NUM){ 
        bottom=true; 
    }else{ 
        dir.push('bottom'); 
    } 
    if(roomArr[i-1]&&roomArr[i-1].isVisit||(i-1)%NUM===0){ 
        left=true; 
    }else{ 
        dir.push('left'); 
    } 
    if(roomArr[i+1]&&roomArr[i+1].isVisit||i%NUM===0){ 
        right=true; 
    }else{ 
        dir.push('right'); 
    } 
    if(top&&bottom&&left&&right){ 
        roomArr[i].allVisite=true; 
        return {isNearVisitive:true,dirArr:dir}; 
    }else{ 
        return {isNearVisitive:false,dirArr:dir}; 
    } 
} 
//@作者:詹真琦 @日期:2016/1/6 10:48 @描述:绘制墙壁 
function drawWall(room){ 
    var row=1,//第几行  1开始 
        column=1;//第几列  1开始 
    for(var i=1;i<room.length;i++){ 
        row=Math.ceil(i/NUM); 
        i%NUM===0?column=(i-1)%NUM+1:column=i%NUM; 
        if (room[i].top){ 
            labyrinthObj.moveTo((column-1)*wid,(row-1)*wid); 
            labyrinthObj.lineTo(column*wid,(row-1)*wid); 
        } 
        if(room[i].bottom){ 
            labyrinthObj.moveTo(column*wid,row*wid); 
            labyrinthObj.lineTo((column-1)*wid,row*wid); 
        } 
        if(room[i].left) { 
            labyrinthObj.moveTo((column-1)*wid,row*wid); 
            labyrinthObj.lineTo((column-1)*wid,(row-1)*wid); 
         } 
        if(room[i].right){ 
            labyrinthObj.moveTo(column*wid,(row-1)*wid); 
            labyrinthObj.lineTo(column*wid,row*wid); 
        } 
    } 
    labyrinthObj.stroke(); 
} 
 
//@作者:詹真琦 @日期:2016/1/6 14:35 @描述:绑定键盘事件 
$(document).on('keydown',function(e){ 
    move(e.keyCode,index); 
}); 
 
//@作者:詹真琦 @日期:2016/1/6 14:35 @描述:行走 
function move(keyCode,i){ 
    switch (keyCode){ 
        case 37: 
            if(!roomArr[index].left){ 
                i--; 
            } 
            break; 
        case 38: 
            if(!roomArr[index].top){ 
                i-=NUM; 
            } 
            break; 
        case 39: 
            if(!roomArr[index].right){ 
                i++; 
            } 
            break; 
        case 40: 
            if(!roomArr[index].bottom){ 
                i+=NUM; 
            } 
            break; 
    } 
    index=i; 
    drawPeople(index); 
    if(index===NUM*NUM){ 
        alert('通关成功!') 
    } 
} 
 
//@作者:詹真琦 @日期:2016/1/6 14:39 @描述:画人 
function drawPeople(i){ 
    var row=Math.ceil(i/NUM), 
        column=1; 
    i%NUM===0?column=(i-1)%NUM+1:column=i%NUM; 
    peopleDom.width=peopleDom.width; 
    peopleObj.fillStyle='skyblue'; 
    peopleObj.fillRect((column-1)*wid,(row-1)*wid,wid,wid) 
}