<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
*{
padding: 0;margin: 0;
} html,body{width:100%;height: 100%;}
/*canvas{
margin-top: 80px;
margin-left: 50px;
}*/
div{
text-align: center;
}
div button{
line-height: 50px;
font-size: 36px;
}
.canvas{
margin: 0 auto;
}
</style>
</head>
<body>
<div class="canvas">
<canvas id="gobang"></canvas>
</div> <div>
<button id="regret">悔棋</button>
</div>
</body>
<script type="text/javascript">
// 五子棋 class Gobang {
constructor(canvasId, rowCount = 16) {
this.canvasId = canvasId;
this.rowCount = rowCount;
this.resetData();
} // 渲染页面
renderUi() {
//清除之前的画布
this.ctx.clearRect(0, 0, this.width, this.height); // 重绘画布
this.drawChessBoard();
this.drawPieces();
} // 重置数据,再来一局
resetData() {
var body = document.documentElement || document.body;
var minWidth = Math.min(body.clientWidth,body.clientHeight); // 属性
this.pieces = []; // 棋子数组 二位数组[[],[]] 0——空 1——白 2——黑
this.colCount = this.rowCount; // 列数
this.cellWidth = minWidth / (this.rowCount); //每个格子的宽
this.width = this.rowCount * this.cellWidth; // 棋盘的宽
this.height = this.width; // 棋盘的高
this.R = this.cellWidth * 0.4; // 棋子半径
this.hisStatus = []; // 历史记录 history status
this.active = 2; // 当前走棋方
this.canvas = document.getElementById(this.canvasId); // canvas DOM
this.ctx = this.canvas.getContext("2d"); // canvas环境
this.victor = 0; // 胜利方
this.canContinue = true; // 是否可以继续下棋(产生赢家以后不可以)
this.myPositions = []; // 我方的推荐位置数组 格式:{x:5,y:6,weight:8}
this.enemyPositions = []; // 敌方的推荐位置数组 this.init();
} // 初始化数据
init() {
this.initCanvas();
this.initPiece();
this.renderUi();
} // 暂时给棋盘中间加一个黑棋
first(){
var center = Math.floor((this.rowCount+1)/2);
this.pieces[center][center] = this.active;
this.hisStatus[1] = this.deepClone(this.pieces);
this.exchange();
} // 设置棋盘的宽高
initCanvas() {
this.canvas.width = this.width;
this.canvas.height = this.height;
} // 初始化棋子
initPiece() {
var initPieces = [];
for(let i = 0; i <= this.rowCount; i++) { // 行
initPieces.push([]);
for(let j = 0; j <= this.colCount; j++) { // 列
if(i==0 || j==0 || i==this.rowCount || j==this.rowCount){
initPieces[i].push(-1); // 不可以走的位置-1 空0 白1 黑2
}else{
initPieces[i].push(0); // 空0 白1 黑2
}
}
}
this.pieces = this.deepClone(initPieces);
this.hisStatus[0] = this.deepClone(initPieces);
//this.first();
} // 获取某个棋子的颜色 0——空 1——白 2——黑
getColor (num){
var res = "";
switch(num){
case 0:
res = "";
break;
case 2:
res = "black";
break;
case 1:
res = "white";
break;
case -1:
res = "yellow";
break;
default:
res="red"; // 错误了
}
return res;
} // 画棋盘
drawChessBoard() {
// 背景
this.ctx.beginPath();
this.ctx.rect(0, 0, this.width, this.height);
this.ctx.closePath();
this.ctx.fillStyle = "#0099CC";
this.ctx.fill(); // 画横线
this.ctx.beginPath();
for(let i = 0; i < this.rowCount; i++) {
this.ctx.moveTo(0, this.cellWidth * i);
this.ctx.lineTo(this.width, this.cellWidth * i);
}
this.ctx.stroke(); // 画纵线
this.ctx.beginPath();
for(let i = 0; i < this.colCount; i++) {
this.ctx.moveTo( this.cellWidth * i, 0);
this.ctx.lineTo( this.cellWidth * i, this.height);
}
this.ctx.stroke();
} // 画一个棋子
drawDot(x, y, r, color) {
this.ctx.beginPath();
this.ctx.arc(x, y, r, 0, 2 * Math.PI);
this.ctx.closePath(); this.ctx.fillStyle = color;
this.ctx.fill();
} // 画所有的棋子
drawPieces(x, y) {
//console.log(this.pieces)
for(var i = 0; i < this.pieces.length; i++) { // 边界线不让走棋
for(let j=0; j < this.pieces[i].length;j++){
// if(this.pieces[i][j] == 1 || this.pieces[i][j] == 2){
if(this.pieces[i][j] !== 0){
var x = i * this.cellWidth;
var y = j * this.cellWidth;
// var y = j * this.cellWidth - this.cellWidth/2;
this.drawDot(x,y,this.R,this.getColor(this.pieces[i][j]));
}
}
}
} //
drawOnePiece(i,j){
var x = i * this.cellWidth;
var y = j * this.cellWidth;
this.drawDot(x,y,this.R,this.getColor(this.active));
} // 判断是否可以走这一步
canGo(x, y) {
if(this.canContinue === false){
alert("游戏已结束");
return false;
}
if(x==0 || y==0 || x==this.rowCount || y==this.rowCount){
alert("边界上不可以走棋哦");
return false;
}
if(this.pieces[x][y]==0){
return true;
}else {
return false;
}
} // 切换角色(换着走棋)
exchange(){
this.active = this.active == 1 ? 2 : 1;
} // 走一步棋
goStep(x,y) {
// 判断这一步是否可以走
if(this.canGo(x,y)){
this.pieces[x][y] = this.active;//添加棋子
this.drawOnePiece(x,y);//把棋子画上去
this.hisStatus.push(this.deepClone(this.pieces));//历史记录(悔棋复盘功能使用)
this.getVictor();//这里有个坑,棋子还没有画上去,已经把输赢判断了????
this.exchange();
if(this.active == 1 && !this.victor){ // 白棋,机器人
this.helpRecommend(this.active);
console.log(this.myPositions,this.enemyPositions)
var p = this.bestPosition();
console.log(p);
this.pieces[p.x][p.y] = this.active;
this.drawOnePiece(p.x,p.y);
this.hisStatus.push(this.deepClone(this.pieces));//历史记录(悔棋复盘功能使用)
this.getVictor();
this.exchange();
// this.renderUi();
}
}else {
// alert("这个位置已经被占领了,换个位置走吧");
}
} // 悔棋
regret() {
if(this.hisStatus.length<=1){
console.log("当前不可以悔棋了");
return;
}
this.hisStatus.pop();
this.hisStatus.pop();
this.pieces = this.deepClone(this.hisStatus[this.hisStatus.length-1]);
// this.exchange();
this.renderUi();
} //
helpRecommend(oneSide){
var enemySide = oneSide == 1 ? 2 : 1;
this.myPositions = [];
this.enemyPositions = []; for(let i=1;i<this.rowCount;i++){ for(let j=1;j<this.rowCount;j++){ // var arr = ["r","rd","d","ld","l","lt","t","rt"];
var arr = ["r","rd","d","ld"];
// 权重相关变量 forward backward center double null
var n2f1 = 0.2, // 两头空时 前面第一个空位置的权重
n2f2 = 0.1, // 两头空时 前面第二个空位置的权重
n2b1 = n2f1,
n2b2 = n2f2,
n1f1 = -0.1, // 一头空另一头是敌方或边界时 前面第一个空位置的权重
n1f2 = -0.2,
n1b1 = n1f1,
n1b2 = n1f2,
dn2c = 0.2, // 有两个片段时 两端都是空的时 中间位置的权重
dn2b1 = 0.1,// 有两个片段时 两端都是空的时 后方第一个位置的权重
dn2f1 = dn2b1,
dn1c = -0.1,
dn1b1 = -0.1,
dn1f1 = dn1b1; if(this.pieces[i][j]==oneSide){ // 我方
for(var d =0;d<arr.length;d++){
var count = 0;
count = this.directionCount(arr[d],oneSide,i,j);
var nd = this.nd(arr[d]);
var h = nd.h;
var v = nd.v; // 某个方向的末端的推荐权重 (权重暂时认为后方第一个位置和第二位位置一样) 两头空的+0.2 一端空的+0 两端都死的考虑能否凑5个子
if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空
if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空
if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+n2b1));
this.sumWeight(this.myPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n2b2));
}else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === oneSide){ // 末2己
let count2 = this.directionCount(arr[d],oneSide,i+(count+1)*h,j+(count+1)*v);
if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+dn2c));
this.sumWeight(this.myPositions,i+(count+1+count2)*h,j+(count+1+count2)*v,10**(count+count2+dn2b1));
}else {
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+dn1c));
}
}else { //末2敌或边界
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+n1b1));
}
}else { // 末1敌或边界 末1不可能是己方的
// 末端没有推荐的位置
}
}else if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === oneSide){ // 前1己 这里已经计算过了,跳过逻辑
continue;
}else { // 前1 敌方或边界
if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空
if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+n1b1));
this.sumWeight(this.myPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n1b2));
}else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === oneSide){ // 末2己
// this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+0.1));
let count2 = this.directionCount(arr[d],oneSide,i+(count+1)*h,j+(count+1)*v);
if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+dn1c));
this.sumWeight(this.myPositions,i+(count+1+count2)*h,j+(count+1+count2)*v,10**(count+count2+dn1b1));
}else {// 两端是死的 中间要么是5要么就没意义
if(count+1+count2 == 5){
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+count2+1));
}else{
this.sumWeight(this.myPositions,i+count*h,j+count*v,0);
//console.log("中间凑不够5个,中间权重是0");
}
}
}else { //末2敌或边界
if(count==4){ // 只有四颗子的时候这个位置才有意义
this.sumWeight(this.myPositions,i+count*h,j+count*v,10**(count+1));
}
}
}else { // 末1敌或边界 末1不可能是己方的
// 走不了了
}
} // 某个方向的前端的推荐权重
if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 后1空
if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空
if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空
this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+n2f1));
this.sumWeight(this.myPositions,i-2*h,j-2*v,10**(count+n2f2));
}else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === oneSide){ // 前2己
// this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+0.3));
let count2 = this.directionCount(this.reverseDirection(arr[d]),oneSide,i-2*h,j-2*v);
if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){
this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+dn2c));
this.sumWeight(this.myPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn2f1));
}else {
this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+dn1c));
this.sumWeight(this.myPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn1f1));
}
}else { //前2敌或边界
this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+n1f1));
}
}else { // 前1敌或边界 前1不可能是己方的
// 前端没有推荐的位置
}
}else if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === oneSide){ // 后1己 这里已经计算过了,跳过逻辑
continue;
}else { // 后1 敌方或边界
if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空
if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空
this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+n1f1));
this.sumWeight(this.myPositions,i-2*h,j-2*v,10**(count+n1f2));
}else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === oneSide){ // 前2己
// this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+0.1));
let count2 = this.directionCount(this.reverseDirection(arr[d]),oneSide,i-2*h,j-2*v);
if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){
this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+dn1c));
this.sumWeight(this.myPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn1f1));
}else {
if(count+1+count2 == 5){
this.sumWeight(this.myPositions,i-1*h,j-1*v,10**(count+count2+1));
}else{
this.sumWeight(this.myPositions,i-1*h,j-1*v,0);
//console.log("中间凑不够5个,中间权重是0");
}
}
}else { //前2敌或边界
if(count==4){ // 只有四颗子的时候这个位置才有意义
this.sumWeight(this.myPositions,i-2*h,j-2*v,10**(count+1));
}
}
}else { // 前1敌或边界 前1不可能是己方的
// 前后都是敌,推荐个锤子
}
}
}
}else if(this.pieces[i][j]==enemySide){ // 敌方
for(var d =0;d<arr.length;d++){
var count = 0;
count = this.directionCount(arr[d],enemySide,i,j);
var nd = this.nd(arr[d]);
var h = nd.h;
var v = nd.v;
// 某个方向的末端的推荐权重
if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空
if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空
if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+n2b1));
this.sumWeight(this.enemyPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n2b2));
}else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === enemySide){ // 末2己
let count2 = this.directionCount(arr[d],enemySide,i+(count+1)*h,j+(count+1)*v);
if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn2c));
this.sumWeight(this.enemyPositions,i+(count+1+count2)*h,j+(count+1+count2)*v,10**(count+count2+dn2b1));
}else {
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn1c));
}
// this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+0.3));
}else { //末2敌或边界
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+n1b1));
}
}else { // 末1敌或边界 末1不可能是己方的
// 末端没有推荐的位置
}
}else if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === enemySide){ // 前1己 这里已经计算过了,跳过逻辑
continue;
}else { // 前1 敌方或边界
if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 末1空
if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === 0){ //末2空
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+n1b1));
this.sumWeight(this.enemyPositions,i+(count+1)*h,j+(count+1)*v,10**(count+n1b2));
}else if(this.pieces[i+(count+1)*h] && this.pieces[i+(count+1)*h][j+(count+1)*v] === enemySide){ // 末2己
//this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+0.1));
let count2 = this.directionCount(arr[d],enemySide,i+(count+1)*h,j+(count+1)*v);
if(this.pieces[i+(count+1+count2)*h] && this.pieces[i+(count+1+count2)*h][j+(count+1+count2)*v] === 0){
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn1c));
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+dn1f1));
}else {// 两端是死的,看中间够5个不
if(count+1+count2 == 5){
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+count2+1));
}else{
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,0);
//console.log("中间凑不够5个,中间权重是0");
}
}
}else { //末2敌或边界
if(count==4){ // 只有四颗子的时候这个位置才有意义
this.sumWeight(this.enemyPositions,i+count*h,j+count*v,10**(count+1));
}
}
}else { // 末1敌或边界 末1不可能是己方的
// 走不了了
}
} // 某个方向的前端的推荐权重
if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === 0){ // 后1空
if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空
if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+n2f1));
this.sumWeight(this.enemyPositions,i-2*h,j-2*v,10**(count+n2f2));
}else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === enemySide){ // 前2己
// this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+0.3));
let count2 = this.directionCount(this.reverseDirection(arr[d]),enemySide,i-2*h,j-2*v);
if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+dn2c));
this.sumWeight(this.enemyPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn2f1));
}else {
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+dn1c));
}
}else { //前2敌或边界
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+dn1c));
}
}else { // 前1敌或边界 前1不可能是己方的
// 前端没有推荐的位置
}
}else if(this.pieces[i+count*h] && this.pieces[i+count*h][j+count*v] === enemySide){ // 后1己 这里已经计算过了,跳过逻辑
continue;
}else { // 后1 敌方或边界
if(this.pieces[i-1*h] && this.pieces[i-1*h][j-1*v] === 0){ // 前1空
if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === 0){ //前2空
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+n1f1));
this.sumWeight(this.enemyPositions,i-2*h,j-2*v,10**(count+n1f2));
}else if(this.pieces[i-2*h] && this.pieces[i-2*h][j-2*v] === enemySide){ // 前2己
// this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+0.1));
let count2 = this.directionCount(this.reverseDirection(arr[d]),enemySide,i-2*h,j-2*v);
if(this.pieces[i-(1+count2)*h] && this.pieces[i-(1+count2)*h][j-(1+count2)*v] === 0){
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+dn1c));
this.sumWeight(this.enemyPositions,i-(1+count2)*h,j-(1+count2)*v,10**(count+count2+dn1f1));
}else { // 前后是死的
if(count+1+count2 == 5){
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+count2+1));
}else{
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,0);
//console.log("中间凑不够5个,中间权重是0");
}
}
}else { //前2敌或边界
if(count==4){ // 只有四颗子的时候这个位置才有意义
this.sumWeight(this.enemyPositions,i-1*h,j-1*v,10**(count+1));
}
}
}else { // 前1敌或边界 前1不可能是己方的
// 前后都是敌,推荐个锤子
}
} }
}
}
}
} reverseDirection(direction){
var rd = "";
switch(direction){
case "r":
rd = "l";break;
case "rd":
rd = "lt";break;
case "d":
rd = "t";break;
case "ld":
rd = "rt";break;
case "l":
rd = "r";break;
case "lt":
rd = "rd";break;
case "t":
rd = "d";break;
case "rt":
rd = "ld";break;
default: console.error("输入方向不对,无法反转");
}
return rd;
} // 方向数字化numberDirection r(1,0) rd(1,1) ld(-1,1)
nd(direction){
var res = {h:0,v:0}; // h horizontal v vertical
switch(direction){
case "r":
res.h = 1;
res.v = 0;
break;
case "rd":
res.h = 1;
res.v = 1;
break;
case "d":
res.h = 0;
res.v = 1;
break;
case "ld":
res.h = -1;
res.v = 1;
break;
case "l":
res.h = -1;
res.v = 0;
break;
case "lt":
res.h = -1;
res.v = -1;
break;
case "t":
res.h = 0;
res.v = -1;
break;
case "rt":
res.h = 1;
res.v = -1;
break;
default: console.error("方向输入有误");
}
return res;
} // 合并同一个位置的权重
sumWeight(arr,x,y,weight){
var index = -1;
for(let i=0,len=arr.length;i<len;i++){
if(arr[i].x==x && arr[i].y==y){
index = i;
break;
}
}
if(index!=-1){ // 如果已存在则权重相加
arr[index].weight += weight;
}else{ // 如果不存在则添加一条
arr.push({x,y,weight});
}
} // 从推荐位置中找出最佳位置 权重最大的位置
bestPosition (){
var myMax=0,myP={},myArr=[];
for(let i=0,len=this.myPositions.length;i<len;i++){
if(this.myPositions[i].weight>myMax){
myMax = this.myPositions[i].weight;
myP.x = this.myPositions[i].x;
myP.y = this.myPositions[i].y;
}
}
var enemyMax = 0, enemyP = {}, enemyArr = [];
for(let i=0,len=this.enemyPositions.length;i<len;i++){
if(this.enemyPositions[i].weight>enemyMax){
enemyMax = this.enemyPositions[i].weight;
enemyP.x = this.enemyPositions[i].x;
enemyP.y = this.enemyPositions[i].y;
}
}
// console.log(this.myPositions,this.ememyPositions);
console.log("敌方权重最大:"+enemyMax,"我方权重最大:"+myMax); for(let i=0,len=this.myPositions.length;i<len;i++){
if(this.myPositions[i]==myMax){
myArr.push(this.deepClone(this.myPositions[i]));
}
}
for(let i=0,len=this.enemyPositions.length;i<len;i++){
if(this.enemyPositions[i]==enemyMax){
enemyArr.push(this.deepClone(this.enemyPositions[i]));
}
}
if(enemyMax>myMax){
// 敌方权重最大的地方(有相同位置时,谋求自己的大权重位置)
var myMaxW = 0; // 我方在敌方最有位置处的最佳权重
var recommedP = enemyP;
for(let i=0, len=enemyArr.length;i<len;i++){
for(let j=0,len1=this.myPositions.length;j<len1;j++){
if(this.myPositions[j].x==enemyArr[i].x && this.myPositions[j].y==enemyArr[i].y){
if(this.myPositions[j].weight>myMaxW){
myMaxW = this.myPositions[j].weight;
recommedP.x = this.myPositions[j].x;
recommedP.y = this.myPositions[j].y;
}
}
}
}
return recommedP;
}else {
// 我方权重最大的地方(有相同位置时,占据敌方的相对大权重位置)
var enemyMaxW = 0; // 我方在敌方最有位置处的最佳权重
var recommedP = myP;
for(let i=0, len=myArr.length;i<len;i++){
for(let j=0,len1=this.enemyPositions.length;j<len1;j++){
if(this.enemyPositions[j].x==myArr[i].x && this.enemyPositions[j].y==myArr[i].y){
if(this.enemyPositions[j].weight>enemyMaxW){
enemyMaxW = this.enemyPositions[j].weight;
recommedP.x = this.enemyPositions[j].x;
recommedP.y = this.enemyPositions[j].y;
}
}
}
}
return recommedP;
}
} // 获取赢家
getWinner(){
switch(this.victor){
case 0:
console.log("还没产生赢家");break;
case 1:
this.canContinue = false;
setTimeout(()=>{alert("白棋赢");},30);
break;
case 2:
this.canContinue = false;
setTimeout(()=>{alert("黑棋赢");},30);
break;
default:;
}
} // 判断输赢
getVictor() { //1.找到一个当前棋子,2.判断它的右、右下、下、左下四个方向是否连成5个棋子
for(var i=1;i<this.pieces.length;i++){
for(var j=1;j<this.pieces[i].length;j++){
if(this.pieces[i][j] == this.active){
if(this.directionCount("r",this.active,i,j) == 5){// 右r 下d 左l 上t
this.victor = this.active;
this.getWinner();
return;
}
if(this.directionCount("rd",this.active,i,j) == 5){
this.victor = this.active;
this.getWinner();
return;
}
if(this.directionCount("d",this.active,i,j) == 5){
this.victor = this.active;
this.getWinner();
return;
}
if(this.directionCount("ld",this.active,i,j) == 5){
this.victor = this.active;
this.getWinner();
return;
}
}
}
} } // 此函数替代了原来的 directionCount
directionCount(direction,oneSide,i,j){
var count = 0;
var nd = this.nd(direction);
if(this.pieces[i][j] == oneSide){
count = 1;
for(let k=1;k<5;k++){
if(this.pieces[i+k*nd.h] && this.pieces[i+k*nd.h][j+k*nd.v] === oneSide){
count++;
continue;
}else {
break;
}
}
}
return count;
} // 深拷贝
deepClone(values) {
var copy; // Handle the 3 simple types, and null or undefined
if(null == values || "object" != typeof values) return values; // Handle Date
if(values instanceof Date) {
copy = new Date();
copy.setTime(values.getTime());
return copy;
} // Handle Array
if(values instanceof Array) {
copy = [];
for(var i = 0, len = values.length; i < len; i++) {
copy[i] = this.deepClone(values[i]);
}
return copy;
} // Handle Object
if(values instanceof Object) {
copy = {};
for(var attr in values) {
if(values.hasOwnProperty(attr)) copy[attr] = this.deepClone(values[attr]);
}
return copy;
} throw new Error("Unable to copy values! Its type isn't supported.");
}
}
</script>
<script type="text/javascript">
var gobang = new Gobang("gobang");
var canvas = document.getElementById("gobang");
console.log(canvas.getBoundingClientRect());
var offset;
canvas.addEventListener("click",function(e){
offset = canvas.getBoundingClientRect();
var x = Math.round((e.clientX - offset.left) / gobang.cellWidth);
var y = Math.round((e.clientY - offset.top) / gobang.cellWidth);
console.log(x,y,"点击位置"); // 走棋
gobang.goStep(x,y);
},false);
document.getElementById("regret").addEventListener("click",()=>{
gobang.regret();
},false);
</script>
</html>

