JavaScript实现五子棋小功能整理
整个小游戏主要分成界面部分与赢法统计两方面。
源码地址:https://github.com/sunshineqt/test/tree/master/five-in-line
在线预览:https://sunshineqt.github.io/test/five-in-line/index.html
效果图:

先讲界面部分,主要知识点分为两点:
1 棋盘的画法
canvas绘制直线、设置画笔颜色
2-棋子的画法
canvas画圆、填充渐变色
以上两点可细分为五步:
(1) 获取canvas对象
var chess=document.getElementById("chess"); //1-获取canvas对象
var context=chess.getContext('2d');
(2) for循环绘制棋盘
context.strokeStyle="#b0b0b0";//设置划线的颜色
var drawChessBoard=function(){ //2-绘制棋盘
for(var i=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();
}
}
这里使用封装函数,将绘制棋子方法封装起来,方便调用,如水印图片设置之后再调用,防止水印图片遮挡住了棋盘。
(3) 棋盘上的水印图片
var logo=new Image(); //3-棋盘上的水印图片
logo.src="image/logo.png";
logo.onload=function(){
context.drawImage(logo,0,0,450,450); //水印图片设置
drawChessBoard();//后调用绘制棋盘函数,防止水印图片遮挡住棋盘
}
(4) 棋子的绘制
var oneStep=function(i,j,me){ //4-绘制棋子
context.beginPath();
context.arc(15+i*30,15+j*30,13,0,2*Math.PI); //通过arc弧度函数画圆
context.closePath();
var gradient=context.createRadialGradient(15+i*30,15+j*30,13,15+i*30,15+j*30,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();
}
ps:context.stroke()用来实现描边,而context.fill()用来实现填充。
(5) 鼠标点击,落子实现
var me=true;//控制轮流下棋,首先黑子先下
var chessBoard=[]; //定义一个二维数组,用来存储棋盘上位置的信息,初始化为0表示棋盘上无子
for(var i=0;i<15;i++){
chessBoard[i]=[];
for(var j=0;j<15;j++){
chessBoard[i][j]=0;
}
}
chess.onclick=function(e){ //5--鼠标点击落子情况
var x= e.offsetX;
var y= e.offsetY;
var i=Math.floor(x / 30);//棋盘上的位置索引
var j=Math.floor(y / 30);
if(chessBoard[i][j]==0){ //当存储为0,即棋盘上无子情况才能落子
oneStep(i,j,me);//黑子先下
if(me){ //如果下的是黑子,在二维数组中存储为1
chessBoard[i][j]=1;
}else{ //如果下的是白子,在二维数组中存储为2
chessBoard[i][j]=2;
}
me=!me;
}
}
再讲我方及计算机方落子部分:
判断计算机落子,可先遍历棋盘上哪些交叉点可以落子,基于某种规则给交叉点计算得分,则得分最高的交叉点即为计算机要落子的地方
- 赢法数组:记录五子棋所有的赢法,用三维数组表示,前面两维表示棋盘
- 每一种赢法的统计数组,用一维数组表示
- 如何判断胜负,根据赢法的统计数组,如果说某一种赢法已经达到了五颗棋子,则这种赢法相当于被实现了,即某一方已经胜利了
- 计算机的落子规则,根据赢法的统计数组进行加分,若棋盘上一条线上已有同种颜色的棋子越多,则再落子的价值越大,则对其加一个更高的分数。根据分数最高的位置进行落子。
具体步骤:
(6) 定义赢法数组,表示为三维数组,保存了五子棋的所有赢法
var wins=[]; //6-定义赢法数组,为三维数组,保存了五子棋所有的赢法
for(var i=0;i<15;i++){
wins[i]=[];
for(var j=0;j<15;j++){
wins[i][j]=[];
}
}
(7) 定义赢法种类,用变量count表示,初始化为0
var count=0;//7-定义赢法种类,初始化为0
(8) 填充赢法数组
//8-填充赢法数组
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][count]=true;
}
count++;
}
}
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][count]=true;
}
count++;
}
}
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][count]=true;
}
count++;
}
}
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][count]=true;
}
count++;
}
}
console.log(count);//看有多少种赢法
(9) 赢法的统计数组
//9-赢法的统计数组
var myWin=[];//9-我方赢的数组
var computerWin=[];//9-计算机方赢的数组
for(var i=0;i<count;i++){ //9-赢法的初始化,这里需要注意,赢法的初始化用到//count变量,因此需要在count变量计算完毕之后进行赢法的初始化
myWin[i]=0;
computerWin[i]=0;
}
if(over){ //9--判断游戏是否结束,置于onclick函数开头处
return;
}
for(var k=0;k<count;k++){ //9-我方赢法统计数组,置于onclick函数的黑子落子后
if(wins[i][j][k]){
myWin[k]++;
computerWin[k]=6;//此时,计算机在第k种赢法上不可能赢了
if(myWin[k] == 5){
window.alert("你赢了");
over=true;
}
}
}
ps:如果存在一个k使得myWin[k]==5,则第k种赢法已经实现了。
(10) 实现计算机落子
在onclick函数开始部分,设置为该onclick函数只对我方落子时有效
if(!me){ //10-设置onclick函数只对我方有效
return ;
}
这种情况下,当我方落子时,可直接将棋盘位置存储为1:
chessBoard[i][j]=1;
//10-前面已经设置该click函数只对me即黑子有效,所以可以不用判断,直接存为1
在onclick函数结尾处,在前面赢法统计数组更新完毕后,判断over,若游戏没有结束的话,将下棋的权利交给计算机,同时调用计算机落子函数
if(!over){ //10-判断是否结束,否的话,调用计算机落子函数
me=!me;//10-如果没结束就把下棋的权利交给计算机
computer();
}
var computer=function(){ //10-计算机落子实现
var myScore=[];
var computerScore=[];
for(var i=0;i<15;i++){
myScore[i]=[];
computerScore[i]=[];
for(var j=0;j<15;j++){
myScore[i][j]=0;
computerScore[i][j]=0;
}
}
for(var i=0;i<15;i++){ //10-统计我方和计算机可能赢的分数
for(var j=0;j<15;j++){
if(chessBoard[i][j]==0){
for(var k=0;k<count;k++){ //遍历了所有赢法
if(wins[i][j][k]){
if(myWin[k]==1){
myScore[i][j]+=100;
}else if(myWin[k]==2){
myScore[i][j]+=200;
}else if(myWin[k]==3){
myScore[i][j]+=500;
}else if(myWin[k]==4){
myScore[i][j]+=800;
}
if(computerWin[k]==1){
computerScore[i][j]+=120;
}else if(computerWin[k]==2){
computerScore[i][j]+=220;
}else if(computerWin[k]==3){
computerScore[i][j]+=560;
}else if(computerWin[k]==4){
computerScore[i][j]+=860;
}
}
}
}
}
}
ps: 遍历整个棋盘,若棋盘上点为空,则可落子,对其分数进行计算。假设第k种赢法在[i,j]处为true,则在此处落子是有价值的,对其进行加分。积分的计算则需要用到赢法的统计数组。若myWin[k]=1,则第k种赢法已经存在一个棋子,这时在该处落子是有价值的,这里通过加分多少来体现落子价值高低。若有一个棋子存在再落子,则加100,若有2个棋子存在再落子,则加200,若有3个棋子存在再落子,则加500,若有4个棋子存在再落子,则加800,
(11) 找出myScore、computerScore分数最高的点
定义max保存最高分数,u,v保存最高分数的点的坐标,在k循环完后做寻找最高分数及对应坐标这件事。U,v即为计算机要落子的点,然后调用oneStep(u,v,false)
在computer()函数部分实现
var max=0;//11-用来保存最高分数
var u= 0,v=0;//11-用来保存最高分数的点坐标
if(myScore[i][j] > max){ //11-记录最高分及坐标
max=myScore[i][j];
u=i;
v=j;
}else if(myScore[i][j]==max){
if(computerScore[i][j] > computerScore[u][v]){
u=i;
v=j;
}
}
if(computerScore[i][j] > max){ //11-记录最高分及坐标
max=computerScore[i][j];
u=i;
v=j;
}else if(computerScore[i][j]==max){
if(myScore[i][j] > myScore[u][v]){
u=i;
v=j;
}
}
oneStep(u,v,false);
chessBoard[u][v]=2;//11-表示计算机在u,v处落子
(12) 计算机落子后,更新赢法的统计数组
其逻辑类似于我方的赢法统计数组,在computer()函数最后实现
for(var k=0;k<count;k++){ //12-计算机赢法统计数组
if(wins[u][v][k]){
computerWin[k]++;
myWin[k]=6;
if(computerWin[k] == 5){
window.alert("计算机赢了");
over=true;
}
}
}
if(!over){ //12-判断是否结束,否的话,调用计算机落子函数
me=!me;//12-如果没结束就把下棋的权利交给我方
}
效果图:

js插曲:
JavaScript数据类型大致可分为三种:基本数据类型、复合数据类型、特殊数据类型
基本数据类型:数值型(整型、实型)、布尔型、字符串型
复合数据类型:数组、对象
特殊数据类型:null、undefined
parseInt()、parseFloat()系统函数用于数据类型转换
typeof 用于判断表达式的数据类型
instance of判断一个变量是否是某个对象(类)的实例,返回值是布尔型的
prompt()函数用于显示提示用户进行输入的对话框
writeln()方法直接在浏览器中输出内容

浙公网安备 33010602011771号