みさご解体新書

マップ生成1

実行例

実行結果を見る

ソースコード

app.ts

解説/アルゴリズム

  1. 二次元配列を 2 つ用意する。(計算用、表示用)
  2. 計算用の中身を、x% の確率で壁の値、 (100 - x)% の確率で床の値になるように埋めていく。
  3. 表示用の中身をすべて壁の値で埋めておく。
  4. ループ(5 回)
  5. 計算用のセル一つ一つを走査する。(x=2 ~幅-2, y=2 ~高さ-2)
  6. 自身のセルの周り 3x3, 5x5 にある 1(壁)の数を数える。(自身のセルも含める)
  7. 3x3 の壁の数が 5 以上、もしくは 5x5 の壁の数が 2 以下なら、表示用のセルを壁に、そうでなければ通路に変える。
  8. 表示用の中身をすべて計算用にコピーする。
  9. 表示用をもとに描画を行う。

コード解説

配列の初期化

const CellType = {
  Wall: "wall",
  Floor: "floor",
};
type CellType = typeof CellType[keyof typeof CellType];

let map: CellType[][]; // 表示用
let temp: CellType[][]; // 計算用

for (let y = 0; y < tileHeight; y++) {
  temp[y] = [];
  for (let x = 0; x < tileWidth; x++) {
    temp[y][x] =
      p.random(1.0) < fillProbability ? CellType.Wall : CellType.Floor;
  }
}

for (let y = 0; y < tileHeight; y++) {
  map[y] = [];
  for (let x = 0; x < tileWidth; x++) {
    map[y][x] = CellType.Wall;
  }
}
  • 計算用の中身を、x% の確率で壁の値、 (100-x)% の確率で床の値になるように埋める。
  • 表示用の中身をすべて壁の値で埋めておく。

配列の捜査

for (let y = 2; y < tileHeight - 2; y++) {
  for (let x = 2; x < tileWidth - 2; x++) {}
}

後に自身のセルの周りにある 5x5 のチェックがあるので、配列外にアクセスしないように最初から 2 マス内側から走査を始める形にしておく。 つまり、配列の外側 2 マス分はアクセスすることがないので、すべて壁のままになる。

壁の数を数える

for (let ty = y - 1; ty <= y + 1; ty++) {
  for (let tx = x - 1; tx <= x + 1; tx++) {
    if (onBoard(tx, ty) && temp[ty][tx] === CellType.Wall) {
      count33++;
    }
  }
}

自身を含めた、周りにある 3x3 の壁の数を確認する。

for (let ty = y - 2; ty <= y + 2; ty++) {
  for (let tx = x - 2; tx <= x + 2; tx++) {
    if (onBoard(tx, ty) && temp[ty][tx] === CellType.Wall) {
      count55++;
    }
  }
}

自身を含めた、周りにある 5x5 の壁の数を確認する。

if (5 <= count33 || count55 <= 2) {
  map[y][x] = CellType.Wall;
} else {
  map[y][x] = CellType.Floor;
}

壁の数に応じて表示用配列の中身を書き換える。

次ループの前の準備

for (let y = 0; y < tileHeight; y++) {
  for (let x = 0; x < tileWidth; x++) {
    temp[y][x] = map[y][x];
  }
}

次の配列の操作の前に表示用の値をすべて計算用にコピーしておく。

内部で利用しているアルゴリズム

擬似乱数