対称な配置を作るパズル:鏡の線を見つける
遊びをつくる執筆にClaudeを使用しています。

7×7の盤に石が十数個、ばらばらに置かれている場面を考えてみます。ただし、ある一本の線で盤を折ると、石のほぼすべてが相手と重なります。重ならない石がひとつだけあり、その相手が置かれていません。プレイヤーの仕事は、その線がどこにあるかを見抜いて、足りない一個を置くことです。 このパズルは、答えの一手そのものが軽い点に特徴があります。置く石は一個だけで、難しさはすべて「どの線で折るのか」という推理に集まります。線が見えにくければ当てずっぽうになり、見えすぎれば一瞬で終わります。その加減を決める部品は、石の数、紛らわしい別の線の有無、線の向き(縦・横・斜め)の三つくらいです。 この記事は、その加減を自分で測るための実験の設計書です。盤面を自動生成する短いコードと、人に解いてもらう手順、記録の取り方、結果の読み方を順に示します。結果そのものは書いていません。数字は読者が自分で集めるものです。
この実験で確かめる問い
中心の問いは、「あと一個で線対称になる配置を出したとき、プレイヤーはどれくらい迷わずに鏡の線へたどり着くのか」です。これを三つの小さな問いに分けます。
- 石の数が増えると、鏡の線は見つけやすくなるのか、見つけにくくなるのか
- 縦・横・斜め(\と/)のうち、見つけにくい向きはあるのか
- 「別の線でも惜しい配置」が混ざると、誤った線を選ぶ回数は増えるのか
石が増えると、重なっている石のペアが増えるので手がかりも増えます。一方で、盤面全体を見渡す負担も増えます。どちらが勝つのかは、予想ではなく測って決める部分です。
ここでの「鏡の線」は、7×7の盤の中心を通る4本に限ります。縦の線、横の線、左上から右下への斜め線(\)、右上から左下への斜め線(/)です。まずは候補を4本に絞り、あとで増やす余地を残しておきます。
盤面を作る:準備するものとコード
準備するものは次のとおりです。
- ブラウザ(外部ライブラリもサーバーも不要です)
- 下のコードを保存した一つのHTMLファイル
- 紙とペン、または表計算ソフト(記録用)
- 時間を測るもの(スマートフォンのストップウォッチで足ります)
- 解いてくれる人(できれば3人以上)
盤面は「ある軸で完全に対称な配置を作り、軸上にない石を一個だけ取り除く」という方法で作ります。取り除いた石の相手が残るので、その軸に限っては「あと一個で対称」になります。
ただし、これだけでは別の軸でもたまたま惜しい配置になることがあります。そこで、正解の軸だけが欠け1個で、他の軸は3個以上足さないと対称にならない盤面だけを採用します。
<pre id="out"></pre>
<script>
const N = 7, T = 2;
const rnd = (n = N) => Math.floor(Math.random() * n);
const axes = {
"縦": (x, y) => [N - 1 - x, y],
"横": (x, y) => [x, N - 1 - y],
"斜め\": (x, y) => [y, x],
"斜め/": (x, y) => [N - 1 - y, N - 1 - x]
};
const key = p => p.join(",");
function missing(stones, r) {
const s = new Set(stones.map(key));
return stones.filter(p => !s.has(key(r(...p)))).length;
}
function generate(name, k) {
const r = axes[name], s = new Set();
for (let i = 0; i < k; i++) {
const p = [rnd(), rnd()];
s.add(key(p)); s.add(key(r(...p)));
}
const all = [...s].map(t => t.split(",").map(Number));
const off = all.filter(p => key(r(...p)) !== key(p));
if (!off.length) return null;
const cut = key(off[rnd(off.length)]);
return all.filter(p => key(p) !== cut);
}
function isUnique(stones, name) {
return Object.entries(axes).every(([n, r]) =>
n === name ? missing(stones, r) === 1 : missing(stones, r) > T);
}
function make(name, k) {
for (let i = 0; i < 1000; i++) {
const b = generate(name, k);
if (b && isUnique(b, name)) return b;
}
return null;
}
const names = Object.keys(axes), name = names[rnd(4)];
const b = make(name, 6);
let text = "条件を満たす盤面が見つかりませんでした";
if (b) {
const s = new Set(b.map(key));
text = "";
for (let y = 0; y < N; y++) {
for (let x = 0; x < N; x++) text += s.has(key([x, y])) ? "● " : "・ ";
text += "\n";
}
text += "石の数: " + b.length + " / 正解の軸: " + name;
}
document.getElementById("out").textContent = text;
</script>generate の第2引数 k は、対称なペアをいくつ作るかという指定です。実際の石の数は、軸上の石や重なりで変わります。そのため、条件のラベルには k ではなく、表示された「石の数」を記録します。
k を小さくしすぎると、他の軸の欠けが3個に届かず「見つかりませんでした」と出ることがあります。その場合は k を上げます。たとえば k を4・6・8の3段階で試すと、石の数の少ない盤面、中くらいの盤面、多い盤面が得られます。
最後の「正解の軸」の表示は、解いてもらう人に見せる前に必ず消すか、自分だけが見られる場所へ移してください。
解いてもらう手順と記録の取り方
次の手順で進めます。
- k を3段階(例:4・6・8)に決め、各段階で5問ずつ、合計15問を用意します。
- 正解の軸が4種類に均等に散るように、問題を選び直します。同じ向きが続かないように並べてください。
- 問題の並び順は、解く人ごとに入れ替えます。後半ほど慣れて速くなるため、全員が同じ順で解くと、その影響が条件の差に混ざります。
- 解く人には、盤の画面か印刷した盤面を渡し、「どこか一本の線で折ると、一個を除いてすべて重なります。その線を答え、足りない石の位置も答えてください」と伝えます。
- 答えが出るまでの秒数を測ります。軸の答えを間違えたら、ここで止めずに「違います」とだけ伝え、続けてもらいます。
- 解き終えたら、次の問題へ移ります。途中で内容の解説はしません。
記録は、1問につき1行で、次の項目を取ります。
- 解く人、単位・書き方:A、B、Cなどの記号
- 問題番号、単位・書き方:出題順の番号
- 石の数、単位・書き方:盤面に表示された実数
- 正解の軸、単位・書き方:縦・横・斜め\・斜め/
- 最初に答えた軸、単位・書き方:同上
- 答えを出した回数、単位・書き方:軸を答えた回数(正解を含む)
- 所要秒数、単位・書き方:提示から正解までの秒
- 足りない石の位置、単位・書き方:合っていたかどうか(○×)
「最初に答えた軸」と「答えを出した回数」は、後の分析で効いてきます。所要秒数だけでは、迷って時間がかかったのか、確信が持てずに慎重だっただけなのか、区別がつかないためです。
結果の読み方
記録が集まったら、次の順で見ていきます。
1. 最初に答えた軸の一致率を見る。 正解の軸は均等に出題しているので、手がかりなしに答えれば一致率は4分の1(25%)に近づくはずです。それを大きく上回っていれば、配置から線を読み取れていることになります。25%付近なら、手がかりが弱いか、プレイヤーが読み取れていません。まず、答えが特定の向き(例:縦ばかり)に偏っていないかを確かめてください。偏りがあれば、一致率はその癖を映しているだけです。
2. 軸の向きごとに所要秒数を並べる。 平均だけでなく、中央値と最大値も見ます。斜めだけ極端に遅いなら、斜めの読み取りに助けが要るという合図です。補助の案として、盤面の四隅に薄い対角線を引く、候補の線を押すと盤が折り畳まれる演出を付ける、などが考えられます。
3. 石の数の条件で比べる。 3段階の中央値を並べます。石が増えるほど遅くなるなら、見渡す負担が手がかりの増加を上回っています。逆なら、手がかりの増加が勝っています。段階の間に差がなければ、石の数はこの設計では調整に効かないということです。
4. 答えを出した回数の分布を見る。 1回で当てた問題が大半なら、4択の推理としては易しすぎる可能性があります。回数が多い問題は、ひとつずつ盤面を見直してください。他の軸の欠けが3個ぎりぎりで、惜しく見えている盤面かもしれません。その場合は T を大きくして、紛らわしい盤面を弾くことができます。
5. 足りない石の位置の正誤を見る。 軸は当てたのに位置を間違える人がいれば、反射の読み取りが難所です。特に斜めでは、行と列が入れ替わる感覚がつかみにくいことがあります。
人数が少ないうちは、数字は傾向の参考にとどめます。大きな差が出た箇所は、盤面を実際に見直して理由を考えるための入り口として使ってください。
起こりやすい誤り
- 作った本人が試すこと。 答えを知っている作者は、盤面を見た瞬間に線が見えてしまいます。必ず他の人に解いてもらってください。
- 乱数の偏り。 正解の軸を毎回ランダムにすると、4種類が均等に出るとは限りません。出題の前に数え、足りない向きを作り直します。
- 軸上の石の扱い。 軸の上にある石は、反射しても自分自身に重なります。取り除く石は軸上以外から選ぶ必要があります。コードの off はそのための絞り込みです。軸上の石を取り除くと、欠けは0個のままで、「あと一個」の盤面になりません。
- 石の数のラベルのずれ。 k と実際の石の数は一致しません。分析には表示された実数を使ってください。
- 練習効果の混入。 並び順を入れ替えないと、後半の問題が簡単に見えてしまいます。
- 指示の言い回しの違い。 人によって「斜めも考えてください」と付け足すと、条件が揃いません。同じ文言を読み上げるか、紙に書いて渡します。
- ヒントの混入。 「縦じゃないですよ」のような一言で、4択が3択になります。声かけは「違います」だけにとどめます。
次の行動
まず上のコードを保存し、k を変えながら盤面を数枚出して、自分の目で「見つけにくそうな盤面」と「一目で分かる盤面」を見分けてみてください。そのうえで3人に15問を解いてもらい、記録表の最初の行から埋めていきます。
アソバースに出す場合は、直下の index.html ひとつで動く形にすれば、HTMLの直接アップロードで公開できます。ただし、作品のプレイ回数は起動の実測回数で、人数ではありません。この実験の記録の代わりにはならないので、手元で集めた表を判断の根拠にしてください。
鏡の向きを読む感覚は、公式作品の「プリズムの道」でも味わえます。鏡を/と\で切り替えて光を灯台へ届ける作品です。斜めの鏡をどう読ませているかは、作品を開いて確かめてみてください。

