JS+canvas五子棋人机大战
1. 创建实例
function Gobang () {
this.over = false; // 是否结束
this.player = true; // true:我 false:电脑
this.allChesses = []; // 所有棋子
this.existChesses = [] // 已经落下的棋子
this.winsCount = 0; // 赢法总数
this.wins = []; // 所有赢法统计
this.myWins = []; //我的赢法统计
this.computerWins = []; //电脑赢法统计
}
2. 初始化
//初始化
Gobang.prototype.init = function(opts) {
// 生成canvas棋盘
this.createCanvas(opts);
//棋盘初始化
this.boardInit();
// 鼠标移动聚焦功能实现
this.mouseMove();
//算法初始化
this.algorithmInit();
//落子功能实现
this.dorpChess();
}
3. 生成canvas棋盘
//初始化
//生成canvas
Gobang.prototype.createCanvas = function(opts) {
var opts = opts || {};
if (opts.width && opts.width%30 !== 0) throw new RangeError(opts.width+'不是30的倍数');
this.col = (opts.width && opts.width/30) || 15; // 棋盘列
var oCanvas = document.createElement('canvas');
oCanvas.width = oCanvas.height = opts.width || 450;
this.canvas = oCanvas;
document.querySelector(opts.container || 'body').appendChild(this.canvas);
this.ctx = oCanvas.getContext('2d');
}
4. 初始化棋盘
//棋盘初始化
Gobang.prototype.boardInit = function(opts){
this.drawBoard();
}
// 画棋盘
Gobang.prototype.drawBoard = function(){
this.ctx.strokeStyle = "#bfbfbf";
for (var i = 0; i < this.col; i++) {
this.ctx.moveTo(15+ 30*i, 15);
this.ctx.lineTo(15+ 30*i, this.col*30-15);
this.ctx.stroke();
this.ctx.moveTo(15, 15+ 30*i);
this.ctx.lineTo(this.col*30-15, 15+ 30*i);
this.ctx.stroke();
}
}

5. 画棋子
// 画棋子
Gobang.prototype.drawChess = function(x, y, player){
var x = 15 + x * 30,
y = 15 + y * 30;
this.ctx.beginPath();
this.ctx.arc(x, y, 13, 0, Math.PI*2);
var grd = this.ctx.createRadialGradient(x + 2, y - 2, 13 , x + 2, y - 2, 0);
if (player) { //我 == 黑棋
grd.addColorStop(0, '#0a0a0a');
grd.addColorStop(1, '#636766');
}else{ //电脑 == 白棋
grd.addColorStop(0, '#d1d1d1');
grd.addColorStop(1, '#f9f9f9');
}
this.ctx.fillStyle = grd;
this.ctx.fill()
}

6. 移动聚焦
// 鼠标移动时触发聚焦效果, 需要前面的聚焦效果消失, 所有需要重绘canvas
Gobang.prototype.mouseMove = function(){
var that = this;
this.canvas.addEventListener('mousemove', function (e) {
that.ctx.clearRect(0, 0, that.col*30, that.col*30);
var x = Math.floor((e.offsetX)/30),
y = Math.floor((e.offsetY)/30);
//重绘棋盘
that.drawBoard();
//移动聚焦效果
that.focusChess(x, y);
//重绘已经下好的棋子
that.redrawedChess()
});
}
//鼠标移动聚焦
Gobang.prototype.focusChess = function(x, y){
this.ctx.beginPath();
this.ctx.fillStyle = '#E74343';
this.ctx.arc(15 + x * 30, 15 + y * 30, 6, 0, Math.PI*2);
this.ctx.fill();
}
//重绘当前下好的棋子
Gobang.prototype.redrawedChess = function(x, y){
for (var i = 0; i < this.existChesses.length; i++) {
this.drawChess(this.existChesses[i].x, this.existChesses[i].y, this.existChesses[i].player);
}
}

7. 算法初始化
//算法初始化
Gobang.prototype.algorithmInit = function(){
//初始化棋盘的每个位置和赢法
for (var x = 0; x < this.col; x++) {
this.allChesses[x] = [];
this.wins[x] = [];
for (var y = 0; y < this.col; y++) {
this.allChesses[x][y] = false;
this.wins[x][y] = [];
}
}
//获取所有赢法
this.computedWins();
// 初始化电脑和我每个赢法当前拥有的棋子数
for (var i = 0; i < this.winsCount; i++) {
this.myWins[i] = 0;
this.computerWins[i] = 0;
}
}
8. 获取所有赢法
Gobang.prototype.computedWins = function(){
/*
直线赢法
以15列为准
*/
for (var x = 0; x < this.col; x++) { //纵向所有赢法
for (var y = 0; y < this.col-4; y ++) {
this.winsCount ++;
/*
如:
1.组成的第一种赢法
[0,0]
[0,1]
[0,2]
[0,3]
[0,4]
2.组成的第二种赢法
[0,1]
[0,2]
[0,3]
[0,4]
[0,5]
以此类推一列最多也就11种赢法, 所有纵向x有15列 每列最多11种, 所有纵向总共15 * 11种
*/
//以下for循环给每种赢法的位置信息储存起来
for (var k = 0; k < 5; k ++) {
this.wins[x][y+k][this.winsCount] = true;
/*
位置信息
第一种赢法的时候:
this.wins = [
[
[1:true],
[1:true],
[1:true],
[1:true],
[1:true]
],
[
......
]
]
虽然这是一个三维数组, 我们把它拆分下就好理解了
相当于 this.wins[0][0][1], this.wins[0][4][1], this.wins[0][5][1], this.wins[0][6][1], this.wins[0][7][1]
因为对象可以这样取值:
var obj = {
a: 10,
b: 'demo'
}
obj['a'] === obj.a
所有也就相当于 this.wins[0][0].1, this.wins[0][8].1, this.wins[0][9].1, this.wins[0][10].1, this.wins[0][11].1
虽然数组不能这么取值,可以这么理解
所以
this.wins[0][0].1 就可以理解为 在 x=0, y=0, 上有第一种赢法
this.wins[0][12].1 就可以理解为 在 x=0, y=1, 上有第一种赢法
......
以上this.wins[0][0],this.wins[0][13]...可以看作是 this.wins[x][y]
所以第一种赢法的坐标就是: [0,0] [0,1] [0,2] [0,3] [0,4]
*/
}
}
}
for (var y = 0; y < this.col; y++) { //横向所有赢法, 同纵向赢法一样,也是15 * 11种
for (var x = 0; x < this.col-4; x ++) {
this.winsCount ++;
for (var k = 0; k < 5; k ++) {
this.wins[x+k][y][this.winsCount] = true;
}
}
}