♟️

Flutter で将棋ゲームを作ろう #3.5 駒の移動範囲判定

2023/07/03に公開

はじめに

こちらは Flutter で将棋ゲームを作ろう #3 駒を動かそう(前編) をさらに解説するための記事です。
https://zenn.dev/flutteruniv_dev/articles/d84f5b822dba21

helper_methods.dart に定義した駒の移動範囲判定メソッドが長すぎるので、より詳細に解説したいと思ってこの記事を書いています。

自陣と敵陣の分岐

分かりやすさのために歩の処理を抜き出します。

helper_methods.dart
List<List<int>> calculateValidMoves(List<List<dynamic>> shogiBoard, int row, int col, ShogiPiece? piece) {
  if (piece == null) {
    return [];
  }

  List<List<int>> candidateMoves = [];
  int direction = piece.isAlly ? -1 : 1;

  switch (piece.type) {
    case ShogiPieceType.hohei: // 歩兵
      var newRow = row + direction;

      // 盤面から出ていない
      if (isInBoard(newRow, col)) {
        // 空の座標か、敵の座標だった場合
        if (shogiBoard[newRow][col] == null || shogiBoard[newRow][col]!.isAlly != piece.isAlly) {
          candidateMoves.add([newRow, col]);
        }
      }

      break;
    default:
  }

  return candidateMoves;
}

歩は自分の駒なら上へ、相手の駒なら下にしか進めません。

int direction = piece.isAlly ? -1 : 1;

isAlly (味方の駒かどうか) を使って進む方向を決めています。

var newRow = row + direction;
.
.
.
candidateMoves.add([newRow, col]);

自分の歩だった場合、横への移動はできないので、 row - 1 してひとつ上に進んだ座標のみが移動可能な座標として計算されます。

自分の駒はNGで相手の駒はOK

駒が移動可能な範囲の条件として以下があり

  • 盤面の中であること
  • 駒が置かれていないこと
  • 駒があっても相手の駒であること
// 盤面から出ていない
if (isInBoard(newRow, col)) {
  // 空の座標か、敵の座標だった場合
  if (shogiBoard[newRow][col] == null || shogiBoard[newRow][col]!.isAlly != piece.isAlly) {
    candidateMoves.add([newRow, col]);
  }
}

この if 文で実現しています。

1方向にどこまでも行ける駒

飛車、角、香車などがありますね。

case ShogiPieceType.hisya: // 飛車
  var directions = [
    [-1, 0], // 上
    [1, 0], // 下
    [0, -1], // 左
    [0, 1], // 右
  ];

  for (var direction in directions) {
    var i = 1;

    while (true) {
      var newRow = row + (direction[0] * i);
      var newCol = col + (direction[1] * i);

      // 盤面から出た場合
      if (!isInBoard(newRow, newCol)) {
        break;
      }

      // 対象の座標に駒がある
      if (shogiBoard[newRow][newCol] != null) {
        // 対象の駒が敵
        if (shogiBoard[newRow][newCol]!.isAlly != piece.isAlly) {
          candidateMoves.add([newRow, newCol]);
        }
        break;
      }

      candidateMoves.add([newRow, newCol]);
      i++;
    }
  }

飛車の動きで解説をします。

var directions = [
  [-1, 0], // 上
  [1, 0], // 下
  [0, -1], // 左
  [0, 1], // 右
];

まず進める方向を定義します。
飛車は上下左右に動けるのでこの値ですね。

for (var direction in directions) {
  var i = 1;

  while (true) {
    var newRow = row + (direction[0] * i);
    var newCol = col + (direction[1] * i);

    // 盤面から出た場合
    if (!isInBoard(newRow, newCol)) {
      break;
    }

    // 対象の座標に駒がある
    if (shogiBoard[newRow][newCol] != null) {
      // 対象の駒が敵
      if (shogiBoard[newRow][newCol]!.isAlly != piece.isAlly) {
        candidateMoves.add([newRow, newCol]);
      }
      break;
    }

    candidateMoves.add([newRow, newCol]);
    i++;
  }
}

for ループで上・下・左・右順番に処理し、盤面から出るまですべての座標を確認していきます。
確認した中で、空きの座標・最初に見つけた相手の駒の座標を candidateMoves に入れることで、飛車の動きを実装できます。

1マスしか進めない駒

歩、金将、王などがそうですね。

case ShogiPieceType.kinsho:
  var directions = [
    [-1, 0], // 上
    [1, 0], // 下
    [0, -1], // 左
    [0, 1], // 右
    [direction, -1], // 左上
    [direction, 1], // 右上
  ];

  for (var direction in directions) {
    var newRow = row + (direction[0]);
    var newCol = col + (direction[1]);

    // 盤面から出た場合
    if (!isInBoard(newRow, newCol)) {
      continue;
    }

    // 対象の座標に駒がある
    if (shogiBoard[newRow][newCol] != null) {
      // 対象の駒が敵
      if (shogiBoard[newRow][newCol]!.isAlly != piece.isAlly) {
        candidateMoves.add([newRow, newCol]);
      }
      continue;
    }

    candidateMoves.add([newRow, newCol]);
  }

金将の動きで解説をします。

var directions = [
  [-1, 0], // 上
  [1, 0], // 下
  [0, -1], // 左
  [0, 1], // 右
  [direction, -1], // 左上
  [direction, 1], // 右上
];

金将は上・下・左・右・右上・左上に1マスだけ進める駒です。
右上、左上のみに direction を利用しているのは、自陣と敵陣で斜め上の方向が異なる(自陣の金は左上・右上、敵陣の金は左下・右下)からですね。

for (var direction in directions) {
  var newRow = row + (direction[0]);
  var newCol = col + (direction[1]);

  // 盤面から出た場合
  if (!isInBoard(newRow, newCol)) {
    continue;
  }

  // 対象の座標に駒がある
  if (shogiBoard[newRow][newCol] != null) {
    // 対象の駒が敵
    if (shogiBoard[newRow][newCol]!.isAlly != piece.isAlly) {
      candidateMoves.add([newRow, newCol]);
    }
    continue;
  }

  candidateMoves.add([newRow, newCol]);
}

for ループで directions を順番に処理し確認していきます。
確認した中で、空きの座標・最初に見つけた相手の駒の座標を candidateMoves に入れることで、金将の動きを実装できます。

終わりに

ここまで読んでいただきありがとうございました〜!

駒の移動判定は将棋の肝といえる部分だと思うので、
本編で詳しく説明出来なかったぶんをここで挽回できていれば幸いです。
質問などあればお気軽にコメントください!

本編はまだまだ続きますのでお付き合いよろしくお願いします🌿

Twitter

https://twitter.com/minase_hiro_

Flutter大学

Discussion