Chapter 70

盤面

miku
miku
2021.11.25に更新

グリッドの表現やオセロや将棋のような盤面を扱う場合は、一次元配列あるいは二次元配列に値を入れて管理する場面が多い。この章ではそのような盤面の扱う例を幾つか紹介する。

二次元配列の初期化

const n = 10;
let board;

function setup() {
  createCanvas(windowWidth, windowHeight);

  board = [];
  for (let y = 0; y < n; y++) {
    board[y] = [];
    for (let x = 0; x < n; x++) {
      board[y][x] = 0;
    }
  }
}

n x n サイズの盤面を用意する作例。

駒の描画

const n = 10;
const Cell = {
  Piece: 1,
  None: 0,
  Size: 20,
};

// 画面左上から盤面を描画すると見づらくなるので、
// この座標から描画するようにする
const start = { x: 20, y: 20 };
let board;

function setup() {
  createCanvas(windowWidth, windowHeight);

  board = [];
  for (let y = 0; y < n; y++) {
    board[y] = [];
    for (let x = 0; x < n; x++) {
      // 駒をランダムに配置する
      board[y][x] = random([Cell.Piece, Cell.None]);
    }
  }
}

function draw() {
  clear();
  translate(start.x, start.y);

  for (let y = 0; y < n; y++) {
    for (let x = 0; x < n; x++) {
      if (board[y][x] === Cell.Piece) {
        rect(x * Cell.Size, y * Cell.Size, Cell.Size);
      }
    }
  }
}

二次元配列の要素に 1 が入っていたら矩形を描画、 0 なら描画をしない作例。

クリックしたら駒を置く

const n = 10;
const Cell = {
  Piece: 1,
  None: 0,
  Size: 20,
};
const start = { x: 20, y: 20 };
let board;

function setup() {
  createCanvas(windowWidth, windowHeight);

  board = [];
  for (let y = 0; y < n; y++) {
    board[y] = [];
    for (let x = 0; x < n; x++) {
      board[y][x] = Cell.None;
    }
  }

  drawBoard();
}

function drawBoard() {
  clear();
  translate(start.x, start.y);

  for (let y = 0; y < n; y++) {
    for (let x = 0; x < n; x++) {
      if (board[y][x] === Cell.Piece) {
        fill(240);
      } else {
        fill(100);
      }
      rect(x * Cell.Size, y * Cell.Size, Cell.Size, Cell.Size);
    }
  }

  resetMatrix();
}

function mouseClicked() {
  const x = floor((mouseX - start.x) / Cell.Size);
  const y = floor((mouseY - start.y) / Cell.Size);

  if (board[y][x] === Cell.None) {
    board[y][x] = Cell.Piece;
  }

  drawBoard();
}

マウスでクリックした座標に対応する配列の値を更新する。

1つあたりのマスのサイズは Cell.Size なので、x / Cell.Size, y / Cell.Size の整数部がクリックした座標に対応する配列の添字になる。ただし、描画を (start.x, start.y) から開始しているので、マウス座標から start を引く必要がある。

一次元配列

盤面を二次元配列ではなく一次元配列で扱う場合がある。p5.jsだと pixels[] が代表的だ。座標 (x, y) の値を取得するには pixels[y][x] のようにアクセスはできないので、二次元の添字から一次元添字に変換する必要がある。

インデックスを一次元から二次元に変換する場合

const x = index % width;
const y = floor(index / width);

インデックスを二次元から一次元に変換する場合

const index = y * width + x;

シリアライズ

function setup() {
  createCanvas(windowWidth, windowHeight);

  const board = [1, 0, 0, 1, 0, 1];
  const data = board.toString();
  console.log(data); // 1,0,0,1,0,1

  console.log(data.split(",").map((c) => Number(c)));
}

一次元配列で管理するメリットとしてシリアライズ/デシリアライズが楽になるということが挙げられる。Array#toString() で文字列として出力ができるのでこのままテキストやDBに格納したり、もとに戻す場合はカンマでスプリットすればいいので Array#split(",") を実行すればいい。配列の中身は文字列になるので、数値に変換する場合は更に Array#map() を使用する。