记得读大学时,有段时间特别喜欢和室友们下五子棋,因为脑子不是特别灵光,再加上室友确实经验丰富,本身天然是屡屡战败。时光荏苒,一眨眼好多年过去了,非常怀念那时惬意的时光!大学毕业后,室友们都从事了不一样行业的工做,我也是如愿选择了作“程序员”。AI近些年来一直很火热,其余行业的小伙伴老是误认为咱们很厉害的样子,却不知“如鱼饮水,冷暖自知”。不过“专业思想”还确实是有的,因此有感写了这篇博文,和你们就五子棋探讨一下“计算机”思惟和“人类”思惟的区别和联系。话很少说,先上一张“战败”效果图(我认可我仍是下不过本身的智能算法):点击查看源代码:git
五子棋的棋盘很简单,不过是一些横竖交叉的线条而已。懂canvas的同窗天然知道是怎么回事:设计好坐标以后,在画布上画15条横竖交叉的线就能够了,也是横x,竖y,两个for循环而已。上代码:程序员
context.strokeStyle = "#BFBFBF"; //设置线条颜色 function drawChessBoard() { //绘制棋盘 for(var i=0; i<15; i++) { //横线条 context.moveTo(20 + i*40, 20); context.lineTo(20 + i*40, 580); context.stroke(); //竖线条 context.moveTo(20, 20 + i*40); context.lineTo(580, 20 + i*40); context.stroke(); } }
“棋子”是圆的,有黑、白两种颜色。只要咱们找好圆心点,设置好半径,颜色就能够了。为了使棋子看起来更加逼真,能够给棋子中心添加渐变光泽,也不过是调用一个函数,封装成函数代码以下:github
function drawChessMan(i, j) { //绘制棋子 context.beginPath(); context.arc(20 + i*40, 20 + j*40, 18, 0, 2*Math.PI); context.closePath(); var gradient = context.createRadialGradient(23 + i*40, 17 + j*40, 18, 23 + i*40, 17 + j*40, 0); if(curColor === 'white') { gradient.addColorStop(0, "#D1D1D1"); gradient.addColorStop(1, "#F9F9F9"); } if(curColor === 'black') { gradient.addColorStop(0, "#0A0A0A"); gradient.addColorStop(1, "#636766"); } context.fillStyle = gradient; context.fill(); curColor = (curColor === 'white') ? 'black' : 'white'; }
增长鼠标点击事件,在棋盘上寻找最近的落子点绘制棋子。设置一个当前棋子颜色变量,黑白交替绘制,实现这个功能在棋子绘制函数中就已经存在:curColor = (curColor === 'white') ? 'black' : 'white';下边是鼠标事件监听函数算法
//鼠标落下,画棋子 chessboard.onclick = function(e) { ...... var x = e.offsetX; var y = e.offsetY; var i = Math.floor(x / 40); var j = Math.floor(y / 40); if(chessManStatus[i][j] === 0) { drawChessMan(i, j); ...... } }
咱们不得不认可“计算机”的计算速度比人类要快的多,它可以每秒进行亿万次计算,并且按照既定的流程走,它永远不会累,也不会失误。若是我是“计算机”,我就把全部对手可能会赢的状况全都算出来,而后根据下棋的过程,根据每一次的数据状况,计算出一个坐标,这个坐标棋子可以阻止对手赢比赛,也能让本身更快的赢比赛。事实上,计算机也是这样作的,提早计算好全部可能会赢的状况对聪明的人类来讲只须要几秒,可是对于计算机来讲,作完这件事情并存储全部的数据,只是毫秒间的工做而已。下面是程序的实现(和咱们想象的同样:将横的、竖的、倾斜的连着五个相同棋子的状况都考虑进去,就是全部会赢的状况。并且计算机还将棋子的状态,本身和人的数据作分类处理。)canvas
var wins = [], personWin = [], computerWin = [],chessManStatus = [], winCount = 0; //全部获胜的数量和数量统计 for(var i=0;i<15;i++) { wins[i] = []; chessManStatus[i] = []; for(var j=0;j<15;j++) { wins[i][j] = []; chessManStatus[i][j] = 0; } } for(var i=0;i<15;i++) { for(var j=0;j<11;j++) { for(var k=0;k<5;k++){ wins[i][j+k][winCount] = true; } winCount++; } } for(var i=0;i<15;i++) { for(var j=0;j<11;j++) { for(var k=0;k<5;k++){ wins[j+k][i][winCount] = true; } winCount++; } } for(var i=0;i<11;i++) { for(var j=0;j<11;j++) { for(var k=0;k<5;k++){ wins[i+k][j+k][winCount] = true; } winCount++; } } for(var i=0;i<11;i++) { for(var j=14;j>3;j--) { for(var k=0;k<5;k++){ wins[i+k][j-k][winCount] = true; } winCount++; } } for(var i=0; i<winCount; i++) { personWin[i] = 0; computerWin[i] = 0; }
通过经过棋盘上全部可能胜利的状况不过572种而已!函数
若是我是计算机,接下来我要作的就是当“聪明的人类”下好棋以后,我怎样下好本身的棋。这个棋的目标:不让人类赢,让本身赢!做为人类的我会大概看一下棋盘上的全部棋子,而后选好一个落棋点,我很羡慕计算机的计算能力,若是我是它我就能够计算一下棋盘上的每个坐标点,对它们的状况进行评估打分,而后选得分最高的点下棋。固然计算机就是这么作的,每一次下棋它都作了128700次计算来挑选出最合适的落子点。而进行这么屡次计算也不过是毫秒之间的事情,因此计算机下棋特别的快,快到“人类”看不出它的反应,几乎和本身同时下棋的,或许你的棋子刚落下,它就赢了,感受特别沮丧!优化
function computerAI() { //电脑智能下棋 var personScore = [],computerScore = [],maxScore = 0,curX = 0, curY = 0; for(var i=0; i<15; i++) { personScore[i] = [];computerScore[i] = []; for(var j=0; j<15; j++) { personScore[i][j] = 0;computerScore[i][j] = 0; } } for(var i=0; i<15; i++) { for(var j=0; j<15; j++) { if(chessManStatus[i][j] == 0) { for(var k=0; k<winCount; k++) { if(wins[i][j][k]) { if(personWin[k] == 1 || personWin[k] == 2 || personWin[k] == 3 || personWin[k] == 4) { personScore[i][j] += personWin[k]*personWin[k] * 200; } if(computerWin[k] == 1 || computerWin[k] == 2 || computerWin[k] == 3 || computerWin[k] == 4) { computerScore[i][j] += (computerWin[k]*computerWin[k] - 1) * 200 + 399; } } } if(personScore[i][j] > maxScore) { maxScore = personScore[i][j]; curX = i; curY = j; }else if(personScore[i][j] == maxScore) { if(computerScore[i][j] > computerScore[curX][curY]) { curX = i; curY = j; } } if(computerScore[i][j] > maxScore) { maxScore = computerScore[i][j]; curX = i; curY = j; }else if(computerScore[i][j] == maxScore) { if(personScore[i][j] > personScore[curX][curY]) { curX = i; curY = j; } } } } } drawChessMan(curX, curY); ...... }
这个打分算法是整个算法的核心。计算机将连在一块儿的棋子作打分,1个棋子分数很低,只有200;两个棋子就会翻到800;三个棋子会更高,幂次运算。有了评分标准之后,计算机开始评分,先给对手评分,给对手评完分数后再给本身评分,计算机本身的评分规则比对手评分规则高,可是原则上是2个棋子连在一块儿的评分不会高于对手三个棋子连在一块儿的:计算机在本身赢以前是不会让对手有赢的机会的。spa
那么游戏什么事件结束呢?固然是有五个棋子连在一块儿就结束了。这些工做就交给计算机来作吧:计算机记录每个赢的状况下连珠棋子的个数,当有棋子达到5时游戏就结束了,固然黑白棋子是互斥的,当白棋在一个位置有连珠时,黑棋就永远没有机会了,咱们在程序上给它设置为6,无论它怎样加连珠,都不多是5了。初始化一个gameOver变量为false,标识游戏是否结束,当游戏结束时,取反就行:gameOver = !gameOver了。设计
for(var k=0; k<winCount; k++) { if(wins[curX][curY][k]) { computerWin[k]++; personWin[k] = 6; if(computerWin[k] == 5) { alert('电脑赢了!'); gameOver = !gameOver; } } }
我认可本身代码写的很烂,考虑不到不少优化的状况。可是我把能想到的,能作到的还都是尽力作到了。好比我在一个已经有棋子的地方点击,是不会让系统从新绘制棋子的;我在游戏结束以后,不会让接下来的工做继续进行的;没有从新开始,没有善后工做,这些就留想要作一个完美做品的人来作吧。写这个智能算法给个人感悟就是我更可以像计算机同样的去思考问题了,人类的思考能力会受到情感的左右,而计算机不会,一个越成熟的人,越会像计算机同样,像程序同样,作事情井井有理,受情感因素的干扰会很小。3d