js+canvas五子棋人机大战ai算法的更多相关文章

  1. JS+canvas实现人机大战之五子棋

    效果图: html代码如下: <!DOCTYPE html><html>    <head>        <meta charset="utf-8 ...

  2. js实现五子棋人机对战源码

    indexhtml <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  3. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

  4. HTML5 五子棋 - JS/Canvas 游戏

    背景介绍 因为之前用c#的winform中的gdi+,java图形包做过五子棋,所以做这个逻辑思路也就驾轻就熟,然而最近想温故html5的canvas绘图功能(公司一般不用这些),所以做了个五子棋,当 ...

  5. 利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈

    前言     本文主要介绍利用开源引擎 lufylegend.js开发基于Html5的游戏--五子棋,主要叙述其详细开发过程. 游戏规则 玩过五子棋的都应该知道五子棋的规则,这里就简单介绍其规则. 1 ...

  6. Java五子棋小游戏(控制台纯Ai算法)

    Java五子棋小游戏(控制台纯Ai算法) 继续之前的那个五子棋程序 修复了一些已知的小Bug 这里是之前的五子棋程序 原文链接 修复了一些算法缺陷 本次增加了AI算法 可以人机对战 也可以Ai对Ai看 ...

  7. 【中国象棋人机对战】引入了AI算法,学习低代码和高代码如何混编并互相调用

    以低代码和高代码(原生JS代码)混编的方式引入了AI算法,学习如何使用表达式调用原生代码的.整个过程在众触低代码应用平台进行,适合高阶学员. AI智能级别演示 AI算法分三个等级,体现出来的智能水平不 ...

  8. 随便谈谈alphago与人机大战

    3月16日历时8天的人机大战终于落下帷幕,alphago以4:1的比分击败了当年如日中天的李世石.这个结果让我这个围棋爱好者+计算机爱好者百感交集…… ——一个时代落幕了,一个新的时代开启了. 这次人 ...

  9. H5版俄罗斯方块(3)---游戏的AI算法

    前言: 算是"long long ago"的事了, 某著名互联网公司在我校举行了一次"lengend code"的比赛, 其中有一题就是"智能俄罗斯方 ...

