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()方法直接在浏览器中输出内容

posted @ 2016-04-23 09:23  安静的嘶吼  阅读(590)  评论(0编辑  收藏  举报