原生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 相关文章推荐
Javascript里使用Dom操作Xml
Jan 22 Javascript
javascript单例模式的简单实现方法
Jul 25 Javascript
JS模拟实现方法重载示例
Aug 03 Javascript
Bootstrap 手风琴菜单的实现代码
Jan 20 Javascript
Vue开发中整合axios的文件整理
Apr 29 Javascript
angular ng-click防止重复提交实例
Jun 16 Javascript
用JS编写一个函数,返回数组中重复出现过的元素(实例)
Sep 14 Javascript
解决修复npm安装全局模块权限的问题
May 17 Javascript
详解Angular6 热加载配置方案
Aug 18 Javascript
vue.js 图片上传并预览及图片更换功能的实现代码
Aug 27 Javascript
微信小程序自定义导航栏
Dec 31 Javascript
jQuery实现高度灵活的表单验证功能示例【无UI】
Apr 30 jQuery
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
动态网站web开发 PHP、ASP还是ASP.NET
2006/10/09 PHP
在Mac OS上搭建PHP的Yii框架及相关测试环境
2016/02/14 PHP
PHP中类属性与类静态变量的访问方法示例
2016/07/13 PHP
php 7新特性之类型申明详解
2017/06/06 PHP
PHP获取日期对应星期、一周日期、星期开始与结束日期的方法
2018/06/22 PHP
thinkPHP5框架实现基于ajax的分页功能示例
2018/06/12 PHP
PHP+MySQL实现在线测试答题实例
2020/01/02 PHP
Javascript中 关于prototype属性实现继承的原理图
2013/04/16 Javascript
Jquery实现列表(隔行换色,全选,鼠标滑过当前行)效果实例
2013/06/09 Javascript
基于JQuery实现的Select级联
2014/01/27 Javascript
javascript下使用Promise封装FileReader
2016/02/19 Javascript
简单谈谈JS数组中的indexOf方法
2016/10/13 Javascript
纯JS代码实现隔行变色鼠标移入高亮
2016/11/23 Javascript
Bootstrap基本组件学习笔记之下拉菜单(7)
2016/12/07 Javascript
JS实现DIV高度自适应窗口示例
2017/02/16 Javascript
layui 优化button按钮和弹出框的方法
2018/08/15 Javascript
详解JavaScript中的坐标和距离
2019/05/27 Javascript
vue实现图片预览组件封装与使用
2019/07/13 Javascript
vue视频播放插件vue-video-player的具体使用方法
2019/11/08 Javascript
用python制作游戏外挂
2018/01/04 Python
python实现获取单向链表倒数第k个结点的值示例
2019/10/24 Python
centos+nginx+uwsgi+Django实现IP+port访问服务器
2019/11/15 Python
python实点云分割k-means(sklearn)详解
2020/05/28 Python
美国小蜜蜂Burt’s Bees德国官网:天然唇部、皮肤和身体护理产品
2020/06/14 全球购物
优秀大学生推荐信范文
2013/11/28 职场文书
汽车维修工岗位职责
2014/02/12 职场文书
公司授权委托书
2014/04/04 职场文书
社区党的群众路线教育实践活动总结材料
2014/10/31 职场文书
五四青年节活动总结
2015/02/10 职场文书
《1942》观后感
2015/06/08 职场文书
辣妈辣妹观后感
2015/06/10 职场文书
《黄道婆》教学反思
2016/02/22 职场文书
离婚协议书范本(2016最新版)
2016/03/18 职场文书
golang中的空接口使用详解
2021/03/30 Python
WordPress多语言翻译插件 - WPML使用教程
2021/04/01 PHP
python疲劳驾驶困倦低头检测功能的实现
2022/04/04 Python