原生JS+Canvas实现五子棋游戏实例


Posted in Javascript onJune 19, 2017

一、功能模块

先看下现在做完的效果:

原生JS+Canvas实现五子棋游戏实例

线上体验:https://wj704.github.io/five_game.html

主要功能模块为:

1.人机对战功能
2.悔棋功能
3.撤销悔棋功能

二、代码详解

2.1 人机对战功能实现

从效果图可以看到,棋盘的横竖可以放的位置为15*15,通过canvas画棋盘:

//绘画棋盘
    var drawChessBoard = function(){
      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();
      }
    }

知道格子数后,我们先看五子棋有多少种赢法:

//赢法数组
      var wins = [];
      for(var i = 0; i < 15; i++){
        wins[i] = [];
        for(var j = 0; j < 15; j++){
          wins[i][j] = [];
        }
      }
      var count = 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][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++;
        }
      }

根据赢法总数定义分别保存计算机和人赢法的数组:

for(var i = 0; i < count; i++){
        myWin[i] = 0;
        _myWin[i] = 0;
        computerWin[i] = 0;
        _compWin[i] = 0;
   }

然后就是人开始下棋:

 

// 我,下棋
  chess.onclick = function(e){
    if(over){ // 游戏结束
      return;
    }
    if(!me){
      return;
    }
    var x = e.offsetX;
    var y = e.offsetY;
    var i = Math.floor(x / 30);
    var j = Math.floor(y / 30);
    _nowi = i;
    _nowj = j;
    if(chressBord[i][j] == 0){
      oneStep(i,j,me);
      chressBord[i][j] = 1; //我,已占位置    

      for(var k = 0; k < count; k++){ // 将可能赢的情况都加1
        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( new RegExp( "(\\s|^)unable(\\s|$)" )," " );     
  }

oneStep() 方法为落子,要在棋盘上画一个棋子:

//画棋子
    var oneStep = function(i,j,me){
      // debugger;
      context.beginPath();
      context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);//画圆
      context.closePath();
      //渐变
      var gradient = 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()方法:

// 计算机下棋
    var computerAI = function (){
      var myScore = [];
      var computerScore = [];
      var max = 0;
      var u = 0, v = 0;
      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++){
        for(var j = 0; j < 15; j++){
          if(chressBord[i][j] == 0){
            for(var k = 0; k < count; k++){
              if(wins[i][j][k]){
                if(myWin[k] == 1){
                  myScore[i][j] += 200;
                }else if(myWin[k] == 2){
                  myScore[i][j] += 400;
                }else if(myWin[k] == 3){
                  myScore[i][j] += 2000;
                }else if(myWin[k] == 4){
                  myScore[i][j] += 10000;
                }

                if(computerWin[k] == 1){
                  computerScore[i][j] += 220;
                }else if(computerWin[k] == 2){
                  computerScore[i][j] += 420;
                }else if(computerWin[k] == 3){
                  computerScore[i][j] += 2100;
                }else if(computerWin[k] == 4){
                  computerScore[i][j] += 20000;
                }            
              }
            }

            if(myScore[i][j] > max){
              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){
              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;  
              }
            }

          }
        }
      }
      _compi = u;
      _compj = v;
      oneStep(u,v,false);
      chressBord[u][v] = 2; //计算机占据位置
      for(var k = 0; k < count; 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.onclick = function(e){
        if(!backAble) { return;}
        over = false;
        me = true;

        // 我,悔棋
        chressBord[_nowi][_nowj] = 0; //我,已占位置 还原
        minusStep(_nowi, _nowj); //销毁棋子                 
        for(var k = 0; k < count; k++){ // 将可能赢的情况都减1
          if(wins[_nowi][_nowj][k]){
            myWin[k]--;
            computerWin[k] = _compWin[k];//这个位置对方可能赢
          }
        }

        // 计算机相应的悔棋
        chressBord[_compi][_compj] = 0; //计算机,已占位置 还原
        minusStep(_compi, _compj); //销毁棋子                 
        for(var k = 0; k < count; k++){ // 将可能赢的情况都减1
          if(wins[_compi][_compj][k]){
            computerWin[k]--;
            myWin[k] = _myWin[i];//这个位置对方可能赢
          }
        }
        resultTxt.innerHTML = '--益智五子棋--';
        returnAble = true;
        backAble = false;
         // 撤销悔棋功能可用
        returnbtn.className = returnbtn.className.replace( new RegExp( "(\\s|^)unable(\\s|$)" )," " ); 
      }

minusStep()为销毁棋子的方法,我们看下是怎么销毁的。

//销毁棋子
     var minusStep = 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.onclick = function(e){
      if(!returnAble) { return; }
      // 我,撤销悔棋
      chressBord[_nowi][_nowj] = 1; //我,已占位置 
      oneStep(_nowi,_nowj,me);               
      for(var k = 0; k < count; 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(var k = 0; k < count; 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。

最后代码放到github上了,地址:https://github.com/wj704/wj704.github.io/blob/master/five_game.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
两个select之间option的互相添加操作(jquery实现)
Nov 12 Javascript
mysql输出数据赋给js变量报unterminated string literal错误原因
May 22 Javascript
jQuery Dialog 弹出层对话框插件
Aug 09 Javascript
ajax的hide隐藏问题解决方法
Dec 11 Javascript
jquery验证手机号是否正确实例讲解
Nov 17 Javascript
Bootstrap CSS组件之面包屑导航(breadcrumb)
Dec 17 Javascript
理解Angular的providers给Http添加默认headers
Jul 04 Javascript
Javascript 一些需要注意的细节(必看篇)
Jul 08 Javascript
Node.js中DNS模块学习总结
Feb 28 Javascript
JavaScript中的E-mail 地址格式验证
Mar 28 Javascript
详解Vue 单文件组件的三种写法
Feb 19 Javascript
微信小程序实现选择地址省市区三级联动
Jun 21 Javascript
Node.js环境下Koa2添加travis ci持续集成工具的方法
Jun 19 #Javascript
Node.js中 __dirname 的使用介绍
Jun 19 #Javascript
JavaScript 上传文件(psd,压缩包等),图片,视频的实现方法
Jun 19 #Javascript
AngularJS实现自定义指令与控制器数据交互的方法示例
Jun 19 #Javascript
jQuery自定义多选下拉框效果
Jun 19 #jQuery
JS实现自动轮播图效果(自适应屏幕宽度+手机触屏滑动)
Jun 19 #Javascript
JS仿淘宝搜索框用户输入事件的实现
Jun 19 #Javascript
You might like
单台服务器的PHP进程之间实现共享内存的方法
2014/06/13 PHP
php使用post数组的键值创建同名变量并赋值的方法
2015/04/03 PHP
php项目中百度 UEditor 简单安装调试和调用
2015/07/15 PHP
一些常用的Javascript函数
2006/12/22 Javascript
动态加载图片路径 保持JavaScript控件的相对独立性
2010/09/03 Javascript
js猜数字小游戏的简单实现代码
2013/07/02 Javascript
浅析Js中的单引号与双引号问题
2013/11/06 Javascript
jQuery弹出框代码封装DialogHelper
2015/01/30 Javascript
教你如何使用firebug调试功能了解javascript闭包和this
2015/03/04 Javascript
详解Bootstrap四种图片样式
2016/01/04 Javascript
微信小程序 Record API详解及实例代码
2016/09/30 Javascript
使用vue.js实现联动效果的示例代码
2017/01/10 Javascript
vue2.0实战之基础入门(1)
2017/03/27 Javascript
在Vue methods中调用filters里的过滤器实例
2018/08/30 Javascript
JS原生带缩略图的图片切换效果
2018/10/10 Javascript
JavaScript对象字面量和构造函数原理与用法详解
2020/04/18 Javascript
手写Vue2.0 数据劫持的示例
2021/03/04 Vue.js
[01:11:11]Alliance vs RNG 2019国际邀请赛淘汰赛 败者组BO1 8.20.mp4
2020/07/19 DOTA
让python的Cookie.py模块支持冒号做key的方法
2010/12/28 Python
Python素数检测的方法
2015/05/11 Python
Python使用pyautogui模块实现自动化鼠标和键盘操作示例
2018/09/04 Python
详解配置Django的Celery异步之路踩坑
2018/11/25 Python
Python实现京东秒杀功能代码
2019/05/16 Python
Python模块的制作方法实例分析
2019/12/21 Python
Python 字符串池化的前提
2020/07/03 Python
Python 利用Entrez库筛选下载PubMed文献摘要的示例
2020/11/24 Python
css3实现蒙版弹幕功能
2019/06/18 HTML / CSS
美国畅销的跑步机品牌:ProForm
2017/02/06 全球购物
4s店机修工岗位职责
2013/12/20 职场文书
公司任命书范本
2014/06/04 职场文书
白莲教口号
2014/06/18 职场文书
运动会广播稿50字-100字
2014/10/11 职场文书
2014年设计师工作总结
2014/11/25 职场文书
2015年初中生自我评价范文
2015/03/03 职场文书
用Python爬取各大高校并可视化帮弟弟选大学,弟弟直呼牛X
2021/06/11 Python
游戏《东方异文石:爱亚利亚黎明》正式版发布
2022/04/03 其他游戏