原标题:原生 JS + Canvas 实现五子棋游戏
||
一、功能模块
先看下现在做完的效果:
线上体验:
https://wj704.github.io/five_game.html
主要功能模块为:
人机对战功能
悔棋功能
撤销悔棋功能二、代码详解 2.1 人机对战功能实现
从效果图可以看到,棋盘的横竖可以放的位置为15*15,通过canvas画棋盘:
//绘画棋盘
vardrawChessBoard=function(){
for(vari=0;i<15;i++){
context.moveTo(15+i*30,15);
context.lineTo(15+i*30,435);
context.stroke();
context.moveTo(15,15+i*30);
context.lineTo(435,15+i*30);
context.stroke();
}
}
知道格子数后,我们先看五子棋有多少种赢法:
//赢法数组
varwins=[];
for(vari=0;i<15;i++){
wins[i]=[];
for(varj=0;j<15;j++){
wins[i][j]=[];
}
}
varcount=0;//赢法总数
//横线赢法
for(vari=0;i<15;i++){
for(varj=0;j<11;j++){
for(vark=0;k<5;k++){
wins[i][j+k][count]=true;
}
count++;
}
}
//竖线赢法
for(vari=0;i<15;i++){
for(varj=0;j<11;j++){
for(vark=0;k<5;k++){
wins[j+k][i][count]=true;
}
count++;
}
}
//正斜线赢法
for(vari=0;i<11;i++){
for(varj=0;j<11;j++){
for(vark=0;k<5;k++){
wins[i+k][j+k][count]=true;
}
count++;
}
}
//反斜线赢法
for(vari=0;i<11;i++){
for(varj=14;j>3;j--){
for(vark=0;k<5;k++){
wins[i+k][j-k][count]=true;
}
count++;
}
}
根据赢法总数定义分别保存计算机和人赢法的数组:
for(vari=0;i
myWin[i]=0;
_myWin[i]=0;
computerWin[i]=0;
_compWin[i]=0;
}
然后就是人开始下棋:
// 我,下棋
chess.οnclick=function(e){
if(over){// 游戏结束
return;
}
if(!me){
return;
}
varx=e.offsetX;
vary=e.offsetY;
vari=Math.floor(x/30);
varj=Math.floor(y/30);
_nowi=i;
_nowj=j;
if(chressBord[i][j]==0){
oneStep(i,j,me);
chressBord[i][j]=1;//我,已占位置
for(vark=0;k
if(wins[i][j][k]){
myWin[k]++;
_compWin[k]=computerWin[k];// 为悔棋做准备
computerWin[k]=6;//这个位置对方不可能赢了
if(myWin[k]==5){
resultTxt.innerHTML='恭喜,你赢了!';
over=true;
}
}
}
if(!over){
me=!me;
computerAI();
}
}
// 悔棋功能可用
backbtn.className=backbtn.className.replace(newRegExp("(s|^)unable(s|$)")," ");
}
oneStep() 方法为落子,要在棋盘上画一个棋子:
//画棋子
varoneStep=function(i,j,me){
// debugger;
context.beginPath();
context.arc(15+i*30,15+j*30,13,0,2*Math.PI);//画圆
context.closePath();
//渐变
vargradient=context.createRadialGradient(15+i*30+2,15+j*30-2,13,15+i*30+2,15+j*30-2,0);
if(me){
gradient.addColorStop(0,'#0a0a0a');
gradient.addColorStop(1,'#636766');
}else{
gradient.addColorStop(0,'#d1d1d1');
gradient.addColorStop(1,'#f9f9f9');
}
context.fillStyle=gradient;
context.fill();
}
接着看计算机怎么下棋,具体看computerAI()方法:
// 计算机下棋
varcomputerAI=function(){
varmyScore=[];
varcomputerScore=[];
varmax=0;
varu=0,v=0;
for(vari=0;i<15;i++){
myScore[i]=[];
computerScore[i]=[];
for(varj=0;j<15;j++){
myScore[i][j]=0;
computerScore[i][j]=0;
}
}
for(vari=0;i<15;i++){
for(varj=0;j<15;j++){
if(chressBord[i][j]==0){
for(vark=0;k
if(wins[i][j][k]){
if(myWin[k]==1){
myScore[i][j]+=200;
}elseif(myWin[k]==2){
myScore[i][j]+=400;
}elseif(myWin[k]==3){
myScore[i][j]+=2000;
}elseif(myWin[k]==4){
myScore[i][j]+=10000;
}
if(computerWin[k]==1){
computerScore[i][j]+=220;
}elseif(computerWin[k]==2){
computerScore[i][j]+=420;
}elseif(computerWin[k]==3){
computerScore[i][j]+=2100;
}elseif(computerWin[k]==4){
computerScore[i][j]+=20000;
}
}
}
if(myScore[i][j]>max){
max=myScore[i][j];
u=i;
v=j;
}elseif(myScore[i][j]==max){
if(computerScore[i][j]>computerScore[u][v]){
u=i;
v=j;
}
}
if(computerScore[i][j]>max){
max=computerScore[i][j];
u=i;
v=j;
}elseif(computerScore[i][j]==max){
if(myScore[i][j]>myScore[u][v]){
u=i;
v=j;
}
}
}
}
}
_compi=u;
_compj=v;
oneStep(u,v,false);
chressBord[u][v]=2;//计算机占据位置
for(vark=0;k
if(wins[u][v][k]){
computerWin[k]++;
_myWin[k]=myWin[k];
myWin[k]=6;//这个位置对方不可能赢了
if(computerWin[k]==5){
resultTxt.innerHTML='o(╯□╰)o,计算机赢了,继续加油哦!';
over=true;
}
}
}
if(!over){
me=!me;
}
}
根据相应的权重,计算出计算机应该落子的位置。
2.2 悔棋功能
要提的是,这里暂时只能悔一步棋。悔棋功能主要关键点是:1、销毁刚刚下的棋子;2、将之前不可能赢的状态还原;看下具体的代码:
// 悔棋
backbtn.οnclick=function(e){
if(!backAble){return;}
over=false;
me=true;
// 我,悔棋
chressBord[_nowi][_nowj]=0;//我,已占位置 还原
minusStep(_nowi,_nowj);//销毁棋子
for(vark=0;k
if(wins[_nowi][_nowj][k]){
myWin[k]--;
computerWin[k]=_compWin[k];//这个位置对方可能赢
}
}
// 计算机相应的悔棋
chressBord[_compi][_compj]=0;//计算机,已占位置 还原
minusStep(_compi,_compj);//销毁棋子
for(vark=0;k
if(wins[_compi][_compj][k]){
computerWin[k]--;
myWin[k]=_myWin[i];//这个位置对方可能赢
}
}
resultTxt.innerHTML='--益智五子棋--';
returnAble=true;
backAble=false;
// 撤销悔棋功能可用
returnbtn.className=returnbtn.className.replace(newRegExp("(s|^)unable(s|$)")," ");
}
minusStep()为销毁棋子的方法,我们看下是怎么销毁的。
//销毁棋子
varminusStep=function(i,j){
//擦除该圆
context.clearRect((i)*30,(j)*30,30,30);
// 重画该圆周围的格子
context.beginPath();
context.moveTo(15+i*30,j*30);
context.lineTo(15+i*30,j*30+30);
context.moveTo(i*30,j*30+15);
context.lineTo((i+1)*30,j*30+15);
context.stroke();
}
首先通过clearRect()擦掉该圆,然后再重新画该圆周围的格子,注意相应的位置,这里花了些时间折腾。
2.3 撤销悔棋功能
悔棋过后,再撤销,相当于还原悔棋之前的状态。代码比较简单:
// 撤销悔棋
returnbtn.οnclick=function(e){
if(!returnAble){return;}
// 我,撤销悔棋
chressBord[_nowi][_nowj]=1;//我,已占位置
oneStep(_nowi,_nowj,me);
for(vark=0;k
if(wins[_nowi][_nowj][k]){
myWin[k]++;
_compWin[k]=computerWin[k];
computerWin[k]=6;//这个位置对方不可能赢
}
if(myWin[k]==5){
resultTxt.innerHTML='恭喜,你赢了!';
over=true;
}
}
// 计算机撤销相应的悔棋
chressBord[_compi][_compj]=2;//计算机,已占位置
oneStep(_compi,_compj,false);
for(vark=0;k
if(wins[_compi][_compj][k]){
computerWin[k]++;
_myWin[k]=myWin[k];
myWin[k]=6;//这个位置对方不可能赢
}
if(computerWin[k]==5){
resultTxt.innerHTML='o(╯□╰)o,计算机赢了,继续加油哦!';
over=true;
}
}
returnbtn.className+='unable';
returnAble=false;
backAble=true;
}
至此,比较简单的完成了这三个功能。
三、总结
五子棋游戏的核心关键点是:
1、弄清楚有多少种赢法;
2、怎么判断是否已经赢了;
3、计算机下棋算法。这里巧妙地运用数组存储赢法,判断是否赢了,通过权重比较,计算出计算机该下棋的位置。 过程中用到canvas,之前有学习过,虽然很久没用,查了些资料,复习了怎么画线,画圆,学会了怎么如何清除一个圆等。 然后要注意的是,用原生Js怎么为元素添加、删除class。
作者:eraser123
源自:https://segmentfault.com/a/1190000009826648
声明:文章著作权归作者所有,如有侵权,请联系小编删除。返回搜狐,查看更多
责任编辑: