記憶ゲームの出題:同じ音が続きすぎない乱数の作り方
遊びをつくる執筆にClaudeを使用しています。

音を覚えて返す記憶ゲームを作り、出題を「4つの音からランダムに選ぶ」だけで組んだ場面を考えてみます。遊んでみると、「ド、ド、ド、ミ」のように同じ音が3回続く列が出ることがあります。すると、遊ぶ人は「覚えにくい」と感じるかもしれませんし、「簡単すぎる」と感じるかもしれません。どちらにしても、出題の質が運に左右されてしまいます。 この記事の問いは、同じ音が続きすぎないようにしつつ、ランダムらしさも残すには、乱数をどう作ればよいかです。まず素の乱数でどれだけ連続が起きるかを計算で見積もります。次に偏りを抑える3つの方法を比べ、手元で数えられる短いコードを示します。最後に、光る葉の順番を覚えて4つの音色で返す「こだまの森」の確認済みの仕様から、出題に求められる性質を読み取ります。
素の乱数は、思ったより「同じ」を出す
音が4種類で、毎回それぞれ等確率に選ぶとします。直前と同じ音が出る確率は4分の1です。これは1回ごとの確率なので小さく見えますが、列が長くなると積み重なります。
「どこにも隣り合う同音がない」確率は、長さ n の列なら (3/4) の (n-1) 乗です。そこから、「少なくとも1か所で同じ音が隣り合う」確率を計算できます。
- 6、隣り合う同音が1か所以上ある確率(約):76%
- 8、隣り合う同音が1か所以上ある確率(約):87%
- 10、隣り合う同音が1か所以上ある確率(約):92%
長さ8の列では、10本のうち9本近くに「同じ音の連続」が含まれる計算です。偶然ではなく、ほぼ毎回起きる現象です。
3回連続も珍しくありません。長さ8の列には「連続する3つ」の窓が6か所あり、各窓がすべて同じ音になる確率は16分の1です。したがって、3連続が起きる窓の個数の期待値は 6÷16=0.375 です。窓どうしは重なるため「3連続を含む列の割合」とは一致しませんが、およそ3本に1本の規模で起こりうると見積もれます。
つまり、素の乱数は「ランダムに見えない列」をよく出します。人は本物のランダム列を見ると、偏っていると感じやすいと言われます。ゲームの出題では、その感覚をそのまま設計に織り込む必要があります。
偏りを抑える3つの作り方
音の種類を 0〜3 の番号で表すと、次の3つの方法が考えられます。
A. 直前と同じ音を禁止する
直前の音を除いた3つから選びます。連続は完全になくなります。ただし「次は必ず違う音」と読めてしまいます。遊ぶ人がこの癖に気づくと、4択のはずの予測が3択になり、記憶の負担が下がります。
B. 同じ音の連続を2回までにする
同じ音の連続は許しつつ、3回目だけを禁止します。「同じ音が2回続く」という出題を残せるので、Aより自然に見えます。ただし、2連続そのものは素の乱数と変わらず起きます。
C. シャッフルバッグ
4つの音を1セットとして並べ替え、使い切ったら次のセットを作り直す方式です。トランプを配り切ってから切り直すイメージです。
- 1セットの中では4つの音が1回ずつ出るので、音の出現回数が均等になります。
- 連続が起きるのはセットの境目だけで、その確率は4分の1です。
- 境目で前後の音が重なっても、連続は最大2回で止まります。
長さ6や8の列では境目が1か所なので、隣り合う同音が1か所以上ある確率は理論上25%です。長さ10では境目が2か所で、約44%(1−(3/4)²)です。先ほどの素の乱数の76〜92%と比べて、大きく下がります。
3つの方法には、それぞれ弱点があります。
- A 直前を禁止、連続の抑え方:連続ゼロ、出題として気になる点:「違う音が来る」と読める
- B 3連続を禁止、連続の抑え方:3連続のみゼロ、出題として気になる点:2連続は残る
- C バッグ、連続の抑え方:境目のみ、出題として気になる点:4音ごとに1巡するリズムが読める
どれが良いかは、遊ばせたい難しさで決まります。連続が少ないほど覚えやすくなりますが、パターンが読めるほど記憶ゲームとしては簡単になります。
手元で数える:短いコードで3方式を比べる
次のコードをブラウザの開発者ツールのコンソールに貼ると、3方式の連続の出方を数えられます。外部ライブラリは使いません。結果は環境ごとに少し揺れるので、この記事には載せません。
const N = 4;
const rnd = n => Math.floor(Math.random() * n);
function plain(len) {
return Array.from({ length: len }, () => rnd(N));
}
function limitRun(len, maxRun = 2) {
const s = [];
while (s.length < len) {
const v = rnd(N);
const blocked = s.length >= maxRun &&
s.slice(-maxRun).every(x => x === v);
if (!blocked) s.push(v);
}
return s;
}
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = rnd(i + 1);
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function bag(len) {
const s = [];
while (s.length < len) s.push(...shuffle([0, 1, 2, 3]));
return s.slice(0, len);
}
function maxRunOf(s) {
let best = 1, cur = 1;
for (let i = 1; i < s.length; i++) {
cur = s[i] === s[i - 1] ? cur + 1 : 1;
best = Math.max(best, cur);
}
return best;
}
function report(gen, len, trials = 10000) {
let rep = 0, tri = 0;
for (let t = 0; t < trials; t++) {
const m = maxRunOf(gen(len));
if (m >= 2) rep++;
if (m >= 3) tri++;
}
console.log(gen.name, len, "連続あり", rep / trials, "3連続以上", tri / trials);
}
[plain, limitRun, bag].forEach(g => [6, 8, 10].forEach(n => report(g, n)));記録の取り方と読み方
- 記録する単位は「方式×列の長さ」の組で、3方式×3長さの9行が出ます。
- 試行回数は1万回のままで構いません。同じコードを2回流すと、数値が小数点以下の第2位あたりで揺れる様子が見えます。揺れの幅が分かれば、差が本物かどうかを判断できます。
- plain の「連続あり」が、先ほどの表の76%・87%・92%に近ければ、計算と実装が合っています。大きくずれたら、コードの写し間違いを疑ってください。
- bag の「連続あり」は、理論上25%・25%・約44%が目安です。「3連続以上」は0になるはずです。
- limitRun の「3連続以上」は0になるはずです。
起こりやすい誤りもあります。Math.random() に Math.floor をかけ忘れると小数が混ざり、=== の比較が成り立たなくなります。シャッフルで sort(() => Math.random() - 0.5) を使うと、偏りが出る場合があります。上のように、入れ替えを順に行う方法のほうが確実です。
こだまの森の設計から読み取れること
ここからは、確認済みの仕様だけを根拠にします。こだまの森は、光る葉の順番を覚え、4つの音色で返す記憶ゲームです。6・8・10ラウンドの3コースがあり、聴き直しで15点減、間違いで30点減、3回間違えると終了します。音なしでも遊べます。
この仕様から読み取れることは3つあります。
- 選択肢が4つと少ない。 音色は4つなので、素の乱数なら直前と同じものが出る確率は4分の1です。前節の見積もりがほぼそのまま当てはまる規模です。出題を作る側は、連続の出方を意識する必要があります。
- 失敗が点数に直結する。 間違いは30点減で、3回で終了します。運悪く覚えにくい列が出ると、実力と関係なく減点されます。出題の偏りを抑える動機は、設計の中に見て取れます。
- 音なしでも遊べる。 音以外の手がかり、つまり葉の光り方も出題の一部です。「偏り」は音色だけでなく、葉の順番にも現れうると考えられます。
一方で、次の点は確認済みの範囲では分かりません。
- 出題の乱数に、素の乱数・連続の制限・シャッフルバッグのどれが使われているか
- 葉の数と音色の数の対応
- ラウンドが進むと列が伸びるのか、毎回新しい列なのか
これらは作品を開いて確かめる部分です。
作品を開いて確かめる手順
- 6ラウンドのコースを選び、出題を番号に置き換えて紙に書きます。音が分かりにくければ、光った葉の位置を番号にします。同じ記録方法を最後まで通してください。
- まず、ラウンドごとに列が伸びるのか、毎回新しい列なのかを確認します。長さの扱いが分かれば、先ほどの表と比べられます。
- 8・10ラウンドのコースも、同じ方法で記録します。1コースにつき10回ほど遊ぶと、傾向が見えやすくなります。
- 隣り合う同じ記号の有無と、同じ記号が3連続した回数を数えます。
- 作品内の最高記録は画面を開いている間しか残らないので、記録は手元のメモに残します。
読み方の目安は次のとおりです。長さ8の列を10本集めて、隣り合う同音が1本もなければ、素の乱数ではまず起きません。素の乱数で連続のない列が出る確率は、約13%だからです。これが10本続く確率は極めて小さいため、何らかの抑制が働いていると推測できます。逆に、ほとんどの列で連続が見つかれば、素の乱数に近いと考えられます。ただし、これは推測であって断定ではありません。列の長さの前提がずれていたり、サンプルが少なかったりすると、結論も揺れます。
次の行動
まず上のコードを貼り、3方式の数値を自分の環境で出してみてください。次にこだまの森を開き、6ラウンドのコースで10回分の出題を番号で書き留め、連続の出方を数えます。自作の記憶ゲームがあれば、バッグ方式への差し替えは bag 関数を呼ぶだけで済みます。遊び比べて、覚えやすさと簡単さのどちらに寄るかを確かめてください。

