美文网首页app开发
JS实现的围棋吃子算法

JS实现的围棋吃子算法

作者: 丑角的晨歌 | 来源:发表于2018-06-01 21:26 被阅读393次

    由于前段时间自己开发一个围棋相关的微信小程序,需要实现围棋吃子的功能。网上找了一圈没有找到满足需求的实现,于是决定自己动手,丰衣足食。
    首先先分析一下具体规则,拆解一下需求:
    (1)与棋子直接紧邻的空点称之为“气”,直接紧邻的同色棋子视为一个整体,它们的“气”应一起计算;
    (2)没有气的棋子则为死子,应从棋盘上提掉;
    (3)当下棋造成双方棋子都没有气时,只有对方棋子被吃掉;
    (4)当下棋只造成自己棋子没气,这是不合法的(不允许自杀);

    开始写代码:

    先用一个19*19的二维数组表示棋盘,-1代表空交叉点,0代表白子,1代表黑子,这就不谈了;
    当然这里19可以定义成常量,这样就方便实现9路、13路小棋盘等;

    数气:

    DFS,用一个与棋盘一样大的数组记录是否遍历过,依次遍历4个方向;

    countLiberty: function (x, y, checked) {
      let q = 0;    // 气
      let checked_pos = checked || this.getEmptyStones();
      let color = stones[x][y];
    
      if (checked_pos[x][y] != -1) return 0;    // 已经遍历过
      checked_pos[x][y] = 1;
    
      const left_x = x - 1;
      const right_x = x + 1;
      const up_y = y - 1;
      const down_y = y + 1;
    
      if (left_x >= 0) {
        if (stones[left_x][y] == -1) { // 空位
          ++q;
        } else if (stones[left_x][y] == color) { // 己方棋子
          q += this.countLiberty(left_x, y, checked_pos);
        }
      }
    
      ......
    
    获取一片棋子:

    当确认了死子之后,需要将相连的棋子一起从棋盘中提掉;
    将所有死子的信息放入deads数组,反馈给UI处理;同样是DFS:

    removeBlock: function (x, y, deads, checked) {
      let checked_pos = checked != undefined ? checked : this.getEmptyStones();
      const color = stones[x][y];
      const left_x = x - 1;
      const right_x = x + 1;
      const up_y = y - 1;
      const down_y = y + 1;
      if (checked_pos[x][y] != -1) return;
    
      if (stones[x][y] != -1) {
        //console.log('remove block push x:', x, ' y:', y);
        deads.push({ "x": x, "y": y, "color":color });
        stones[x][y] = -1;
      }
      checked_pos[x][y] = 1;
    
      if (left_x >= 0 && stones[left_x][y] == color) {
        this.removeBlock(left_x, y, deads, checked_pos);
      }
      if (right_x <= LINE_COUNT - 1 && stones[right_x][y] == color) {
        this.removeBlock(right_x, y, deads, checked_pos);
      }
      if (up_y >= 0 && stones[x][up_y] == color) {
        this.removeBlock(x, up_y, deads, checked_pos);
      }
      if (down_y <= LINE_COUNT - 1 && stones[x][down_y] == color) {
        this.removeBlock(x, down_y, deads, checked_pos);
      }
    },
    
    落子逻辑:

    (1)首先判断是不是空位;
    (2)判断能不能提掉对方棋子;
    (3)判断是不是禁入点(不允许自杀);
    接口这里设计的是返回吃掉的对方棋子数组,如果落子不符合规则则返回false

    addStone: function (x, y, color) {
      if (x < 0 || x > LINE_COUNT - 1) return false;
      if (y < 0 || y > LINE_COUNT - 1) return false;
      if (stones[x][y] != -1) return false; // 已经有棋子
      stones[x][y] = color;
    
      // 判断是否使对方棋子没气
      const left_x = x - 1;
      const right_x = x + 1;
      const up_y = y - 1;
      const down_y = y + 1;
    
      let left_dead = false, right_dead = false, up_dead = false, down_dead = false;
      if (left_x >= 0 && stones[left_x][y] != -1 && stones[left_x][y] != color) {
        //console.log('left liberty ', this.countLiberty(left_x, y));
        left_dead = (this.countLiberty(left_x, y) == 0);
      }
      ......
    
      if (!(left_dead || right_dead || up_dead || down_dead)) { // 没有吃掉对方棋子
        // 判断自己是否没气
        //console.log('this liberty ', this.countLiberty(x, y));
        if (this.countLiberty(x, y) == 0) {
          stones[x][y] = -1;
          return false;
        } else return [];
      }
    
      let deads = [];
      if (left_dead && stones[left_x][y] != color) {
        this.removeBlock(left_x, y, deads);
      }
      ......
      return deads;
    },
    

    最后简单放几个测试用例吧:
    吃子:


    image.png

    同时吃多片棋子:


    image.png
    自杀:
    image.png
    棋盘边角情况:
    image.png

    相关文章

      网友评论

        本文标题:JS实现的围棋吃子算法

        本文链接:https://www.haomeiwen.com/subject/jagksftx.html