Vue.jsで迷路の自動生成

## はじめに
この記事ではvue.jsを使用して、「棒倒し法」と呼ばれるシンプルなアルゴリズムで迷路を自動生成する方法について解説していきます。



## 迷路の定義
今回の迷路は2次元配列を用いて定義し、`1`を通れない壁、`0`を通れる道として表現します。この表現方法により、迷路を簡単に作成することができます。



### 迷路の例
初めに、どのような形で迷路が生成されるか確認します。黒い部分が壁(`1`)、白い部分が通路(`0`)です。

![スクリーンショット 2024-01-26 12.49.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730382/d68a15d3-dbc8-6a24-0ba5-c4ec7a20ae37.png)

壁の部分の背景を黒、通路を白にして再度表示すると以下のようになります
![スクリーンショット 2024-01-26 12.50.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730382/f430e013-062a-c865-3bba-036a6466a71e.png)


## 棒倒し法とは
棒倒し法では、迷路の生成を「棒」の概念を用いて行います。以下のステップに従って迷路を作成していきます。

### 1. 初期状態の設定
最初に外壁と内壁を設定します。内壁は2マスごとに配置し、迷路の基本的な枠組みを作ります。
壁が１、通路が0で２次元配列を作成します。


```vuejs
<script setup>
import { ref, onMounted } from "vue";

const height = 11;
const width = 11;
const maze = ref([]);

// 外壁かどうかの判定
function isBorder(row, col, height, width) {
  return row === 1 || row === height || col === 1 || col === width;
}

// 内壁かどうかの判定
function isWall(row, col) {
  return row % 2 === 1 && col % 2 === 1;
}

function createArray(height, width) {
  for (let row = 1; row <= height; row++) {
    maze.value[row] = [];
    for (let col = 1; col <= width; col++) {
      // 壁だったら１を配列に入れる
      if (isBorder(row, col, height, width) || isWall(row, col)) {
        maze.value[row][col] = 1;
      } else {
        maze.value[row][col] = 0;
      }
    }
  }
}
onMounted(() => {
  createArray(height, width);
});

</script>
<template>
  <div class="flex" v-for="(row,index) in maze" :key="index">
    <div v-for="(cell, index) in row" :key="index">
      <div class="flex">
        <div v-if="cell === 1" class="wall"></div>
        <div v-else-if="cell === 0" class="blank"></div>
      </div>
    </div>
  </div>
</template>
<style scoped >
body{
  background-color: #dd4343;
}
.flex {
  display: flex;
}
.wall {
  width: 10px;
  height: 10px;
  background-color: black;
}
.blank {
  width: 10px;
  height: 10px;
  background-color: white;
}
.cell{
  width: 15px;
  height: 15px;

}
</style>

```

壁の部分の背景を黒、通路を白にして表示してみました。
迷路の中にある黒い点の部分が内壁になります。
![スクリーンショット 2024-01-26 12.51.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730382/d98b900c-02f5-e17b-9edc-4b1efec3e085.png)



２次元配列だと以下のように表示されます。
![スクリーンショット 2024-01-26 12.40.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730382/6a9fa091-1e96-eafb-7a44-78dfcba7ab89.png)


### 2. 壁（棒）の倒し方
内壁（２次元配列の内側の１の部分）を棒と見立て、。以下のルールに沿ってランダムな方向に「倒します」。
- 外周の壁は倒さない。
- 最初の行の棒は上、下、左、右のいずれかランダムに倒す。
- それ以外の行では下、左、右のいずれかに倒す。
- 壁がある方向には棒を倒さない。


```javascript

function rand(n) {
  return Math.floor(Math.random() * n);
}

const createMazeArray = async (height, width) => {
  for (let y = 3; y < height; y += 2) {
    for (let x = 3; x < width; x += 2) {
      // 棒を倒せる方向を配列にする
      // 右と下は全パターンでOK
      const direction = ["right", "down"];

      if (y == 3) {
        direction.push("up");
      }
 
      if (maze.value[y][x - 1] == 0) {
        direction.push("left");
      }
      switch (direction[rand(direction.length)]) {
        case "up":
          maze.value[y - 1][x] = 1;
          break;
        case "right":
          maze.value[y][x + 1] = 1;
          break;
        case "down":
          maze.value[y + 1][x] = 1;
          break;
        case "left":
          maze.value[y][x - 1] = 1;
          break;
      }
    }
  }
  
  // 入口と出口を作成
  maze.value[1][2] = 0;
  maze.value[height][width - 1] = 0;
}

onMounted(() => {
  createArray(height, width);
  createMazeArray(height, width)
});


```

迷路のheightとwidthの大きさを変えて表示してみます。

```javascript
const height = 33;
const width = 33;
```

以下のような迷路が生成されます。

![スクリーンショット 2024-01-31 10.52.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730382/55c6772e-db7f-cadb-767d-016d5f6f7e6f.png)


## まとめ
棒倒し法は、シンプルな迷路自動生成アルゴリズムです。
他にも穴掘り法や、壁伸ばし法などのアルゴリズムがあり、
ゆくゆくは不思議なダンジョンのようなダンジョンの自動生成もしてみたいです。
ローグライクの開発について議論しているコミュニティが公開している生成アルゴリズムにダンジョンの生成もあるので気になる方はぜひ調べてみてください。

### 参考
https://algoful.com/Archive/Algorithm/MazeBar

https://github.com/marukrap/RoguelikeDevResources