迷宫程序 链接,
迷宫生成:
解迷宫:
迷宫生成算法的可视化演示,使用 React 来实现。
迷宫的生成,涉及在图中找出生成树相关算法。
解迷宫,就是在生成的树上,进行深度遍历,寻找链接指定两个点的唯一可达路线。
以下为具体的部分关键实现代码。
import React, { Component } from 'react'; import './maze.css'; import Grid from './Grid'; class AppDemo extends Component { constructor(proprs) { super(proprs); var column = 20;//默认列数 var row = 15;//默认行数 var pixel = 35;//默认单位方格像素数 var staticMatrix = this.initStaticMatrix(column, row);//静态二维数组,第一维度是位置编号,第二维度是当下位置的邻居编号 var dynamicMatrix = this.initDynamicMatrix(column, row);//动态二维数组,第一维度是位置编号,第二维度是当下位置已连接的方向信息 var elements = this.initElements(column, row); var arr = this.initArr(column, row); var start = Math.floor(Math.random() * column * row + 1);//随机初始位置 this.state = { staticMatrix: staticMatrix, dynamicMatrix: dynamicMatrix, now: start,//当下位置* begin: start,//开始位置 end: start,//最后一个位置 column: column,//列数 row: row,//行数 timeID: 0,//定时器ID speed: 100,//速度(毫秒) speedtype: "中",//用于界面速度显示 pixel: pixel,//方格像素数 step: false,//是否参与过单步 time: 0,//用时显示 begintime: 0,//起始时间 stoptime: 0,//完成时间 reverse: [0, 3, 4, 1, 2], //用于快速反向 elements: elements,//边界元素集合 over: column * row, maxwidth: 900,//最大宽度像素数 maxheight: 600,//最大高度像素数 arr: arr,//路径元素编号信息 historyPath: [],//路径栈 findtime: 0,//用时显示 findbegintime: 0,//起始时间 findstoptime: 0,//完成时间 findtimeID: 0,//定时器ID findover: false,//寻路完毕 findStep: false,//是否参与过单步 hide: false //是否隐藏界面中的标记 } } /** * 每一步的处理算法,调用一次,迷宫界面就会向前走一步,方向是随机的 */ handle() { var elements = this.state.elements; var r = this.around(this.state.now, this.state.dynamicMatrix);//返回未涉足邻居编号集合 if (r.length < 1) {//若未涉足邻居个数小于1(即从当下位置出发已经无路可走),则随机切换到边界集合中的其他位置 if (this.state.over <= 1) { this.stop(); //this.info("迷宫已经生成完毕。"); return; } var randomNow = this.randomElement(elements); this.setState({ time: (new Date().getTime() - this.state.begintime) / 1000, now: randomNow }); } else { //方向随机1表示向上,2表示向右,3表示向下,4表示向左 var direction = this.randomDirection(r);//随机取一个方向 var next = this.state.staticMatrix[this.state.now][direction];//下一个位置编号 if (this.around(next, this.state.dynamicMatrix).length >= 2) {//如果下一个位置的未涉足邻居个数大于等于2,则标记为边界。 elements[next] = 1; } var dynamicMatrix = this.state.dynamicMatrix;//动态生成的“树” this.connect(this.state.now, direction, next, dynamicMatrix);//连接now和next this.setState({ time: (new Date().getTime() - this.state.begintime) / 1000, now: next, elements: this.freshElements(elements, next, dynamicMatrix), dynamicMatrix: dynamicMatrix, over: this.state.over - 1 }); } } findPath() { if (this.state.findover === false && this.state.over <= 1) { var historyPath = this.state.historyPath;//历史路径 var dynamicMatrix = this.state.dynamicMatrix; var arr = this.state.arr;//路径编号 var staticMatrix = this.state.staticMatrix; if (historyPath.length === 0) { historyPath.push(dynamicMatrix[1].concat()); //arr[historyPath[historyPath.length - 1][0]][] = 1; } var nowRow = historyPath[historyPath.length - 1][0];//获取当下的位置编号 var b = false; for (var i = 1; i <= 4; i++) { var p = historyPath[historyPath.length - 1][i]; if (p === 1) { var next = staticMatrix[nowRow][i]; historyPath.push(dynamicMatrix[next].concat()); historyPath[historyPath.length - 1][this.state.reverse[i]] = 0; arr[next][this.state.reverse[i]] = 1; arr[nowRow][i] = 1; this.setState({ findtime: (new Date().getTime() - this.state.findbegintime) / 1000, historyPath: historyPath, arr: arr }); if (next === this.state.column * this.state.row) { this.findStop(); this.setState({ findover: true }); //this.info("迷宫路径已找到(暂时只支持从左上到右下)。"); return; } b = true; break; } } if (!b) {//如果无路可走 //判断当下路径(未退步之前)是否包含于历史记录。 var nown = historyPath[historyPath.length - 1][0]; var last = historyPath[historyPath.length - 2][0]; arr[nown] = [nown,0,0,0,0]; historyPath.pop(); for (var j = 1; j <= 4; j++) { var p2 = historyPath[historyPath.length - 1][j]; if (p2 === 1) { arr[last][j] = 0; historyPath[historyPath.length - 1][j] = 0; this.setState({ findtime: (new Date().getTime() - this.state.findbegintime) / 1000, historyPath: historyPath, arr: arr }); break; } } } } else { this.findStop(); } } /** * 启动解迷宫程序 */ findStart() { if (this.state.over <= 1) {//若迷宫已经生成完毕,才可以解迷宫 var findtimeID = this.state.findtimeID; if (findtimeID === 0) {//若 当下定时器不为0,则启动新定时器(防止重复启动多个定时器) findtimeID = setInterval( () => this.findPath(), this.state.speed ); this.setState({ findbegintime: new Date().getTime() - this.state.findtime * 1000, findtimeID: findtimeID, now: 0 }); } }else{ this.info("需要先生成迷宫,才能解迷宫的!笨蛋"); } } findStop() { var findtimeID = this.state.findtimeID; if (findtimeID !== 0) { clearInterval(findtimeID); this.setState({ findtimeID: 0 }); } } findStep() { this.findStop(); this.findPath(); this.setState({ findStep: true }); } hide(){ this.setState({ hide: !this.state.hide }); } render() { return ( <div className="maze"> <div className="aaa"> <Grid pixel={this.state.pixel} width={this.state.column} height={this.state.row} dynamicMatrix={this.state.dynamicMatrix} now={this.state.now} elements={this.state.elements} begin={this.state.begin} end={this.state.end} arr={this.state.arr} hide={this.state.hide} /> </div> <div className="bbb"> <div className="control navbar navbar-default"> <span className="title">生成迷宫:(用时{this.state.step ? "单步参与后不再计时" : (this.state.time + "s")})</span><br /> <span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.start()}>开始</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.stop()}>暂停</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.start()}>继续</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.step()}>单步</button><span > </span> </div> <div className="control navbar navbar-default"> <span className="title">解迷宫:(用时{this.state.findStep ? "单步参与后不再计时" : (this.state.findtime + "s")})<span > </span>{this.state.findover?("找到,路径长度为:"+this.state.historyPath.length)+"":"迷宫入口为左上角,出口为右下角。"}</span><br /> <span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.findStart()}>开始</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.findStop()}>暂停</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.findStart()}>继续</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.findStep()}>单步</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.resetFind()}>清除</button> </div> <div className="control navbar navbar-default"> <span className="title">速度:{this.state.speedtype}</span><br /> <span > </span> <button className="btn btn-primary navbar-btn" type="button" onClick={() => this.speed(1000)}>极慢</button><span > </span> <button className="btn btn-primary navbar-btn" type="button" onClick={() => this.speed(500)}>慢</button><span > </span> <button className="btn btn-primary navbar-btn" type="button" onClick={() => this.speed(100)}>中</button><span > </span> <button className="btn btn-primary navbar-btn" type="button" onClick={() => this.speed(50)}>快</button><span > </span> <button className="btn btn-primary navbar-btn" type="button" onClick={() => this.speed(10)}>极���</button> </div> <div className="control navbar navbar-default"> <span className="title">列数:{this.state.column}</span><br /> <span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addwidth(1)}>列+1</button><span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addwidth(-1)}>列-1</button> <span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addwidth(5)}>列+5</button><span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addwidth(-5)}>列-5</button><br /> </div> <div className="control navbar navbar-default"> <span className="title">行数:{this.state.row}</span><br /> <span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addheight(1)}>行+1</button><span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addheight(-1)}>行-1</button> <span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addheight(5)}>行+5</button><span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.addheight(-5)}>行-5</button><br /> </div> <div className="control navbar navbar-default"> <span className="title">大小:{this.state.column * this.state.pixel}px : {this.state.row * this.state.pixel}px</span><br /> <span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.big(1)}>放大+1</button><span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.big(-1)}>缩小-1</button> <span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.big(5)}>放大+5</button><span > </span> <button className="btn btn-primary navbar-btn" style={{ width: "80px" }} type="button" onClick={() => this.big(-5)}>缩小-5</button><br /> </div> <div className="control navbar navbar-default"> <span className="title">其他:</span><br /> <span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.hide()}>{this.state.hide?"显示标记":"隐藏标记"}</button><span > </span> <button type="button" className="btn btn-primary navbar-btn" onClick={() => this.resetAll()}>全部重置</button> </div> </div> </div > ) } /** * 连接now和next两个位置 * @param {*} now * @param {*} direction * @param {*} next * @param {*} dynamicMatrix */ connect(now, direction, next, dynamicMatrix) { dynamicMatrix[now][direction] = 1;//连接下一个位置 dynamicMatrix[next][this.state.reverse[direction]] = 1;//连接下一个位置 } /** * 从边界集合中随机选择一个返回 * @param {} elements */ randomElement(elements) { var u = this.state.column * this.state.row var n = Math.floor(Math.random() * u); var i = 0; while (elements[n] !== 1) { i++; n++; if (n > u) { n = 1; } if (i > u) { n = 0; break; } } return n; } /** * 初始化StaticMatrix * @param {*} width * @param {*} height */ initStaticMatrix(width, height) { var matrix = [[0, 1, 2, 3, 4]]; for (var numb = 1; numb <= width * height; numb++) { var up = numb > width ? numb - width : 0; var right = (numb % width) !== 0 ? numb + 1 : 0; var down = numb <= width * (height - 1) ? numb + width : 0; var left = ((numb - 1) % width) !== 0 ? numb - 1 : 0; var arr = [numb]; arr.push(up); arr.push(right); arr.push(down); arr.push(left); matrix.push(arr); } return matrix; } /** * 初始化DynamicMatrix * @param {*} width * @param {*} height */ initDynamicMatrix(width, height) { var dynamicMatrix = [[0, 1, 2, 3, 4]]; for (var numb = 1; numb <= width * height; numb++) { var up = 0; var right = 0; var down = 0; var left = 0; var arr = [numb]; arr.push(up); arr.push(right); arr.push(down); arr.push(left); dynamicMatrix.push(arr); } return dynamicMatrix; } initArr(width, height) { var dynamicMatrix = [[0, 1, 2, 3, 4]]; for (var numb = 1; numb <= width * height; numb++) { var up = 0; var right = 0; var down = 0; var left = 0; var arr = [numb]; arr.push(up); arr.push(right); arr.push(down); arr.push(left); dynamicMatrix.push(arr); } return dynamicMatrix; } /** * 初始化Elements边界集合 * @param {*} column * @param {*} row */ initElements(column, row) { var arr = new Array(column * row + 1); for (var i = 0; i < arr.length; i++) { arr[i] = 0; } return arr; } /** * 返回未涉足邻居编号集合 * 参数new,是当下位置的编号 */ around(now, dynamicMatrix) { var r = []; for (var j = 1; j <= 4; j++) {//每个位置周边共四个“邻居” var runod = this.state.staticMatrix[now][j];//其中一个邻居 //判断“邻居”是否已经被涉足,若没有,则将此邻居方向编号加入将要返回的集合中 if (runod !== 0 && (dynamicMatrix[runod][1] === 0 && dynamicMatrix[runod][2] === 0 && dynamicMatrix[runod][3] === 0 && dynamicMatrix[runod][4] === 0)) { r.push(j); } } return r;//返回未涉足邻居方向编号集合 } /** * 从邻居中随机选出一个作为下一个位置 * @param {*} r */ randomDirection(r) { if (r.length === 1) { return r[r.length - 1]; } return r[Math.floor(Math.random() * r.length)]; } /** * 刷新边界集合,(每前进一步,整个盘面唯有一个位置有所改变,所以只扫面更新此位置四周即可) * @param {*} elements * @param {*} next * @param {*} dynamicMatrix */ freshElements(elements, next, dynamicMatrix) { for (var n = 1; n <= 4; n++) { var runod = this.state.staticMatrix[next][n];//其中一个邻居 //判断“邻居”是否已经被涉足,若没有,则将此邻居编号加入将要返回的集合中 if (elements[runod] !== 0 && this.around(runod, dynamicMatrix).length === 0) {//如果elements[n]周围没有未涉足区域,则从边缘集合中去掉 elements[runod] = 0; } } return elements; } /** * 添加列数,n为添加的列数 * @param {*} n */ addwidth(n) { this.stop(); var column = this.state.column; column = column + n; if (column > 0 && column * this.state.pixel <= this.state.maxwidth) { var staticMatrix = this.initStaticMatrix(column, this.state.row); var dynamicMatrix = this.initDynamicMatrix(column, this.state.row); var elements = this.initElements(column, this.state.row); var arr = this.initArr(column, this.state.row); var start = Math.floor(Math.random() * column * this.state.row + 1); this.setState({ staticMatrix: staticMatrix, dynamicMatrix: dynamicMatrix, begin: start, now: start, column: column, time: 0, elements: elements, over: column * this.state.row, arr: arr, historyPath: [], findtime: 0, findover: false }); } else { var maxColumn = Math.floor(this.state.maxwidth / this.state.pixel); var staticMatrix1 = this.initStaticMatrix(maxColumn, this.state.row); var dynamicMatrix1 = this.initDynamicMatrix(maxColumn, this.state.row); var elements1 = this.initElements(maxColumn, this.state.row); var arr1 = this.initArr(maxColumn, this.state.row); var newstart = Math.floor(Math.random() * maxColumn * this.state.row + 1); this.setState({ staticMatrix: staticMatrix1, dynamicMatrix: dynamicMatrix1, begin: newstart, now: newstart, column: maxColumn, time: 0, elements: elements1, over: maxColumn * this.state.row, arr: arr1, historyPath: [], findtime: 0, findover: false }); } } /** * 添加行数,n为添加的行数 * @param {*} n */ addheight(n) { this.stop(); var row = this.state.row; row = row + n; if (row > 0 && row * this.state.pixel <= this.state.maxheight) { var staticMatrix = this.initStaticMatrix(this.state.column, row); var dynamicMatrix = this.initDynamicMatrix(this.state.column, row); var elements = this.initElements(this.state.column, row); var arr = this.initArr(this.state.column, row); var start = Math.floor(Math.random() * this.state.column * row + 1); this.setState({ staticMatrix: staticMatrix, dynamicMatrix: dynamicMatrix, begin: start, now: start, row: row, time: 0, elements: elements, over: this.state.column * row, arr: arr, historyPath: [], findtime: 0, findover: false }); } else { var maxRow = Math.floor(this.state.maxheight / this.state.pixel); var staticMatrix1 = this.initStaticMatrix(this.state.column, maxRow); var dynamicMatrix1 = this.initDynamicMatrix(this.state.column, maxRow); var elements1 = this.initElements(this.state.column, maxRow); var arr1 = this.initArr(this.state.column, maxRow); var newstart = Math.floor(Math.random() * this.state.column * maxRow + 1); this.setState({ staticMatrix: staticMatrix1, dynamicMatrix: dynamicMatrix1, begin: newstart, now: newstart, time: 0, row: maxRow, elements: elements1, over: this.state.column * maxRow, arr: arr1, historyPath: [], findtime: 0, findover: false }); } } /** * 缩放界面大小,n为每个单位方格改变的像素数 * @param {*} n */ big(n) { var pixel = this.state.pixel; pixel = pixel + n; if (pixel >= 5 && ((pixel * this.state.column <= this.state.maxwidth) && (pixel * this.state.row <= this.state.maxheight))) { this.setState({ pixel: pixel }); return; } if(pixel < 5 ){ this.info("已经最小,再小你就看不见了,你以为你是显微镜呀!"); this.setState({ pixel: 5 }); }else { this.info("不能再大了,笨蛋"); this.setState({ pixel: Math.floor((pixel * this.state.column / this.state.maxwidth > pixel * this.state.row / this.state.maxheight) ? (this.state.maxwidth / this.state.column) : (this.state.maxheight / this.state.row)) }); } } /** * 重置应用,使其除行列和大小外,其他因素设置成初始状态 */ resetAll() { this.stop(); var staticMatrix = this.initStaticMatrix(this.state.column, this.state.row); var dynamicMatrix = this.initDynamicMatrix(this.state.column, this.state.row); var elements = this.initElements(this.state.column, this.state.row); var arr = this.initArr(this.state.column, this.state.row); var start = Math.floor(Math.random() * this.state.column * this.state.row + 1); this.setState({ staticMatrix: staticMatrix, dynamicMatrix: dynamicMatrix, begin: start, now: start, time: 0, elements: elements, over: this.state.column * this.state.row, arr: arr,//路径元素编号信息 historyPath: [],//路径栈 findtime: 0,//用时显示 findbegintime: 0,//起始时间 findstoptime: 0,//完成时间 findtimeID: 0,//定时器ID findover: false,//寻路完毕 findStep: false,//是否参与过单步 hide: false //是否隐藏界面中的标记 }); } /** * 重置应用,使其除行列和大小外,其他因素设置成初始状态 */ resetFind() { this.findStop(); var arr = this.initArr(this.state.column, this.state.row); this.setState({ arr: arr,//路径元素编号信息 historyPath: [],//路径栈 findtime: 0,//用时显示 findbegintime: 0,//起始时间 findstoptime: 0,//完成时间 findtimeID: 0,//定时器ID findover: false,//寻路完毕 findStep: false,//是否参与过单步 hide: false //是否隐藏界面中的标记 }); } /** * 单步 */ step() { this.stop(); this.handle(); this.setState({ step: true }); } /** * 开始 */ start() { var timeID = this.state.timeID; if (timeID === 0) { timeID = setInterval( () => this.handle(), this.state.speed ); this.setState({ begintime: new Date().getTime() - this.state.time * 1000, timeID: timeID }); } } /** * 暂停 */ stop() { var timeID = this.state.timeID; if (timeID !== 0) { clearInterval(timeID); this.setState({ timeID: 0 }); } } /** * 改变速度,n为新的时间间隔,单位ms,每隔n ms时间,定时器调用一次相关方法 * @param {*} n */ speed(n) { var timeID = this.state.timeID; if (timeID !== 0) { clearInterval(timeID); timeID = 0; timeID = setInterval( () => this.handle(), n ); } var findtimeID = this.state.findtimeID; if (findtimeID !== 0) { clearInterval(findtimeID); findtimeID = 0; findtimeID = setInterval( () => this.findPath(), n ); } var speedtype = ""; if (n === 10) { speedtype = "极快"; } if (n === 50) { speedtype = "快"; } if (n === 100) { speedtype = "中"; } if (n === 500) { speedtype = "慢"; } if (n === 1000) { speedtype = "极慢"; } this.setState({ timeID: timeID, findtimeID: findtimeID, speed: n, speedtype: speedtype }); } info(info){ alert(info); } } export default AppDemo;
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于