迷路の出口が必ずある理由を体験するゲーム
遊びをつくる執筆にClaudeを使用しています。

迷路ゲームを作って誰かに渡す場面を考えてみます。生成した迷路の入口と出口の間に、そもそも道がつながっていなかったら、遊ぶ人は永遠に出口へ着けません。「出口が必ずある」という性質は、迷路ゲームの土台です。 迷路には、もうひとつよく聞く言い伝えがあります。「右手を壁につけて歩き続ければ、いつか出口に着く」というものです。この記事では、この二つが別々の問いであることを、小さなプログラムで確かめます。「出口へ道があること」と「右手法で出口に着くこと」は、別の条件で決まります。二つの迷路を用意して右手法のボットを走らせ、片方では成立し、片方では成立しない理由を自分の目で追うのがこの実験のねらいです。 この記事は実験の設計書です。迷路、コード、記録の取り方、結果の読み方までを示しますが、実行結果は書きません。手を動かして答えを出すのは読者です。
二つの問いを分ける
最初に、問いを二つに分けます。
- 問い1(出口はあるか):入口から出口まで、通路をたどってつながっているか。
- 問い2(右手法で着くか):右手を壁につけて歩く規則に従うと、出口のセルに入るか。
問い1は「つながり」の話です。通路のセルを点、隣り合う通路を線とみなすと、入口と出口が同じかたまりに入っているかどうかで決まります。迷路を作るとき、スタートのセルから隣の未訪問のセルへ通路を掘り進める方法を使うと、新しく掘ったセルは必ず直前のセルとつながります。そのため、掘ったセルはすべて入口と同じかたまりに入ります。これが「出口が必ずある」理由の基本形です。反対に、壁をランダムに置いていく方法では、通路が分断される可能性が残ります。
問い2は「壁のつながり」の話です。右手を壁につけて歩くと、手が触れている壁のかたまりの縁を、ぐるりと一周するように進みます。出口がその壁のかたまりに接していれば、一周するうちに通ります。ところが壁が二つのかたまりに分かれていると話が変わります。外周から切り離された「島」の壁があり、右手が外周の壁だけをなぞり続けると、島に接した出口は永遠に通りません。
つまり、道がつながっていること(問い1)と、右手が出口の壁に触れていること(問い2)は独立しています。この実験では、問い1は成り立っているのに問い2が成り立たない迷路を、わざと作ります。
準備するもの:二つの迷路とボット
用意するのは、テキストエディタとブラウザだけです。外部のライブラリもサーバーも使いません。
迷路は7×7の文字で表します。# が壁、. が通路、S がスタート、G がゴールです。位置は「行, 列」で、左上を0行0列と数えます。
迷路Aです。
#######
#S#...#
#.#.#.#
#.#.#.#
#...#.#
#...#G#
#######迷路Bです。
#######
#S....#
#.###.#
#.#G#.#
#.#.#.#
#.....#
#######どちらも、スタートは1行1列、ゴールは入口からたどれる場所にあります。問い1の答えは、二つとも「出口はある」です。鉛筆で通路をなぞって確かめてください。
違いは壁にあります。
- 迷路A:壁はすべて外周とつながっています。2〜4行目の2列目の壁は上の外周から、2〜5行目の4列目の壁は下の外周からぶら下がる形です。
- 迷路B:中央の壁(2行目の2〜4列目、3行目と4行目の2列目と4列目)は、外周の壁と一切つながっていません。ゴールはこの島の内側に面しています。
壁のつながりは、上下左右の隣だけで数えます。斜めに接しているだけの壁は、つながっていません。これは後で誤りやすい点として触れます。
次が右手法のボットです。maze.html のような名前で保存し、ブラウザで開いてください。
<pre id="o"></pre>
<script>
const M={
A:["#######","#S#...#","#.#.#.#","#.#.#.#","#...#.#","#...#G#","#######"],
B:["#######","#S....#","#.###.#","#.#G#.#","#.#.#.#","#.....#","#######"]};
const dx=[0,1,0,-1],dy=[-1,0,1,0],N=["北","東","南","西"];
function run(k,d0){
const g=M[k],seen=new Set();
let x=1,y=1,d=d0,n=0;
while(true){
if(g[y][x]==="G")return `${k} 向き${N[d0]}: 到達 ${n}歩`;
const key=[x,y,d].join();
if(seen.has(key))return `${k} 向き${N[d0]}: 周回 ${n}歩`;
seen.add(key);
for(const t of [1,0,3,2]){
const e=(d+t)%4;
if(g[y+dy[e]][x+dx[e]]!=="#"){d=e;x+=dx[e];y+=dy[e];n++;break;}
}
}
}
let s="";
for(const k in M)for(let d=0;d<4;d++)s+=run(k,d)+"\n";
o.textContent=s;
</script>仕組みは三点です。向きは北・東・南・西を0〜3の数字で持ちます。一歩ごとに「右、直進、左、後ろ」の順で進める方向を探し、最初に見つかった方へ進みます。そして「位置と向きの組」が一度通った状態に戻ったら、決定的な規則なので同じ道を回り続けると判断し、「周回」として止めます。
手順:予想を先に書いてから走らせる
遊びとして成立させるコツは、実行する前に予想を書くことです。ボットを走らせて眺めるだけでは、「そうなんだ」で終わってしまいます。次の順で進めてください。
- 紙に、迷路A・Bそれぞれについて、スタートの向きを北・東・南・西にした8通りの行を作ります。
- 各行に「到達」か「周回」かの予想を書き込みます。まだコードは動かしません。
- 予想の根拠も一言添えます。「右手は外周の壁に触れるはず」「この向きだと最初に島の壁に触れる」といった内容です。
- maze.html を開き、表示された8行と予想を突き合わせます。
- 外れた行について、ボットが通ったセルを迷路の図に鉛筆でなぞり、どの壁に右手が触れていたかを確かめます。
予想の手がかりは、スタート直後に右手がどの壁に触れるかです。スタートのセルは外周の壁に面しています。向きによっては、右に曲がれる通路が先に見つかり、最初に触れる壁が変わる可能性があります。手でなぞって考えてみてください。
記録の取り方
記録の単位は「迷路×向き」の1行です。右手法の規則は決定的なので、同じ条件で何度走らせても同じ結果になります。回数を重ねる必要はなく、1行につき1回で足ります。
- A、向き:北、予想:、実測:、歩数:、右手が触れた壁(外周/島):
- A、向き:東、予想:、実測:、歩数:、右手が触れた壁(外周/島):
- …、向き:…、予想:、実測:、歩数:、右手が触れた壁(外周/島):
最後の列は、鉛筆でなぞった結果から書きます。「歩数」の値は参考程度で十分です。周回の歩数は「同じ状態に戻ったと検出するまで」の数で、迷路の一周の長さと一致するとは限りません。
結果の読み方
実測が出たら、次のように読み解いてください。
- 迷路Aが、すべての向きで「到達」だった場合:壁が一体なので、どこから始めても右手は同じ壁のかたまりをなぞり、その縁にゴールが含まれていた、と解釈できます。
- 迷路Bで、向きによって「到達」と「周回」が分かれた場合:同じ迷路、同じスタート、同じ規則でも、最初に右手が触れる壁が外周か島かで結果が変わった、と読めます。なぞった線の外側と内側を見比べてください。
- 迷路Bの「周回」の行で、ゴールの隣のセルを一度も通っていない場合:右手が島に一度も触れていないことの確認になります。
- 迷路Aと迷路Bの結果が全部同じだった場合:迷路の文字列を転記し間違えている可能性があります。行ごとに # の数と位置を見直してください。
- 予想と大きくずれた場合:右手法の優先順位(右、直進、左、後ろ)の理解か、壁のつながり方の見落としが原因のことが多いです。
ここで確かめたいのは、「周回」だからといって「出口がない」とは限らない点です。迷路Bのゴールには、入口から道がつながっています。右手法の失敗は、問い2が成り立たなかったということで、問い1の否定にはなりません。
起こりやすい誤り
- 斜めの接触を、壁のつながりと数える:壁のつながりは、上下左右の隣だけで判定します。斜めだけで接している壁は別のかたまりです。自作の迷路で島ができたかどうかを調べるとき、見落としやすい点です。
- 周回の判定を位置だけでしてしまう:行き止まりで引き返す正常な動きは、同じ位置を二度通ります。位置だけで判定すると、これを周回と誤認します。位置と向きの組で見る必要があります。
- 右手法の優先順位を入れ替える:右に曲がれるなら、直進できても右を優先します。順序を変えると、右手が壁から離れるので、別のアルゴリズムになります。
- 広い空間を迷路Aのように読む:迷路Aには、通路が横に広がった場所があります。広いところでは、右手が触れる壁だけを見れば動きを予想できます。反対側の壁は関係しません。
- 右手法で出口の有無を確かめようとする:右手法が着かなかった迷路を「出口なし」と判断するのは誤りです。出口の有無は、入口から通路を順にたどって、出口のセルに届くかを調べて確かめます。
ゲームづくりへの持ち帰り方
この実験から、迷路ゲームの設計に使える判断基準が二つ取り出せます。
一つ目は、出口の保証です。通路を掘り進めて作る方式なら、つながりは作り方が保証してくれます。壁を置く方式や、手作りの迷路では、公開前に入口から通路をたどって出口に届くかを確認する工程を入れておくと安全です。
二つ目は、難度の設計です。右手法で解ける迷路は、壁が一体だから解けます。島を一つ置くだけで、「壁沿いに歩けば解ける」という近道は封じられます。島を入れる場所と数が、難度の山場になります。
ただし、島を入れたことで出口への道が壊れていないかは、必ず別に確認してください。
次の行動
まず迷路Bの島の位置を一か所ずらして、ゴールを外周の壁に面する場所へ動かし、予想を先に書いてから8通りを走らせ直してください。ゴールと島の関係が変わったときに、結果の何が動くかを見る練習になります。それができたら、maze.html をそのまま index.html として直下に置いたZIPにし、アソバースへ投稿して、遊ぶ人にも予想を立ててもらう形に育ててみてください。公開にはメールアドレスの確認が必要です。

