欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS+canvas五子棋人機(jī)對(duì)戰(zhàn)實(shí)現(xiàn)步驟詳解

 更新時(shí)間:2020年06月04日 16:02:19   作者:javascript癡癡  
這篇文章主要介紹了JS+canvas五子棋人機(jī)對(duì)戰(zhàn)實(shí)現(xiàn)步驟詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

1. 創(chuàng)建實(shí)例

function Gobang () {
  this.over = false; // 是否結(jié)束
  this.player = true; // true:我 false:電腦
  this.allChesses = []; // 所有棋子
  this.existChesses = [] // 已經(jīng)落下的棋子
  this.winsCount = 0; // 贏法總數(shù)
  this.wins = []; // 所有贏法統(tǒng)計(jì)
  this.myWins = []; //我的贏法統(tǒng)計(jì)
  this.computerWins = []; //電腦贏法統(tǒng)計(jì)
}

2. 初始化

//初始化
Gobang.prototype.init = function(opts) {
  // 生成canvas棋盤
  this.createCanvas(opts);

  //棋盤初始化 
  this.boardInit();

  // 鼠標(biāo)移動(dòng)聚焦功能實(shí)現(xiàn)
  this.mouseMove();

  //算法初始化
  this.algorithmInit();

  //落子功能實(shí)現(xiàn)
  this.dorpChess();
}

3. 生成canvas棋盤

//初始化
//生成canvas
Gobang.prototype.createCanvas = function(opts) {
  var opts = opts || {};
  if (opts.width && opts.width%30 !== 0) throw new RangeError(opts.width+'不是30的倍數(shù)');
  this.col = (opts.width && opts.width/30) || 15; // 棋盤列

  var oCanvas = document.createElement('canvas');
  oCanvas.width = oCanvas.height = opts.width || 450;
  this.canvas = oCanvas;
  document.querySelector(opts.container || 'body').appendChild(this.canvas);
  this.ctx = oCanvas.getContext('2d');
}

4. 初始化棋盤

//棋盤初始化
Gobang.prototype.boardInit = function(opts){
  this.drawBoard();
}

// 畫棋盤
Gobang.prototype.drawBoard = function(){
  this.ctx.strokeStyle = "#bfbfbf";
  for (var i = 0; i < this.col; i++) {
    this.ctx.moveTo(15+ 30*i, 15);
    this.ctx.lineTo(15+ 30*i, this.col*30-15);
    this.ctx.stroke();
    this.ctx.moveTo(15, 15+ 30*i);
    this.ctx.lineTo(this.col*30-15, 15+ 30*i);
    this.ctx.stroke();
  }
}

5. 畫棋子

// 畫棋子
Gobang.prototype.drawChess = function(x, y, player){
  var x = 15 + x * 30,
    y = 15 + y * 30;
  this.ctx.beginPath();
  this.ctx.arc(x, y, 13, 0, Math.PI*2);
  var grd = this.ctx.createRadialGradient(x + 2, y - 2, 13 , x + 2, y - 2, 0);
  if (player) { //我 == 黑棋 
    grd.addColorStop(0, '#0a0a0a');
    grd.addColorStop(1, '#636766');
  }else{ //電腦 == 白棋
    grd.addColorStop(0, '#d1d1d1');
    grd.addColorStop(1, '#f9f9f9');
  }
  this.ctx.fillStyle = grd;
  this.ctx.fill()
}

6. 移動(dòng)聚焦

// 鼠標(biāo)移動(dòng)時(shí)觸發(fā)聚焦效果, 需要前面的聚焦效果消失, 所有需要重繪canvas
Gobang.prototype.mouseMove = function(){
  var that = this;
  this.canvas.addEventListener('mousemove', function (e) {
    that.ctx.clearRect(0, 0, that.col*30, that.col*30);
    var x = Math.floor((e.offsetX)/30),
      y = Math.floor((e.offsetY)/30);

    //重繪棋盤
    that.drawBoard();

    //移動(dòng)聚焦效果
    that.focusChess(x, y);

    //重繪已經(jīng)下好的棋子
    that.redrawedChess()
  });
}

//鼠標(biāo)移動(dòng)聚焦
Gobang.prototype.focusChess = function(x, y){
  this.ctx.beginPath();
  this.ctx.fillStyle = '#E74343';
  this.ctx.arc(15 + x * 30, 15 + y * 30, 6, 0, Math.PI*2);
  this.ctx.fill();
}

//重繪當(dāng)前下好的棋子
Gobang.prototype.redrawedChess = function(x, y){
  for (var i = 0; i < this.existChesses.length; i++) {
    this.drawChess(this.existChesses[i].x, this.existChesses[i].y, this.existChesses[i].player);
  }
}

