马踏棋盘算法介绍和游戏演示
1.马踏棋盘算法也被称为骑士周游问题
2.将马随机放在国际象棋的8×8棋盘Board[0~7][0~7]的某个方格中,马按走棋规则(马走日字)进行移动。要求每个方格只进入一次,走遍棋盘上全部64个方格
3.游戏演示: 马踏棋盘_马踏棋盘html5游戏在线玩_4399h5游戏-4399在线玩
马踏棋盘游戏代码实现
1.马踏棋盘问题(骑士周游问题)实际上是图的深度优先搜索(DFS)的应用。
2.如果使用回溯(就是深度优先搜索)来解决,假如马儿踏了53个点,如图:走到了第53个,坐标(1,0),发现已经走到尽头,没办法,那就只能回退了,查看其他的路径,就在棋盘上不停的回溯…… ,思路分析+代码实现
3.分析第一种方式的问题,并使用贪心算法(greedyalgorithm)进行优化。解决马踏棋盘问题.
4.使用前面的游戏来验证算法是否正确。
/**
*
*
骑士周游问题
1.创建周游问题的解决步骤和思路
2.将当前位置设置为已经访问,然后根据当前位置,计算马儿还能走哪些位置,
并放入到一个集合中,最后有8个位置,每走一步,就使用step+1
3.遍历list中存放的所有位置,看看哪个位置可以走通,如果走通,就回溯
4.判断马儿是否完成了任务,使用step和应该走的步数比较,如果没有达到数量,
则表示没有完成任务,将整个棋盘置0
注意:马儿不同的走法(策略),会得到不同的结果,效率也有影响(优化),
注意:如果不用贪心算法进行优化,计算步骤会十分庞大导致js线程卡死,
马踏棋盘问题和迷宫问题不一样,马踏棋盘算法中一个点可能会走很多次,而迷宫问题中一个点只会走一次
所以马踏棋盘算法的计算量要大的多
使用贪心算法进行优化
1.我们获取当前位置,可以走的下一个位置的集合ps
2.我们需要对ps中所有的point下一步的所有集合的数目,进行非递减排序,其实就是递增
优化的原因,我们优先选择所有的下一个位置的选择的位置最小的那个,
可以减少回溯
假设我们每一个马儿位置,有n个位置可以继续走,我们此时不知道这n个位置能否成功走完棋盘,那么
设每个位置的完成概率是相等的p(n*p=1),但是我们可以确定每个位置的完成时间
(完成时间=成功时间*成功概率+失败时间*失败概率,成功时间,成功概率和失败概率是相等的,但是失败时间不等)
大小,n个位置的完成时间,依次为t1,t2,...tn,.....,
此时我们可以看到走n1和n2位置成功与走n3和n4的总成功率是相等的,但是总完成时间是不等,
我们为了效率,在获取相同成功率的情况下,总完成时间越短则效率越高
*
*
*/
class HorseChessboard {
x;//棋盘的列数
y;//棋盘的行数
visited = [];//创建一个数组,标记棋盘的各个位置是否被访问过
//使用一个属性,标记是否棋盘的所有位置都被访问过
finished;
constructor() {
}
//我们需要对ps中所有的point下一步的所有集合的数目,进行非递减排序,其实就是递增
sort = (o1, o2) => {
//获取到o1的下一步的所有位置个数
let count1 = this.next(o1).length;
//获取到o2的下一步的所有位置个数
let count2 = this.next(o2).length;
if (count1 < count2) {
return -1;
} else if (count1 == count2) {
return 0;
} else {
return 1;
}
}
/**
* 完成骑士周游问题的算法
* @param {棋盘} chessboard
* @param {马儿当前位置的行,初始为0} row
* @param {马儿当前位置的列,初始为0} col
* @param {第第几步,初始位置为1} step
*/
traversalChessboard(chessboard, row, col, step) {
chessboard[row][col] = step;
this.visited[row * this.x + col] = true;//标记为已访问
//获取当前位置可以走的下一个位置的集合
let ps = this.next(new Point(col, row));
//贪心算法优化,对ps中所有的point下一步的所有集合的数目,进行非递减排序,其实就是递增
ps.sort(this.sort);
//遍历ps
while (ps.length > 0) {
//取出下一个可以走的位置
let p = ps.shift();
//判断该点是否已经访问过
if (!this.visited[p.y * this.x + p.x]) {
//还没访问过
this.traversalChessboard(chessboard, p.y, p.x, step + 1);
}
}
//判断马儿是否完成了任务
//如果没有达到数量,则表示没有完成任务,将整个棋盘置0
//说明:step < this.x * this.y成立的情况有两种
//1.棋盘到目前位置,棋盘没有走完
//2。棋盘处于一个回溯过程
if (step < this.x * this.y && !this.finished) {
chessboard[row][col] = 0;
this.visited[row * this.x + col] = false;
} else {
this.finished = true;
}
}
/**
* 根据当前位置,计算马儿还能走哪些位置,最多有8个位置
* @param {*} curPoint
*/
next(curPoint) {
let list = [];
let p1 = new Point();
//1.表示马儿可以走左二上一这个位置
if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
list.push(new Point(p1.x, p1.y));
}
//2.判断马儿可以走左一上二
if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
list.push(new Point(p1.x, p1.y));
}
//3.判断马儿可以走右一上二
if ((p1.x = curPoint.x + 1) < this.x && (p1.y = curPoint.y - 2) >= 0) {
list.push(new Point(p1.x, p1.y));
}
//4.表示马儿可以走右二上一
if ((p1.x = curPoint.x + 2) < this.x && (p1.y = curPoint.y - 1) >= 0) {
list.push(new Point(p1.x, p1.y));
}
//5.表示马儿可以走右二下一
if ((p1.x = curPoint.x + 2) < this.x && (p1.y = curPoint.y + 1) < this.y) {
list.push(new Point(p1.x, p1.y));
}
//6.表示马儿可以走右一下二
if ((p1.x = curPoint.x + 1) < this.x && (p1.y = curPoint.y + 2) < this.y) {
list.push(new Point(p1.x, p1.y));
}
//7.表示马儿可以走左一下二
if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < this.y) {
list.push(new Point(p1.x, p1.y));
}
//8.表示马儿可以走右二下一
if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < this.y) {
list.push(new Point(p1.x, p1.y));
}
return list;
}
}
//点坐标
class Point {
x;
y;
constructor(x, y) {
this.x = x;
this.y = y;
}
}
//测试骑士周游算法
let x = 8, y = 8;
let test = new HorseChessboard();
test.x = x;
test.y = y;
let row = 1;//马儿初始位置的行,1
let column = 1;//马儿初始位置的列,1
//创建棋盘
let chessboard = new Array(y);
for (let i = 0; i < y; i++) {
chessboard[i] = new Array(x).fill(0);
}
test.visited = new Array(x * y).fill(false);
test.traversalChessboard(chessboard, row - 1, column - 1, 1);
//输出棋盘
console.log('棋盘走的步骤:', chessboard);
console.log(test);
测试: