マガジン

光が鏡で曲がる処理:一歩ずつ進めて壁に当たるまで

遊びをつくる

執筆にClaudeを使用しています。

小さな鏡で反射する光が、格子状の盤の上の琥珀色の結晶へ届く光パズルの模型
記事のテーマをもとに制作したAI生成イラスト。

光が鏡に当たって曲がる様子は、一歩ずつ進める小さな処理に分けると、紙と鉛筆だけで追えます。「プリズムの道」は、鏡(/と\を切り替えられます)と色ガラスを使って、灯台へ同じ色の光を届ける8ステージの光学パズルです。この記事では、その光線を5列×4行のマス目に置き換えたモデルで、スタートから壁や灯台に届くまで手計算します。 先にお断りします。「プリズムの道」の内部実装や盤面の細かい仕様は、確認済みの情報にありません。盤面が実際にマス目なのか、光がどんな単位で進むのかも、この記事では断定しません。ここで扱うのは、光が反射する処理を学ぶための簡単な「モデル」です。作品の実際の挙動との照らし合わせは、記事の後半で読者の手元で確かめられる形にします。 問いは一つです。「光はどのタイミングで向きを変え、どうなったら止まるのか」。答えは、座標と向きを一組の状態として持ち、一歩ごとに規則を一回当てはめるだけです。

この記事の目次
  1. 使うモデル:マス目、座標、向き
  2. 反射の規則は一行ずつ
  3. 手計算してみる:灯台に届く場合
  4. 鏡を一枚切り替えると
  5. 同じことをコードにする
  6. 設計から読み取れること
  7. 起こりやすい誤り
  8. 次の行動

使うモデル:マス目、座標、向き

盤面は列を x、行を y とし、左上を (0,0) とします。x は右へ行くほど増え、y は下へ行くほど増える点に注意してください。画面の座標は、数学のグラフと上下が逆であることが多いためです。

光は「いまいるマス」と「進む向き」で表します。向きは (dx, dy) という二つの数で書きます。

  • 右、(dx, dy):(1, 0)
  • 左、(dx, dy):(-1, 0)
  • 上、(dx, dy):(0, -1)
  • 下、(dx, dy):(0, 1)

一歩進むとは、x に dx を、y に dy を足すことです。右向きなら x が 1 増え、上向きなら y が 1 減ります。

この記事の盤面は次のとおりです。記号の意味は、S が光の出発点、T が灯台、. が空きマス、/ と \ が鏡です。

      x=0 x=1 x=2 x=3 x=4
y=0    T   .   .   \  .
y=1    S   .   .   /  .
y=2    .   .   .   .   .
y=3    .   .   .   .   .

光は S の (0,1) から右向きに出ます。

反射の規則は一行ずつ

鏡で向きがどう変わるかは、(dx, dy) の入れ替えだけで書けます。

  • / の鏡:(dx, dy) を (−dy, −dx) にします。
  • \ の鏡:(dx, dy) を (dy, dx) にします。

/は左下から右上へ上がる線です。右向きの光 (1,0) は (0,−1)、つまり上向きになります。上向きの光 (0,−1) は (1,0)、つまり右向きになります。左向き (−1,0) は下向き (0,1) に、下向き (0,1) は左向き (−1,0) になります。

\は左上から右下へ下がる線です。右向き (1,0) は (0,1)、つまり下向きになります。上向き (0,−1) は (−1,0)、つまり左向きになります。

この規則は、鏡の形を頭に描いて「右から来た光が当たると、どちらへ跳ね返るか」と見比べれば、自分で確かめられます。すべての鏡が「直角に曲げる」だけで、光が逆戻りしたり、止まったりはしません。

手計算してみる:灯台に届く場合

処理の順序を決めます。一歩の中で、次の順に行います。

  1. 現在の向きに従って、一マス進みます。
  2. 盤の外に出たら、壁に当たったとして止めます。
  3. 入ったマスが鏡なら、向きを変えます。
  4. 入ったマスが灯台なら、到達として止めます。

では、出発点 (0,1) から右向きの光を、一歩ずつ追ってみます。

  • 1、入ったマス:(1,1)、そこにあるもの:空き、入った後の向き:右
  • 2、入ったマス:(2,1)、そこにあるもの:空き、入った後の向き:右
  • 3、入ったマス:(3,1)、そこにあるもの:/、入った後の向き:右→上
  • 4、入ったマス:(3,0)、そこにあるもの:\、入った後の向き:上→左
  • 5、入ったマス:(2,0)、そこにあるもの:空き、入った後の向き:左
  • 6、入ったマス:(1,0)、そこにあるもの:空き、入った後の向き:左
  • 7、入ったマス:(0,0)、そこにあるもの:灯台、入った後の向き:到達

3歩目で、右向きの光が/に入り、上向きに変わります。4歩目は上へ一マス進んで (3,0) に入ります。そこは\なので、上向きの光は左向きに変わります。5歩目と6歩目は左へまっすぐ進み、7歩目で (0,0) の灯台に入ります。

鏡で向きが変わるのは、鏡のマスに「入った瞬間」です。変わった向きで次の歩が始まる、という順序を覚えておくと、以降の追跡で迷いません。

鏡を一枚切り替えると

次に、(3,1) の鏡を/から\に切り替えます。プリズムの道では鏡の向きを切り替えて遊ぶので、「鏡一枚の違いが光の道筋をどう変えるか」を追うことは、そのままパズルの練習になります。

  • 1、入ったマス:(1,1)、そこにあるもの:空き、入った後の向き:右
  • 2、入ったマス:(2,1)、そこにあるもの:空き、入った後の向き:右
  • 3、入ったマス:(3,1)、そこにあるもの:\、入った後の向き:右→下
  • 4、入ったマス:(3,2)、そこにあるもの:空き、入った後の向き:下
  • 5、入ったマス:(3,3)、そこにあるもの:空き、入った後の向き:下
  • 6、入ったマス:(3,4)、そこにあるもの:盤の外、入った後の向き:壁で停止

3歩目で右向きの光が\に入り、下向きに変わります。(3,2)、(3,3) と進み、次は y=4 になります。この盤は y が 0〜3 までなので、6歩目で盤の外に出て止まります。灯台には届きません。

この二つの表を比べると、鏡一枚の向きだけで結果が「到達」と「壁」に分かれます。パズルで考えるべきことは、結局「どの鏡をどちら向きにすれば、出発点から灯台までの道がつながるか」です。

同じことをコードにする

手計算の規則をそのままコードにします。表示用の <pre> と、一歩ずつの記録だけの最小構成です。

html
<pre id="out"></pre>
<script>
const rows = [
  String.raw`T..\.`,
  String.raw`S../.`,
  String.raw`.....`,
  String.raw`.....`,
];
let x = 0, y = 1, dx = 1, dy = 0;
const log = [];
for (let step = 1; step <= 50; step++) {
  x += dx; y += dy;
  if (y < 0 || y >= rows.length || x < 0 || x >= rows[0].length) {
    log.push(step + ": (" + x + "," + y + ") 壁"); break;
  }
  const c = rows[y][x];
  if (c === "/") [dx, dy] = [-dy, -dx];
  else if (c === "\\") [dx, dy] = [dy, dx];
  log.push(step + ": (" + x + "," + y + ") " + c);
  if (c === "T") { log.push("灯台に到達"); break; }
}
document.getElementById("out").textContent = log.join("\n");
</script>

HTMLファイルとして保存してブラウザで開くと、一歩ごとの座標が並びます。上の最初の表と同じ7歩になるはずです。手計算と食い違うなら、どちらかの規則の適用に誤りがあります。2行目の rows の中の / を \ に書き換えると、二番目の表と同じ結果になります。

ここで step <= 50 という上限を置いているのは、保険のためです。鏡の置き方次第で、光が同じ場所をぐるぐる回る盤面を作ってしまう可能性があるためです。実際の作品でどう防いでいるかは確認済みの情報にないので、自作するときは「歩数に上限を置く」か「同じ(位置, 向き)を二度通ったら止める」かのどちらかを入れておくと安全です。

設計から読み取れること

確認済みの範囲で、「プリズムの道」の設計から読み取れることを整理します。

  • 鏡が/と\の二択である:鏡一枚の状態が二通りしかないので、盤面全体の状態は「どの鏡がどちら向きか」の組み合わせで表せます。上の手計算のように、一枚切り替えるたびに結果を追い直せる大きさです。
  • 「最少回転」を記録する:成績の単位が、時間ではなく鏡を切り替えた回数になっています。光の道筋をあらかじめ考えてから触るほど、回転数が少なくなる設計だと読み取れます。
  • 戻す、一歩だけのヒント:直前の状態へ戻せるのは、鏡の向きの履歴を持っているからだと考えられます。ヒントが「一歩だけ」なのは、答えを丸ごと見せずに考える余地を残すためだと読み取れますが、これは設計意図の推測です。
  • 色ガラス:灯台へ「同じ色の光」を届ける、という条件から、光は向きだけでなく色も持つと読み取れます。色ガラスを通ったときの変化の細かい仕様は確認済みの情報にないので、作品を開いて確かめてください。

作品側の盤面がマス目か、光がマスの中心を通るかなどは、確認できていません。この部分は作品を開いて確かめてください。

起こりやすい誤り

  • y の向きを逆にする:上に進むのに y を増やしてしまうと、/と\の結果がすべて逆になります。「右向きの光が/で上向きになるか」を最初に必ず確かめてください。
  • 鏡の見た目と式を取り違える:/に\の式を当ててしまう誤りです。手計算の表を二、三歩分書くと気づけます。
  • 向きを変える前に進む:鏡に入った後でなく、入る前に向きを変えてしまうと、一マスずれます。順序は「進む、判定する、向きを変える」です。
  • 盤の外の判定を忘れる:rows[y][x] を読む前に範囲を確かめないと、盤の外を読んでエラーになります。
  • 止める条件が一つしかない:壁だけで止める設計だと、灯台に着いた後も進み続けます。到達と壁は別の終了条件です。

次の行動

紙に5×4のマス目を書き、鏡を一枚だけ変えながら光の道筋を二、三通り追ってみてください。そのうえで「プリズムの道」の最初のステージを開き、光が通りそうな道筋を指でなぞって予想してから鏡を切り替えます。予想と食い違ったときは、y の向きと鏡の式の取り違えを疑ってください。それが、光の処理を自分で組み立てる最初の足場になります。