随机推荐

  1. Wamp Apache 启动失败检测方法

    一般情况下,看错误日志就可以解决.如果遇到错误日志看不到的情况,不放试试下面的方法 //无错误日志解决办法cmd命令行切换到C:\wamp\bin\apache\apache2.4.9\bin目录 输 ...

  2. web端的兼容性测试

    目前主流的浏览器有:chrome.firefox.safari.IE edge.Opera等.其中IE edge ,Google浏览器 和firefox被称为现代浏览器. 浏览器排行榜2019年4月浏 ...

  3. 使用线程 Monitor.Wait() 和 Monitor.Pulse()

      Wait() 和 Pulse() 机制用于线程间交互.当在一个对象上使用Wait() 方法时,访问这个对象的线程就会一直等待直到被唤醒.Pulse() 和 PulseAll() 方法用来通知等待的 ...

  4. python_数据类型_list

    names = ['one','two','three','four','five'] #列表切片 print(names[0:]) #['one', 'two', 'three', 'four', ...

  5. Java中用JXL导出Excel代码详解

    jxl是一个韩国人写的java操作excel的工具, 在开源世界中,有两套比较有影响的API可供使用,一个是POI,一个是jExcelAPI.其中功能相对POI比较弱一点.但jExcelAPI对中文支 ...

  6. 洛谷P1311 [NOIP2011提高组Day1T2]选择客栈

    P1311 选择客栈 题目描述 丽江河边有n 家很有特色的客栈,客栈按照其位置顺序从 1 到n 编号.每家客栈都按照某一种色调进行装饰(总共 k 种,用整数 0 ~ k-1 表示),且每家客栈都设有一 ...

  7. Html5知识点以及兼容性

    什么的HTNL5? HTML5 是最新的 HTML 标准. HTML5 是专门为承载丰富的 web 内容而设计的,并且无需额外插件. HTML5 拥有新的语义.图形以及多媒体元素. HTML5 提供的 ...

  8. sql server 创建视图添加表时出现从其他数据库导入的表未显示出来

    创建视图添加表时出现从其他数据库导入的表未显示出来,通过数据库刷新,也不能解决.关闭SQL server management studio 后,再次进入,在创建视图的时候添加表的列表就出现了新导入的 ...

  9. 如何解决Firefox浏览器地址栏中文搜索速度很慢

    一.插件安装 之前使用Chrome浏览器,习惯在地址栏中直接进行中文搜索.转到Firefox之后,突然发现在地址栏进行中文搜索,访问速度会很慢. 可以使用插件解决这个问题:Omnibar 插件地址:h ...

  10. LintCode刷题笔记-- BackpackIV

    标签: 动态规划 描述: Given an integer array nums with all positive numbers and no duplicates, find the numbe ...