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

    宝剑锋从磨砺出,梅花香自苦寒来。
  • 相关阅读:
    编译安装php5 解决编译安装的php加载不了gd
    apache-php
    使用ab对网站进行压力测试
    正则表达式实例 -- 匹配Windows消息宏
    SDK 操作 list-view control 实例 -- 遍历进程
    malloc/free、new/delete 区别
    Python实例 -- 爬虫
    multimap实例 -- 添加、遍历数据
    CListCtrl 扩展风格设置方法---SetExtendedStyle和ModifyStyleEx
    创建指定大小的空文件
  • 原文地址:https://www.cnblogs.com/haimengqingyuan/p/5423877.html
Copyright © 2020-2023  润新知