7. 算法初始化

//算法初始化
Gobang.prototype.algorithmInit = function(){
  //初始化棋盤的每個(gè)位置和贏法
  for (var x = 0; x < this.col; x++) {
    this.allChesses[x] = [];
    this.wins[x] = [];
    for (var y = 0; y < this.col; y++) {
      this.allChesses[x][y] = false;
      this.wins[x][y] = [];
    }
  }
  //獲取所有贏法
  this.computedWins();

  // 初始化電腦和我每個(gè)贏法當(dāng)前擁有的棋子數(shù)
  for (var i = 0; i < this.winsCount; i++) {
    this.myWins[i] = 0;
    this.computerWins[i] = 0;
  }
}

8. 獲取所有贏法

Gobang.prototype.computedWins = function(){
  /*
    直線贏法
    以15列為準(zhǔn)
  */
  for (var x = 0; x < this.col; x++) { //縱向所有贏法
    for (var y = 0; y < this.col-4; y ++) {
      this.winsCount ++; 

      /*
        如: 
        1.組成的第一種贏法
          [0,0]
          [0,1]
          [0,2]
          [0,3]
          [0,4]
        
        2.組成的第二種贏法
          [0,1]
          [0,2]
          [0,3]
          [0,4]
          [0,5]
        以此類推一列最多也就11種贏法, 所有縱向x有15列 每列最多11種, 所有縱向總共15 * 11種
      */
      //以下for循環(huán)給每種贏法的位置信息儲(chǔ)存起來
      for (var k = 0; k < 5; k ++) {
        this.wins[x][y+k][this.winsCount] = true;
        /*
          位置信息
          第一種贏法的時(shí)候:
            this.wins = [
                    [
                      [1:true],
                      [1:true],
                      [1:true],
                      [1:true],
                      [1:true]
                    ],
                    [
                      ......
                    ]
                  ]

            雖然這是一個(gè)三維數(shù)組, 我們把它拆分下就好理解了
            相當(dāng)于 this.wins[0][0][1], this.wins[0][4][1], this.wins[0][5][1], this.wins[0][6][1], this.wins[0][7][1]
            
            因?yàn)閷?duì)象可以這樣取值:
              var obj = {
                a: 10,
                b: 'demo'
              }
              obj['a'] === obj.a

            所有也就相當(dāng)于 this.wins[0][0].1, this.wins[0][8].1, this.wins[0][9].1, this.wins[0][10].1, this.wins[0][11].1 

            雖然數(shù)組不能這么取值,可以這么理解

            所以   this.wins[0][0].1 就可以理解為 在 x=0, y=0, 上有第一種贏法
                this.wins[0][12].1 就可以理解為 在 x=0, y=1, 上有第一種贏法
                ......

            以上this.wins[0][0],this.wins[0][13]...可以看作是 this.wins[x][y]
            所以第一種贏法的坐標(biāo)就是: [0,0] [0,1] [0,2] [0,3] [0,4] 
        */
      }
    }
  }

  for (var y = 0; y < this.col; y++) { //橫向所有贏法, 同縱向贏法一樣,也是15 * 11種
    for (var x = 0; x < this.col-4; x ++) {
      this.winsCount ++;
      for (var k = 0; k < 5; k ++) {
        this.wins[x+k][y][this.winsCount] = true;
      }
    }
  }

交叉贏法

  /*
    交叉贏法
  */
  for (var x = 0; x < this.col-4; x++) { // 左 -> 右 開始的所有交叉贏法 總共11 * 11種
    for (var y = 0; y < this.col-4; y ++) {
      this.winsCount ++;

      /*
      如:
      1. [0,0]
        [1,1]
        [2,2]
        [3,3]
        [4,4]
      
      2. [0,1]
        [1,2]
        [2,3]
        [3,4]
        [4,5]

      3. [0,2]
        [1,3]
        [2,4]
        [3,5]
        [4,6]
      ...

        [1,0]
        [2,1]
        [3,2]
        [4,3]
        [5,5]

      相當(dāng)于從左至右 一列列計(jì)算過去

      */

      for (var k = 0; k < 5; k ++) {
        this.wins[x+k][y+k][this.winsCount] = true;
      }
    }
  }

  for (var x = this.col-1; x >= 4; x --) { //右 -> 左 開始的所有交叉贏法 總共11 * 11種
    for (var y = 0; y < this.col-4; y ++) {
      this.winsCount ++;
      for (var k = 0; k < 5; k ++) {
        this.wins[x-k][y+k][this.winsCount] = true;
      }
    }
  }
}

9. 落子實(shí)現(xiàn)

