小时候一直和爷爷下象棋,可是除了耍赖,就从来没有赢过(lll¬ω¬)
有一次做梦…我写出了一个能赢过爷爷的象棋AI,但却没有机会和他下了
就觉得有必要在还有机会的时候自己写一个智能体出来,也算是经我之手正式地赢了爷爷(●ˇ∀ˇ●)
欲泪成雪
的原创分享,加上我适当的修改DOCTYPE html>
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>中国象棋title>
<style>
style>
head>
<body>
<div class="layout">
<canvas id="canvas1" style="background-color:burlywood" width="1005" height="1105">不支持Canvascanvas>
div>
<script type="text/javascript">
var object = {
//初始化
init: function () {
//棋盘外框
var canvas1 = document.getElementById("canvas1");
this.ctx = canvas1.getContext("2d");
this.ctx.lineWidth = 5;
this.ctx.clearRect(0, 0, 1005, 1105);
this.ctx.strokeStyle = "brown";
this.ctx.strokeRect(100, 100, 800, 900);
this.row();
this.cols();
this.drawFont();
//注意:现在的原点中心为(100,100),所以下面的所有坐标在原来的基础上加(x+100,y+100);
//中心点一(1000,200)
this.center(200, 300);
//中心点二(700,200)
this.center(800, 300);
//中心点三(5,300)
this.center(100, 400);
//中心点四(200,300)
this.center(300, 400);
//中心点五(400,300)
this.center(500, 400);
//中心点六(600,300)
this.center(700, 400);
//中心点七(800,300)
this.center(900, 400);
//中心点八(800,600)
this.center(900, 700);
//中心点九(600,600)
this.center(700, 700);
//中心点十(400,600)
this.center(500, 700);
//中心点十一(200,600)
this.center(300, 700);
//中心点十二(5,600)
this.center(100, 700);
//中心点十三(700,700)
this.center(800, 800);
//中心点十四(100,700)
this.center(200, 800);
},
//此方法用来画棋盘线
LineDrawing: function (mx, my, lx, ly) {
this.ctx.beginPath();
this.ctx.moveTo(mx, my);
this.ctx.lineTo(lx, ly);
this.ctx.stroke();
},
//棋盘行
row: function () {
for (var i = 200; i <= 900; i += 100) {
this.ctx.beginPath();
this.ctx.moveTo(100, i);
this.ctx.lineTo(900, i);
this.ctx.stroke();
}
},
//棋盘列
cols: function () {
for (var i = 200; i <= 800; i += 100) {
this.ctx.beginPath();
this.ctx.moveTo(i, 100);
this.ctx.lineTo(i, 1000);
this.ctx.stroke();
}
//清除指定的矩形区域
//this.ctx.clearRect(5, 402,795, 95);
this.ctx.clearRect(103, 502, 795, 95);
//斜线
this.LineDrawing(400, 105, 600, 300);
this.LineDrawing(400, 805, 600, 1000);
//反斜线
this.LineDrawing(600, 105, 400, 300);
this.LineDrawing(600, 805, 400, 1000);
},
//坐标的中心点
center: function (x, y) {
this.ctx.lineWidth = 5;
//中心点一(100,200)
//左上
this.LineDrawing(x - 10, y - 30, x - 10, y - 10);
this.LineDrawing(x - 10, y - 10, x - 30, y - 10);
//右上
this.LineDrawing(x + 10, y - 30, x + 10, y - 10);
this.LineDrawing(x + 10, y - 10, x + 30, y - 10);
//左下
this.LineDrawing(x - 10, y + 30, x - 10, y + 10);
this.LineDrawing(x - 10, y + 10, x - 30, y + 10);
//右下
this.LineDrawing(x + 10, y + 30, x + 10, y + 10);
this.LineDrawing(x + 10, y + 10, x + 30, y + 10);
this.ctx.stroke();
},
drawFont: function () {
this.ctx.lineWidth = 1;
//绘制文字
this.ctx.font = "60px microsoft yahei";
// this.ctx.fillStyle = "black";
this.ctx.save(); //保存点
//将坐标中心作为起启点
this.ctx.translate(canvas1.width / 2, canvas1.height / 2);
var radian = Math.PI / 2; // 弧度制 Math.PI=π
this.ctx.rotate(radian); // 旋转画布绘制刻度
//填充
this.ctx.fillText("汉", -30, -270);
this.ctx.fillText("界", -30, -150);
this.ctx.restore(); //恢复到保存点
this.ctx.save();
//将坐标中心作为起点
this.ctx.translate(canvas1.width / 2, canvas1.height / 2);
var radian = Math.PI / -2;
this.ctx.rotate(radian);
this.ctx.fillText("楚", -30, -270);
this.ctx.fillText("河", -30, -150);
this.ctx.restore();
},
drawRed(chessPieceName, _x, _y) {
this.drawChessPieces(chessPieceName, _x, _y, {
color: 'red',
fillColor: 'black'
})
},
drawBlack(chessPieceName, _x, _y) {
this.drawChessPieces(chessPieceName, _x, _y, {
color: 'white',
fillColor: 'black'
})
},
showRoad(chessPieceName, _x, _y, x, y) {
this.drawChessPieces(chessPieceName, _x, _y, {
color: 'blue',
fillColor: '',
r: 50
})
},
drawChoose(chessMap, roads, chessPieceName, _x, _y, x, y) {
// this.drawChessPieces(chessPieceName, _x, _y, {
// color: 'blue',
// fillColor: '',
// r: 50
// })
roads.push({
_x, _y, x, y
})
},
drawChessPieces: function (chessPieceName, _x, _y, config) {
let x = (_x) * 100 + 100;
let y = (_y) * 100 + 100;
this.ctx.strokeStyle = config.color;
this.ctx.lineWidth = 5;
this.ctx.beginPath();
this.ctx.arc(x, y, config.r || 40, 0, 2 * Math.PI, true);
if (config.fillColor) {
this.ctx.fillStyle = config.fillColor;
this.ctx.fill();
}
this.ctx.stroke();
this.ctx.font = "60px microsoft yahei";
this.ctx.fillStyle = config.color;
this.ctx.fillText(chessPieceName, x - 30, y + 20);
}
};
object.init();
script>
body>
html>
出来的效果
// 重置棋盘
function resetMap() {
chessMap = [
['车红一', '马红一', '相红一', '仕红一', '帅红一', '仕红二', '相红二', '马红二', '车红二'],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', '砲红一', ' ', ' ', ' ', ' ', ' ', '砲红二', ' '],
['兵红一', ' ', '兵红二', ' ', '兵红三', ' ', '兵红四', ' ', '兵红五'],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['卒黑一', ' ', '卒黑二', ' ', '卒黑三', ' ', '卒黑四', ' ', '卒黑五'],
[' ', '炮黑一', ' ', ' ', ' ', ' ', ' ', '炮黑二', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['车黑一', '马黑一', '象黑一', '士黑一', '将黑一', '士黑二', '象黑二', '马黑二', '车黑二']
]
side = '红';
flushChessCanvas();
}
resetMap();
// 画出棋子
function flushChessCanvas() {
object.init();
for (let y = 0; y < chessMap.length; y++) {
for (let x = 0; x < chessMap[y].length; x++) {
let chess = chessMap[y][x];
if (chess[1] == '红') {
object.drawRed(chess[0], x, y)
}
if (chess[1] == '黑') {
object.drawBlack(chess[0], x, y)
}
}
}
}
目前就大致实现了文章开头的棋盘的效果,至此,我们可以看到一副配备完整的
象棋+棋盘
了(可以拿来卖了( ̄▽ ̄)")
超逸_流
的方法,自己实现了一边棋盘的主逻辑,主要来判断每一颗棋子可以行走的路径// 车逻辑
function getJuRoad(map, roads, x, y) {
let chessMap = map;
let chess = chessMap[y][x];
// 上
for (let _y = y - 1, _x = x; _y > -1; _y--) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
// 右
for (let _y = y, _x = x + 1; _x < 9; _x++) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
// 下
for (let _y = y + 1, _x = x; _y < 10; _y++) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
// 左
for (let _y = y, _x = x - 1; _x > -1; _x--) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
}
// getJuRoad(0,0)
// 马逻辑
function getMaRoad(map, roads, x, y) {
let chessMap = map;
let chess = chessMap[y][x];
// 1点钟方向
if (x + 1 < 9 && y - 2 > -1 && chessMap[y - 1][x] === ' ' && chessMap[y - 2][x + 1][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x + 1, y - 2, x, y)
}
// 2点钟方向
if (x + 2 < 9 && y - 1 > -1 && chessMap[y][x + 1] === ' ' && chessMap[y - 1][x + 2][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x + 2, y - 1, x, y)
}
// 4点钟方向
if (x + 2 < 9 && y + 1 < 10 && chessMap[y][x + 1] === ' ' && chessMap[y + 1][x + 2][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x + 2, y + 1, x, y)
}
// 5点钟方向
if (x + 1 < 9 && y + 2 < 10 && chessMap[y + 1][x] === ' ' && chessMap[y + 2][x + 1][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x + 1, y + 2, x, y)
}
// 7点钟方向
if (x - 1 > -1 && y + 2 < 10 && chessMap[y + 1][x] === ' ' && chessMap[y + 2][x - 1][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x - 1, y + 2, x, y)
}
// 8点钟方向
if (x - 2 > -1 && y + 1 < 10 && chessMap[y][x - 1] === ' ' && chessMap[y + 1][x - 2][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x - 2, y + 1, x, y)
}
// 10点钟方向
if (x - 2 > -1 && y - 1 > -1 && chessMap[y][x - 1] === ' ' && chessMap[y - 1][x - 2][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x - 2, y - 1, x, y)
}
// 11点钟方向
if (x - 1 > -1 && y - 2 > -1 && chessMap[y - 1][x] === ' ' && chessMap[y - 2][x - 1][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x - 1, y - 2, x, y)
}
}
// getMaRoad(1,0)
// 象逻辑
function getXiangRoad(map, roads, x, y, side) {
let chessMap = map;
let chess = chessMap[y][x];
// 1点半方向
if (y - 2 > -1 && x + 2 < 9 && chessMap[y - 1][x + 1] === ' ' && chessMap[y - 2][x + 2][1] != chess[1]) {
if (side == '黑' && (y - 2) > 4) {
object.drawChoose(chessMap, roads, chess[0], x + 2, y - 2, x, y)
}
if (side == '红' && (y - 2) < 5) {
object.drawChoose(chessMap, roads, chess[0], x + 2, y - 2, x, y)
}
}
// 4点半方向
if (y + 2 < 10 && x + 2 < 9 && chessMap[y + 1][x + 1] === ' ' && chessMap[y + 2][x + 2][1] != chess[1]) {
if (side == '黑' && (y + 2) > 4) {
object.drawChoose(chessMap, roads, chess[0], x + 2, y + 2, x, y)
}
if (side == '红' && (y + 2) < 5) {
object.drawChoose(chessMap, roads, chess[0], x + 2, y + 2, x, y)
}
}
// 7点半方向
if (y + 2 < 10 && x - 2 > -1 && chessMap[y + 1][x - 1] === ' ' && chessMap[y + 2][x - 2][1] != chess[1]) {
if (side == '黑' && (y + 2) > 4) {
object.drawChoose(chessMap, roads, chess[0], x - 2, y + 2, x, y)
}
if (side == '红' && (y + 2) < 5) {
object.drawChoose(chessMap, roads, chess[0], x - 2, y + 2, x, y)
}
}
// 10点半方向
if (y - 2 > -1 && x - 2 > -1 && chessMap[y - 1][x - 1] === ' ' && chessMap[y - 2][x - 2][1] != chess[1]) {
if (side == '黑' && (y - 2) > 4) {
object.drawChoose(chessMap, roads, chess[0], x - 2, y - 2, x, y)
}
if (side == '红' && (y - 2) < 5) {
object.drawChoose(chessMap, roads, chess[0], x - 2, y - 2, x, y)
}
}
}
// getXiangRoad(2,0,'红')
// 士逻辑
function getShiRoad(map, roads, x, y, side) {
let chessMap = map;
let chess = chessMap[y][x];
// 1点半方向
if (y - 1 > -1 && x + 1 < 9 && chessMap[y - 1][x + 1][1] != chess[1]) {
if (side == '黑' && (y - 1) > 6 && (x + 1) > 2 && (x + 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x + 1, y - 1, x, y)
}
if (side == '红' && (y - 1) < 3 && (x + 1) > 2 && (x + 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x + 1, y - 1, x, y)
}
}
// 4点半方向
if (y + 1 < 10 && x + 1 < 9 && chessMap[y + 1][x + 1][1] != chess[1]) {
if (side == '黑' && (y + 1) > 6 && (x + 1) > 2 && (x + 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x + 1, y + 1, x, y)
}
if (side == '红' && (y + 1) < 3 && (x + 1) > 2 && (x + 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x + 1, y + 1, x, y)
}
}
// 7点半方向
if (y + 1 < 10 && x - 1 > -1 && chessMap[y + 1][x - 1][1] != chess[1]) {
if (side == '黑' && (y + 1) > 6 && (x - 1) > 2 && (x - 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x - 1, y + 1, x, y)
}
if (side == '红' && (y + 1) < 3 && (x - 1) > 2 && (x - 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x - 1, y + 1, x, y)
}
}
// 10点半方向
if (y - 1 > -1 && x - 1 > -1 && chessMap[y - 1][x - 1][1] != chess[1]) {
if (side == '黑' && (y - 1) > 6 && (x - 1) > 2 && (x - 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x - 1, y - 1, x, y)
}
if (side == '红' && (y - 1) < 3 && (x - 1) > 2 && (x - 1) < 6) {
object.drawChoose(chessMap, roads, chess[0], x - 1, y - 1, x, y)
}
}
}
// getShiRoad(3,0,'红')
// 将逻辑
function getJiangRoad(map, roads, x, y, side) {
let chessMap = map;
let chess = chessMap[y][x];
// 对阵
if (side == '黑') {
for (let _y = y-1;_y > -1; _y--) {
if (chessMap[_y][x][0] === '帅') {
object.drawChoose(chessMap, roads, chess[0], x, _y, x, y)
}
if (chessMap[_y][x] !== ' ') {
break;
}
}
}
if (side == '红') {
for (let _y = y+1;_y < 10; _y++) {
if (chessMap[_y][x][0] === '将') {
object.drawChoose(chessMap, roads, chess[0], x, _y, x, y)
}
if (chessMap[_y][x] !== ' ') {
break;
}
}
}
// 上
if (y - 1 > -1 && chessMap[y - 1][x][1] != chess[1]) {
if (side == '黑' && (y - 1) > 6) {
object.drawChoose(chessMap, roads, chess[0], x, y - 1, x, y)
}
if (side == '红') {
object.drawChoose(chessMap, roads, chess[0], x, y - 1, x, y)
}
}
// 右
if (x + 1 < 6 && chessMap[y][x + 1][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x + 1, y, x, y)
}
// 下
if (y + 1 < 10 && chessMap[y + 1][x][1] != chess[1]) {
if (side == '黑') {
object.drawChoose(chessMap, roads, chess[0], x, y + 1, x, y)
}
if (side == '红' && (y + 1) < 3) {
object.drawChoose(chessMap, roads, chess[0], x, y + 1, x, y)
}
}
// 左
if (x - 1 > 2 && chessMap[y][x - 1][1] != chess[1]) {
object.drawChoose(chessMap, roads, chess[0], x - 1, y, x, y)
}
}
// getJiangRoad(4,0,'红')
// 炮逻辑
function getPaoRoad(map, roads, x, y) {
let chessMap = map;
let chess = chessMap[y][x];
let count = 0;
// 上
for (let _y = y - 1, _x = x; _y > -1; _y--) {
if (count == 0) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
} else {
count++;
}
} else {
// 空白
if (chessMap[_y][_x] === ' ') {
continue;
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
}
count = 0;
// 右
for (let _y = y, _x = x + 1; _x < 9; _x++) {
if (count == 0) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
} else {
count++;
}
} else {
// 空白
if (chessMap[_y][_x] === ' ') {
continue;
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
}
count = 0;
// 下
for (let _y = y + 1, _x = x; _y < 10; _y++) {
if (count == 0) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
} else {
count++;
}
} else {
// 空白
if (chessMap[_y][_x] === ' ') {
continue;
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
}
count = 0;
// 左
for (let _y = y, _x = x - 1; _x > -1; _x--) {
if (count == 0) {
if (chessMap[_y][_x] === ' ') {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
} else {
count++;
}
} else {
// 空白
if (chessMap[_y][_x] === ' ') {
continue;
}
// 同色子
else if (chessMap[_y][_x][1] === chess[1]) {
break;
}
// 异色子
else if (chessMap[_y][_x][1] !== chess[1]) {
object.drawChoose(chessMap, roads, chess[0], _x, _y, x, y)
break;
}
}
}
}
// getPaoRoad(1, 2)
// 卒逻辑
function getZuRoad(map, roads, x, y, side) {
let chessMap = map;
let chess = chessMap[y][x];
// 上
if (side == '黑') {
if (y-1>-1 && chessMap[y-1][x][1] != '黑') {
object.drawChoose(chessMap, roads, chess[0], x, y-1, x, y);
}
}
// 右
if (side == '黑' && y < 5) {
if (x+1<9 && chessMap[y][x+1][1] != '黑') {
object.drawChoose(chessMap, roads, chess[0], x+1, y, x, y);
}
}
if (side == '红' && y > 4) {
if (x+1<9 && chessMap[y][x+1][1] != '红') {
object.drawChoose(chessMap, roads, chess[0], x+1, y, x, y);
}
}
// 下
if (side == '红') {
if (y+1<10 && chessMap[y+1][x][1] != '红') {
object.drawChoose(chessMap, roads, chess[0], x, y+1, x, y);
}
}
// 左
if (side == '黑' && y < 5) {
if (x-1>-1 && chessMap[y][x-1][1] != '黑') {
object.drawChoose(chessMap, roads, chess[0], x-1, y, x, y);
}
}
if (side == '红' && y > 4) {
if (x-1>-1 && chessMap[y][x-1][1] != '红') {
object.drawChoose(chessMap, roads, chess[0], x-1, y, x, y);
}
}
}
// getZuRoad(0,3,'红')
所以,到了这儿,我们手中的一颗颗棋子?,就知道他们各自能走到哪里了~
超逸_流
的象棋AI算法
算法说实话,我是没怎么看懂,什么最小-最大搜索啦,什么负值最大函数,什么Alpha-Beta搜索通通是浮云哈哈哈o( ̄▽ ̄)o,我按我自己的理解先写再说~
function doScore(chess, nowScore) {
let _side = chess[1];
let _chessName = chess[0];
if (_side == '黑') {
nowScore[_side] = nowScore[_side] ? nowScore[_side] + scoreDic[_chessName] : scoreDic[_chessName];
}
if (_side == '红') {
nowScore[_side] = nowScore[_side] ? nowScore[_side] + scoreDic[_chessName] : scoreDic[_chessName];
}
}
function getNowScore(map) {
let nowScore = {
黑: 0,
红: 0
}
for (let y = 0; y < chessMap.length; y++) {
for (let x = 0; x < chessMap[y].length; x++) {
let chess = chessMap[y][x];
doScore(chess, nowScore);
}
}
return nowScore;
}
// 会卡死,最后才出效果,目前没有效果
function addLoadingLayer() {
document.getElementsByClassName('loading-layer')[0].style.display = 'inline-block';
}
function removeLoadingLayer() {
document.getElementsByClassName('loading-layer')[0].style.display = 'none';
}
let startTime;
let endTime;
let ccc = 0;
function checkAllChess(map=chessMap, parentSide=side, steps=[], parentScoreSum=0) {
let chessMap = map;
calNum++
let roads = [];
if (steps.length == 0) {
console.log('chessMap', JSON.parse(JSON.stringify(chessMap)))
startTime = new Date().getTime();
addLoadingLayer();
bestSteps = [];
allSteps = [];
bestScore = -Infinity;
}
if (steps.length == 1) {
console.log(++ccc);
}
// 计算哪一边
let mainSide = parentSide;
if (steps.length % 2 == 1 ) {
mainSide = mainSide == '黑' ? '红' : '黑';
}
// round++
let isAlive = false;
for (let y = 0; y < chessMap.length; y++) {
for (let x = 0; x < chessMap[y].length; x++) {
let chess = chessMap[y][x];
if (chess[1] == parentSide) {
if (chess[0] == '车') {
getJuRoad(chessMap, roads, x, y);
}
if (chess[0] == '马') {
getMaRoad(chessMap, roads, x, y);
}
if (chess[0] == '象' || chess[0] == '相') {
getXiangRoad(chessMap, roads, x, y, chess[1]);
}
if (chess[0] == '士' || chess[0] == '仕') {
getShiRoad(chessMap, roads, x, y, chess[1]);
}
if (chess[0] == '炮' || chess[0] == '砲') {
getPaoRoad(chessMap, roads, x, y)
}
if (chess[0] == '卒' || chess[0] == '兵') {
getZuRoad(chessMap, roads, x, y, chess[1]);
}
if (chess[0] == '将' || chess[0] == '帅') {
getJiangRoad(chessMap, roads, x, y, chess[1]);
isAlive = true;
}
}
}
}
if (isAlive && round < 1000) {
let localBestScore = parentSide == mainSide ? -Infinity : Infinity;
// let road = roads[roads.length*Math.random()|0];
for (let i = 0; i < roads.length; i++) {
let _side = parentSide;
let road = roads[i];
let _map = JSON.parse(JSON.stringify(map))
// map 改变原地图,_map改变模拟地图
let eatedChess = _map[road._y][road._x];
let playChess = _map[road.y][road.x];
let score = downChess(_map, road._x, road._y, road.x, road.y, false);
let scoreSum = parentScoreSum;
let _steps = JSON.parse(JSON.stringify(steps));
if (_side == mainSide) {
score = score * (times - _steps.length);
} else {
score = -score * (times - _steps.length);
}
scoreSum += score;
_steps.push({
score,
_x: road._x,
_y: road._y,
x: road.x,
y: road.y,
playChess,
eatedChess,
id: uuid()
})
if (_steps.length < times) {
// _side = changeSide(_side);
// checkAllChess(_map, _side, _steps, scoreSum);
if (_side == mainSide && score >= localBestScore ) {
localBestScore = score;
if (score == localBestScore && Math.random()>.5) {
_side = changeSide(_side);
checkAllChess(_map, _side, _steps, scoreSum);
} else {
_side = changeSide(_side);
checkAllChess(_map, _side, _steps, scoreSum);
}
}
if (_side != mainSide && score <= localBestScore ) {
localBestScore = score;
if (score == localBestScore && Math.random()>.5) {
_side = changeSide(_side);
checkAllChess(_map, _side, _steps, scoreSum);
} else {
_side = changeSide(_side);
checkAllChess(_map, _side, _steps, scoreSum);
}
}
} else {
allSteps.push(_steps);
if (scoreSum > bestScore) {
bestScore = scoreSum;
bestSteps = _steps;
}
}
}
} else {
let scoreSum = parentScoreSum;
if (scoreSum > bestScore) {
bestScore = scoreSum;
bestSteps = steps;
}
// countFinal();
}
calNum--;
if (calNum == 0) {
endTime = new Date().getTime();
removeLoadingLayer();
let finalDic = {};
for (let i = 0; i < allSteps.length; i++) {
let _steps = allSteps[i];
let accScore = 0;
for (let j = 0; j < times; j++) {
_steps[j] = _steps[j] || {
score: 0,
_x: -1,
_y: -1,
x: -1,
y: -1,
playChess: ' ',
eatedChess: ' '
}
accScore += _steps[j].score;
_steps[j].accScore = accScore;
}
_steps[times - 1].in = 1;
for (let j = 0; j < times; j++) {
_steps[j].finalScore = accScore;
}
}
let accCheckDic = {};
console.log('---------------',accCheckDic['-'])
for (let j = times - 1; j > -1; j--) {
if (j % 2 == 0) {
// 自己方,越大越好
for (let i = 0; i < allSteps.length; i++) {
let _steps = allSteps[i];
let self = _steps[j];
if (!self.in) {
continue;
}
let parent = _steps[j - 1] || {};
let parentKey = parent.id || '-';
if (!accCheckDic[parentKey]) {
accCheckDic[parentKey] = {
bestId: '',
bestFinalScore: ((j % 2)-.5)*2*Infinity
}
}
if (accCheckDic[parentKey].bestFinalScore < self.finalScore) {
try {
delete accCheckDic[parentKey].parent.in;
} catch(err) {
}
accCheckDic[parentKey] = {
bestId: self.id,
level: j,
bestFinalScore: self.finalScore,
parent: parent,
src: self
}
parent.in = 1;
}
}
}
if (j % 2 == 1) {
// 敌对方,越小越好
for (let i = 0; i < allSteps.length; i++) {
let _steps = allSteps[i];
let self = _steps[j];
if (!self.in) {
continue;
}
let parent = _steps[j - 1] || {};
let parentKey = parent.id || '-';
if (!accCheckDic[parentKey]) {
accCheckDic[parentKey] = {
bestId: '',
bestFinalScore: ((j % 2)-.5)*2*Infinity
}
}
if (accCheckDic[parentKey].bestFinalScore > self.finalScore) {
try {
delete accCheckDic[parentKey].parent.in;
} catch(err) {
}
accCheckDic[parentKey] = {
bestId: self.id,
level: j,
bestFinalScore: self.finalScore,
parent: parent,
src: self
}
parent.in = 1;
}
}
}
}
console.log(accCheckDic)
console.log(accCheckDic['-'])
console.log(accCheckDic[accCheckDic['-'].bestId])
console.log(allSteps);
console.log(map)
downChess(map, accCheckDic['-'].src._x, accCheckDic['-'].src._y, accCheckDic['-'].src.x, accCheckDic['-'].src.y, true);
}
}
let dic = {};
function countFinal() {
dic['count'] = dic['count'] ? dic['count'] + 1 : 1;
dic[round] = dic[round] ? dic[round] + 1 : 1;
for (let y = 0; y < chessMap.length; y++) {
for (let x = 0; x < chessMap[y].length; x++) {
let chess = chessMap[y][x];
if (chess !== ' ') {
dic[chess] = dic[chess] ? dic[chess]+1 : 1;
}
}
}
chessMap = [
['车红一', '马红一', '相红一', '仕红一', '帅红一', '仕红二', '相红二', '马红二', '车红二'],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', '砲红一', ' ', ' ', ' ', ' ', ' ', '砲红二', ' '],
['兵红一', ' ', '兵红二', ' ', '兵红三', ' ', '兵红四', ' ', '兵红五'],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['卒黑一', ' ', '卒黑二', ' ', '卒黑三', ' ', '卒黑四', ' ', '卒黑五'],
[' ', '炮黑一', ' ', ' ', ' ', ' ', ' ', '炮黑二', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['车黑一', '马黑一', '象黑一', '士黑一', '将黑一', '士黑二', '象黑二', '马黑二', '车黑二']
]
round = 0;
}
function downChess(map, _x, _y, x, y, isFlush=true) {
if (_x==x&&_y==y) {
return false;
}
let chessMap = map;
let chess = chessMap[y][x].toString();
chessMap[y][x] = ' ';
eatedChess = chessMap[_y][_x].toString();
chessMap[_y][_x] = chess;
if (isFlush) {
flushChessCanvas();
side = side == '黑' ? '红' : '黑';
if (isGameMode && eatedChess[0] == '帅') {
alert('黑方胜');
}
if (isGameMode && eatedChess[0] == '将') {
alert('红方胜');
}
}
return scoreDic[eatedChess[0]];
}
function changeSide(side) {
if (side == '黑') {
side = '红';
} else {
side = '黑';
}
return side;
}
flushChessCanvas()
这么大一串,我猜你们也不会去看,给你们大致讲一下吧
大概就是搜索每一步每一方可走的路径,大约有
40
种可选路径,然后再这40
种中随机挑选1
条路,一直计算下去
当然,对手也不会太笨,他也会从这些中间挑
1
种走法,而且是最有利于他的方法(当然也可以选不利于他的,但他如果选了,我们赢得概率一定更大了)
所以层层下去,算出当下最利于我们的那一条路(考虑到对手选最好的情况下)
let roads = [];
let selectedWhat = null;
canvas1.addEventListener('click', (e)=>{
let viewWidth = window.innerWidth;
if (viewWidth > 1000) {
viewWidth = 1000;
}
let canvasWidth = 1005;
let ctx = canvas1.getContext('2d');
ctx.fillStyle = 'black';
let locX = e.clientX/viewWidth*canvasWidth;
let locY = e.clientY/viewWidth*canvasWidth;
let r = 50;
let x = (locX+r-100)/100|0;
let y = (locY+r-100)/100|0;
for (let i = 0; i < roads.length; i++) {
let road = roads[i];
if (road._x == x && road._y == y) {
let chess = chessMap[road.y][road.x];
downChess(chessMap, road._x, road._y, road.x, road.y);
side = chess[1] == '黑' ? '红' : '黑';
roads = [];
selectedWhat = null;
return;
break;
}
}
if (chessMap[y][x] != ' ') {
// ctx.fillText(`(${x},${y})${chessMap[y][x]}`, locX, locY);
selectedWhat = selectedWhat == chessMap[y][x] ? null : chessMap[y][x];
if (selectedWhat) {
ctx.fillStyle = 'blue';
flushChessCanvas();
roads = [];
getChessRoads(chessMap, x, y);
} else {
flushChessCanvas();
roads = [];
}
}
})
let autoBtn = document.querySelector('#auto-btn');
autoBtn.onclick = function() {
checkAllChess()
}
let resetBtn = document.querySelector('#reset-btn');
resetBtn.onclick = function() {
resetMap();
}
function getChessRoads(map=chessMap, x, y) {
let chessMap = map;
let chess = chessMap[y][x];
let isAlive = false;
if (chess[0] == '车') {
getJuRoad(chessMap, roads, x, y);
}
if (chess[0] == '马') {
getMaRoad(chessMap, roads, x, y);
}
if (chess[0] == '象' || chess[0] == '相') {
getXiangRoad(chessMap, roads, x, y, chess[1]);
}
if (chess[0] == '士' || chess[0] == '仕') {
getShiRoad(chessMap, roads, x, y, chess[1]);
}
if (chess[0] == '炮' || chess[0] == '砲') {
getPaoRoad(chessMap, roads, x, y)
}
if (chess[0] == '卒' || chess[0] == '兵') {
getZuRoad(chessMap, roads, x, y, chess[1]);
}
if (chess[0] == '将' || chess[0] == '帅') {
getJiangRoad(chessMap, roads, x, y, chess[1]);
isAlive = true;
}
for (let i = 0; i < roads.length; i++) {
let road = roads[i];
object.showRoad(chess[0], road._x, road._y, road.x, road.y);
}
}
这一处是纯自己实现的哦,此处可以有专利O(∩_∩)O
大概就是,点击事件后,通过
clientX
和clientY
计算那个位置所在的棋子是什么 xiang’qi
然后第二次点击事件后,通过
clientX
和clientY
计算目标位置是什么,不管那个位置是什么,只要在选中棋子的合规落子范围内,都把棋子走过去就OK啦
然后根据新得
Map
重绘Canvas
戊戌年的除夕,给爷爷看了我这个初版
我:我写了个电脑,可是怎么下,他都走的不对
爷爷:慢慢来么,像以前游戏机里的那个老人、中年人、长头发的女的和小孩,你先试着把那个小孩下赢嘛
我:哦哦,对的,我先一步步来?
最后的最后,在己亥的正月初一完成了我的算是一个完整版,爷爷很认真的和我的AI下了一局
我还是完败,哈哈哈?,后来事实证明我的确写了个笨呼呼的AI呢(它也同样输给了小青和我妹妹),之后再优化吧~但感觉心里还是蛮开心的
发现CSDN发布文章>改文章>重新发布 可以刷积分耶( •̀ ω •́ )y
https://gitee.com/ko-orz/chinese_chess/
https://tekii.cn/public/chinese_chess/
[1] 欲泪成雪. HTML5学习总结——canvas绘制象棋(canvas绘图) 2016-11-24 18:03
[2] 超逸_流. 象棋AI算法(一) 2015-07-29 21:29:28
[3] 超逸_流. 象棋AI算法(二) 2015-07-30 14:11:37
[4] 写了这篇文章的2019年02月18日22时41分的Maxmon