更多精彩文章请访问我的个人博客(zhuoerhuobi.cn)
纯H5的小游戏,放在了404页面,增添了网站的有趣性。点此试玩
要保证迷宫的可抵达性、随机性、一定的趣味性(复杂度)。
该方案保证了随机性、可抵达性,根据测试,趣味性尚可,在大尺寸迷宫难度下具有一定难度。
该方案保证了随机性、可抵达性(并且唯一),根据测试,复杂度较高,容易出现很长的死胡同(因为遍历产生的特性),但是道路唯一,不存在环路。
先用线条画出指定大小的格子棋盘。
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
$("#canvasDiv").append(canvas);
var boardSize = 14; //将迷宫大小设置为变量,便于关卡变化
var piecePos;
var tree;
var reachable;
var startTime;
var costTime;
var mission;
//将画线抽象成一个动作
function drawLine(ctx, startX, startY, endX, endY) {
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();
ctx.closePath();
}
function drawChessBoard() {
ctx.strokeStyle="#202935";
ctx.lineWidth = 3;
drawLine(ctx,15,15,15,15+30*boardSize);
drawLine(ctx,15,15,15+30*boardSize,15);
for (var i = 1; i < boardSize; i++) {
ctx.strokeStyle="grey";
ctx.lineWidth = 2;
drawLine(ctx,15+i*30,15,15+i*30,15+30*boardSize);
drawLine(ctx,15,15+i*30,15+30*boardSize,15+i*30);
}
ctx.strokeStyle="#202935";
ctx.lineWidth = 3;
drawLine(ctx,15+boardSize*30,15,15+30*boardSize,15+30*boardSize);
drawLine(ctx,15,15+30*boardSize,15+30*boardSize,15+30*boardSize);
}
接下来逻辑上用二维数组代表迷宫,对其使用并查集操作生成迷宫。界面上擦掉连通的节点之间的“墙”。
//tree是表示同一子连通图的树,根节点相同则表示相互连通
function root(i) {
if (tree[i] === i) {
return i;
}
return root(tree[i]);
}
//并操作
function union(i, j) {
if (i < j) {
tree[root(j)] = tree[root(i)];
} else {
tree[root(i)] = tree[root(j)];
}
}
//擦线
function wipe(i,j) {
if ( i > j) {
i = i^j;
j = i^j;
i = i^j;
}
if (j-i === 1) {
ctx.clearRect((j%boardSize)*30+14,Math.floor(j/boardSize)*30+16,2,28);
} else if (j-i === boardSize) {
ctx.clearRect((j%boardSize)*30+16,Math.floor(j/boardSize)*30+14,28,2);
}
}
function drawMaze() {
while (root(0) !== root(boardSize*boardSize-1)) {
let ran = Math.floor(Math.random() * (boardSize * boardSize - 1));
let dest = Math.floor(Math.random() * 4);//0上,1右,2下,3左
if (dest === 0) {
if (ran < boardSize) {
continue;
}
if (root(ran) === root(ran-boardSize)) {
continue;
}
union(ran,ran-boardSize);
reachable[ran][0] = 1; //reachable用来表示两个节点之间可不可以直接移动过去,不代表是否连通
reachable[ran-boardSize][2] = 1;
wipe(ran,ran-boardSize);
} else if (dest === 1) {
if (ran%boardSize === boardSize-1) {
continue;
}
if (root(ran) === root(ran+1)) {
continue;
}
union(ran,ran+1);
reachable[ran][1] = 1;
reachable[ran+1][3] = 1;
wipe(ran,ran+1);
} else if (dest === 2) {
if (ran >= boardSize*(boardSize-1)) {
continue;
}
if (root(ran) === root(ran+boardSize)) {
continue;
}
union(ran,ran+boardSize);
reachable[ran][2] = 1;
reachable[ran+boardSize][0] = 1;
wipe(ran,ran+boardSize);
} else if (dest === 3) {
if (ran%boardSize === 0) {
continue;
}
if (root(ran) === root(ran-1)) {
continue;
}
union(ran,ran-1);
reachable[ran][3] = 1;
reachable[ran-1][1] = 1;
wipe(ran,ran-1);
}
}
}
function drawSquare() {
ctx.fillStyle = 'blue';
ctx.fillRect(20,20,20,20);
ctx.fillStyle = 'red';
ctx.fillRect((boardSize-1)*30+20,(boardSize-1)*30+20,20,20);
}
function wipePiecePos() {
ctx.clearRect((piecePos%boardSize)*30+18,Math.floor(piecePos/boardSize)*30+18,24,24);
}
function drawPiecePos() {
ctx.fillStyle = "blue";
ctx.fillRect((piecePos%boardSize)*30+20,Math.floor(piecePos/boardSize)*30+20,20,20);
}
function move(dest) {
switch (dest) {
case "left":
if (piecePos%boardSize === 0 || reachable[piecePos][3] === -1) {
break;
}
wipePiecePos();
piecePos--;
drawPiecePos();
break;
case "up":
if (piecePos < boardSize || reachable[piecePos][0] === -1) {
break;
}
wipePiecePos();
piecePos -= boardSize;
drawPiecePos();
break;
case "right":
if (piecePos%boardSize === boardSize-1 || reachable[piecePos][1] === -1) {
break;
}
wipePiecePos();
piecePos++;
drawPiecePos();
break;
case "down":
if (piecePos >= boardSize*(boardSize-1) || reachable[piecePos][2] === -1) {
break;
}
wipePiecePos();
piecePos += boardSize;
drawPiecePos();
}
}
function stopWatch() {
startTime = new Date().getTime();
mission = setInterval(timing, 10);//每10ms更新一次时间
}
function timing() {
let t = new Date().getTime()-startTime; //用现在的时间减去刚开始的时间就是花费的时间
let time = new Date(t);
let minutes = time.getUTCMinutes();
if (minutes < 10) {
minutes = "0"+minutes;
}
let seconds = time.getUTCSeconds();
if (seconds < 10) {
seconds = "0"+seconds;
}
let millSeconds = time.getUTCMilliseconds();
millSeconds = Math.floor(millSeconds/10);
if (millSeconds < 10) {
millSeconds = "0" + millSeconds;
}
costTime = minutes+":"+seconds+":"+millSeconds;
$("#time").text(costTime);
}
//用于初始化游戏以及更新关卡
function init() {
piecePos = 0;
tree = [];
reachable = [];
for (let i = 0; i < boardSize*boardSize; i++) {
tree[i] = i;
}
for (let i = 0; i < boardSize*boardSize; i++) {
reachable[i] = [-1,-1,-1,-1];//0上,1右,2下,3左
}
drawChessBoard();
drawMaze();
drawSquare();
}
//开始游戏,监听键盘操作
function startGame() {
$(document).keydown(function (e) {
if (boardSize === 14 && piecePos === 0 && e.which >= 37 && e.which <= 40) {
stopWatch();
}
if (e.which === 37) {
move("left");
} else if (e.which === 38) {
e.preventDefault();
move("up");
} else if (e.which === 39) {
move("right");
} else if (e.which === 40) {
e.preventDefault();
move("down");
}
if (piecePos === boardSize*boardSize-1) {
if (boardSize === 26) {
clearInterval(mission); //停止计时
alert("恭喜您通关了!!!\n用时"+costTime+"\n点击确定返回首页");
window.location.href = "/index?page=1";
return;
}
boardSize += 6;
ctx.clearRect(0,0,800,800);
init();
}
});
}
init();
startGame();
更多精彩文章请访问我的个人博客(zhuoerhuobi.cn)