//落子實(shí)現(xiàn)
Gobang.prototype.dorpChess = function(){
  var that = this;
  this.canvas.addEventListener('click', function(e) {
    // 判斷是否結(jié)束
    if (that.over) return;

    var x = Math.floor((e.offsetX)/30),
      y = Math.floor((e.offsetY)/30);

    //判斷該棋子是否已存在
    if (that.allChesses[x][y]) return;

    // 檢查落子情況
    that.checkChess(x, y)

    if (!that.over) {
      that.player = false;
      that.computerDropChess();//計(jì)算機(jī)落子
    }
  })
}


//檢查落子情況
Gobang.prototype.checkChess = function(x, y){
  //畫棋
  this.drawChess(x, y, this.player);
  //記錄落下的棋子
  this.existChesses.push({
    x: x,
    y: y,
    player: this.player
  });
  //該位置棋子置為true,證明已經(jīng)存在
  this.allChesses[x][y] = true;

  this.currWinChesses(x, y, this.player);
}

//判斷當(dāng)前坐標(biāo)贏的方法各自擁有幾粒棋子
Gobang.prototype.currWinChesses = function(x, y, player){
  var currObj = player ? this.myWins : this.computerWins;
  var enemyObj = player ? this.computerWins : this.myWins;
  var currText = player ? '我' : '電腦';
  for (var i = 1; i <= this.winsCount; i++) {
    if (this.wins[x][y][i]) { //因?yàn)橼A法統(tǒng)計(jì)是從1開始的 所以對(duì)應(yīng)我的贏法需要減1
      currObj[i-1] ++; // 每個(gè)經(jīng)過這個(gè)點(diǎn)的贏法都增加一個(gè)棋子;

      enemyObj[i-1] = 6; //這里我下好棋了,證明電腦不可能在這種贏法上取得勝利了, 置為6就永遠(yuǎn)不會(huì)到5

      if (currObj[i-1] === 5) { //當(dāng)達(dá)到 5 的時(shí)候,證明我勝利了
        alert(currText+'贏了')
        this.over = true;
      }
    }
  }
}

10. 計(jì)算機(jī)落子實(shí)現(xiàn)

// 計(jì)算機(jī)落子
Gobang.prototype.computerDropChess = function(){
  var myScore = [], //玩家比分
    computerScore = [], // 電腦比分
    maxScore = 0; //最大比分
  

  //比分初始化
  var scoreInit = function(){
    for( var x = 0; x < this.col; x ++) { 
      myScore[x] = [];
      computerScore[x] = [];
      for (var y = 0; y < this.col; y ++) {
        myScore[x][y] = 0;
        computerScore[x][y] = 0;
      }
    }
  }
  scoreInit.call(this);

  //電腦待會(huì)落子的坐標(biāo)
  var x = 0, y = 0; 

  // 基于我和電腦的每種贏法擁有的棋子來返回對(duì)應(yīng)的分?jǐn)?shù)
  function formatScore(o, n) { 
    if (o < 6 && o > 0) {
      var n = 10;
      for (var i = 0; i < o; i++) {
        n *= 3;
      }
      return n
    }
    return 0
  }

  // 獲取沒有落子的棋盤區(qū)域
  function existChess(arr) { 
    var existArr = [];
    for (var i = 0; i < arr.length; i++) {
      for (var j = 0; j < arr[i].length; j++) {
        if (!arr[i][j]) {
          existArr.push({x:i, y:j})
        }
      }
    }
    return existArr;
  }

  var exceptArr = existChess(this.allChesses);

  // 循環(huán)未落子區(qū)域,找出分?jǐn)?shù)最大的位置
  for (var i = 0; i < exceptArr.length; i++) { 
    var o = exceptArr[i];

    // 循環(huán)所有贏的方法
    for (var k = 0; k < this.winsCount; k++) {

      //判斷每個(gè)坐標(biāo)對(duì)應(yīng)的贏法是否存在
      if (this.wins[o.x][o.y][k]) {

        // 計(jì)算每種贏法,擁有多少棋子,獲取對(duì)應(yīng)分?jǐn)?shù)
        // 電腦起始分?jǐn)?shù)需要高一些,因?yàn)楝F(xiàn)在是電腦落子, 優(yōu)先權(quán)大
        myScore[o.x][o.y] += formatScore(this.myWins[k-1], 10);
        computerScore[o.x][o.y] += formatScore(this.computerWins[k-1], 11); 
      }
    }

    //我的分?jǐn)?shù)判斷
    if (myScore[o.x][o.y] > maxScore) { //當(dāng)我的分?jǐn)?shù)大于最大分?jǐn)?shù)時(shí), 證明這個(gè)位置的是對(duì)我最有利的
      maxScore = myScore[o.x][o.y];
      x = o.x;
      y = o.y;
    }else if (myScore[o.x][o.y] === maxScore) { //當(dāng)我的分?jǐn)?shù)與最大分?jǐn)?shù)一樣時(shí), 證明我在這兩個(gè)位置下的效果一樣, 所以我們應(yīng)該去判斷在這兩個(gè)位置時(shí),電腦方對(duì)應(yīng)的分?jǐn)?shù)
      if (computerScore[o.x][o.y] > computerScore[x][y]) {
        x = o.x;
        y = o.y;
      }
    }

    // 電腦分?jǐn)?shù)判斷, 因?yàn)槭请娔X落子, 所以優(yōu)先權(quán)大
    if (computerScore[o.x][o.y] > maxScore) {
      maxScore = computerScore[o.x][o.y];
      x = o.x;
      y = o.y;
    }else if (computerScore[o.x][o.y] === maxScore) {
      if (myScore[o.x][o.y] > myScore[x][y]) {
        x = o.x;
        y = o.y;
      }
    }
  }

  this.checkChess(x, y)

  if (!this.over) {
    this.player = true;
  }
}
var gobang = new Gobang();
gobang.init()

github地址

線上地址

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Js+Ajax,Get和Post在使用上的區(qū)別小結(jié)

    Js+Ajax,Get和Post在使用上的區(qū)別小結(jié)

    下面小編就為大家?guī)硪黄狫s+Ajax,Get和Post在使用上的區(qū)別小結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06
  • 淺談JavaScript函數(shù)參數(shù)的可修改性問題

    淺談JavaScript函數(shù)參數(shù)的可修改性問題

    這篇文章主要是對(duì)JavaScript函數(shù)參數(shù)的可修改性進(jìn)行了詳細(xì)的介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助
    2013-12-12
  • JavaScript函數(shù)式編程示例分析

    JavaScript函數(shù)式編程示例分析

    函數(shù)式編程是一種編程范式,將整個(gè)程序都由函數(shù)調(diào)用以及函數(shù)組合構(gòu)成。 可以看成一條流水線,數(shù)據(jù)可以不斷地從一個(gè)函數(shù)的輸出流入另一個(gè)函數(shù)的輸入,最后輸出結(jié)果
    2022-10-10
  • 輕量級(jí)JS Cookie插件js-cookie的使用方法

    輕量級(jí)JS Cookie插件js-cookie的使用方法

    js-cookie插件是一個(gè)JS操作cookie的插件,源文件只有3.34 KB,非常輕量級(jí),js-cookie也支持npm和Bower安裝和管理,下面看看js-cookie的具體用法
    2018-03-03
  • javascript實(shí)現(xiàn)文字無縫滾動(dòng)

    javascript實(shí)現(xiàn)文字無縫滾動(dòng)

    這篇文章主要介紹了javascript實(shí)現(xiàn)文字無縫滾動(dòng),文字可以實(shí)現(xiàn)上下滾動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Bootstrap Paginator+PageHelper實(shí)現(xiàn)分頁效果

    Bootstrap Paginator+PageHelper實(shí)現(xiàn)分頁效果

    這篇文章主要為大家詳細(xì)介紹了Bootstrap Paginator+PageHelper實(shí)現(xiàn)分頁效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • ES6中class類用法實(shí)例淺析

    ES6中class類用法實(shí)例淺析

    這篇文章主要介紹了ES6中class類用法,結(jié)合實(shí)例形式分析了ES6中類的實(shí)現(xiàn)方法與相關(guān)語法使用技巧,需要的朋友可以參考下
    2017-04-04
  • 在window.setTimeout方法中傳送對(duì)象

    在window.setTimeout方法中傳送對(duì)象

    setTimeout方法是js中的延時(shí)方法,很多js的bug,只需要使用該方法延時(shí)一下,就會(huì)自動(dòng)解決了,簡直就是萬能藥方,也是我比較喜歡使用的最后手段。
    2006-12-12
  • JS原型對(duì)象操作實(shí)例分析

    JS原型對(duì)象操作實(shí)例分析

    這篇文章主要介紹了JS原型對(duì)象操作,結(jié)合實(shí)例形式分析了JS原型對(duì)象基本原理、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-06-06
  • js 禁用只讀文本框獲得焦點(diǎn)時(shí)的退格鍵

    js 禁用只讀文本框獲得焦點(diǎn)時(shí)的退格鍵

    發(fā)現(xiàn)只讀文本框有一個(gè)缺陷,當(dāng)鼠標(biāo)焦點(diǎn)在文本框里面的時(shí)候按回退鍵(backSpace), 會(huì)退回到前一個(gè)頁面
    2010-04-04

最新評(píng)論