同じ仕様書を3回渡して、出てくるコードはどれだけ違うか
遊びをつくる執筆にClaudeを使用しています。

同じ仕様書をAIに3回渡して、3つのコードが返ってくる場面を考えてみます。ファイルを開くと、見た目はほとんど同じなのに中身の書き方が違うかもしれません。逆に、書き方は似ているのに、入力を間違えたときの挙動だけが違うかもしれません。この「違い」は一括りにすると、どれも同じ重さに見えてしまいます。 この記事は結果を報告するものではなく、読者が自分で行うための実験の設計書です。確かめるのは、同じ仕様書での再現性です。コードの差を「挙動」「構造」「表面」の三つの層に分けて数え、どの層が安定し、どの層が揺れるのかを切り分けます。ゲームを作る人にとって、これが分かると仕様書のどこを厳密に書き、どこを任せてよいかが見えてきます。 実験の中心は、仕様書に書いていない部分がどう埋められるかを見ることです。そこで、あえて空白を一つ残した小さな仕様書を使います。
準備:仕様書を固定し、条件をそろえる
題材は、1〜100の数を当てる「数当て」にします。小さな題材にするのは、3つのコードを読み比べて動作まで確かめる作業を、1回の実験で終えられるようにするためです。次の仕様書を、一字も変えずに3回使います。
- 1〜100の整数をランダムに一つ決めます。
- 入力欄と「答える」ボタンを置きます。
- 入力が整数でない、または1〜100の範囲外なら、エラー文を表示します。この入力は回数に数えません。
- 答えが正解より大きければ「もっと小さい」、小さければ「もっと大きい」と表示します。
- 挑戦できるのは最大7回です。残り回数を常に表示します。
- 正解したら、何回目で当たったかを表示して終了します。7回で当たらなければ正解を表示して終了します。
- 終了後に「もう一度」ボタンで最初からやり直せます。
- 成果物は、直下に置く単一の index.html です。外部ライブラリ、外部通信、サーバー用コード、サービスワーカーは使いません。データは保存しません。
8番は、アソバースへHTMLを直接アップロードするときの条件(直下の index.html が入口、サーバー用コードとサービスワーカーは対象外)に合わせています。出力形式を仕様書に入れておくと、「ファイル構成」の差が最初から消え、比べたい部分に集中できます。
空白は意図的に残しています。たとえば「前に入力した数と同じ数をもう一度入れたらどうするか」は書いていません。回数に数えるのか、警告するのか、何も言わないのか。仕様に書かれていない部分は、生成のたびに別の判断で埋められる可能性があります。ここに限らず、3つのコードの間で判断が食い違う箇所を探すのがこの実験の主眼です。
次の条件を、3回とも同じにします。
- 使うAIツール、モデル、設定(選べる場合)。設定の詳細は、使うツールの案内で確認してください。
- 仕様書の文面。コピーして貼り付け、毎回同じ前置きだけを添えます。
- 会話の状態。毎回、新しい会話を開始します。
手順:3回の生成と保存
手順は次のとおりです。
- 新しい会話を開き、仕様書を貼って「この仕様で作ってください」と依頼します。
- 返ってきたコードを、手を加えずに run1.html として保存します。
- 会話を閉じて新しい会話を開き、同じ文面で依頼して run2.html を保存します。
- もう一度同じことをして run3.html を保存します。
「手を加えずに」が大切です。エラーが出ても直さず、そのまま保存してください。動かないコードが出たなら、それも「揺れ」の一つとして記録します。
保存したら、ブラウザで3つとも開き、仕様書の項目1〜8を一つずつ実際に操作して確かめます。コードを読むだけで判断せず、必ず遊んでください。見落としやすいのは、範囲外入力が回数に数えられていないか、7回目の扱い、「もう一度」で残り回数が戻るかの3点です。
記録:三つの層に分けて残す
差を数える前に、層を決めておきます。
- 挙動、見る対象:仕様書の各項目を満たすか、空白をどう埋めたか、記録の単位:項目ごとに○×、空白は内容を一文で
- 構造、見る対象:状態の持ち方、関数の分け方、DOMの作り方、記録の単位:状態変数の数、関数名の一覧、更新方法の種別
- 表面、見る対象:変数名、画面の文言、色、要素のid、記録の単位:一致・不一致の一覧
挙動の層は、各項目を run ごとに○×で付けます。空白の項目(同じ数の再入力など)は、実際に同じ数を2回入れて、回数が増えるか、警告が出るか、無反応かを書き留めます。
構造と表面の層は、機械的に数えられます。次の最小スクリプトは、選んだHTMLファイルから id、変数名、関数名を取り出し、ファイル同士の重なり具合を0〜1で出します。ローカルで開くだけで動き、外部通信はしません。
<input type="file" id="f" multiple accept=".html">
<pre id="out"></pre>
<script>
const jac = (a, b) => {
const u = new Set([...a, ...b]);
if (u.size === 0) return 1;
let n = 0;
for (const x of a) if (b.has(x)) n++;
return n / u.size;
};
document.getElementById('f').addEventListener('change', async (e) => {
const rows = [];
for (const file of e.target.files) {
const t = await file.text();
const pick = (re) => new Set([...t.matchAll(re)].map(m => m[1]));
rows.push({
name: file.name,
lines: t.split('\n').length,
ids: pick(/id="([^"]+)"/g),
vars: pick(/\b(?:const|let|var)\s+(\w+)/g),
funcs: pick(/function\s+(\w+)/g)
});
}
let s = '';
for (const r of rows) {
s += `${r.name}: ${r.lines}行 / id ${r.ids.size} / 変数 ${r.vars.size} / 関数 ${r.funcs.size}\n`;
}
for (let i = 0; i < rows.length; i++) {
for (let j = i + 1; j < rows.length; j++) {
const a = rows[i], b = rows[j];
s += `${a.name} × ${b.name}: id ${jac(a.ids, b.ids).toFixed(2)}`
+ ` / 変数 ${jac(a.vars, b.vars).toFixed(2)}`
+ ` / 関数 ${jac(a.funcs, b.funcs).toFixed(2)}\n`;
}
}
document.getElementById('out').textContent = s;
});
</script>1.00ならまったく同じ、0に近いほど別物です。これは名前の一致を見るだけの粗い道具なので、数値は目安にとどめ、構造の判断は実際にコードを読んで補ってください。読むときは、状態を持つ変数が何個あるか、画面の更新が「毎回全部描き直す」のか「必要な箇所だけ書き換える」のか、入力検証が一つの関数にまとまっているかを、run ごとに一行ずつメモします。
結果の読み方:揺れの場所で判断を変える
3つの記録がそろったら、各項目を「3つとも同じ」「2つが同じ」「3つとも違う」に分けます。そのうえで、揺れた場所によって次のように読みます。
- 挙動(仕様項目が×)、読み方:仕様書の書き方が曖昧か、AIの取りこぼしの可能性があります。、次の手:該当項目を数値や条件で書き直し、再度3回試します。
- 挙動(空白の埋め方が割れた)、読み方:仕様書が答えを決めていない部分です。、次の手:作りたい挙動を一文足して固定します。
- 構造(挙動は同じ)、読み方:遊ぶ側には影響しません。、次の手:後で自分が改造しやすい形かどうかだけ見ます。
- 表面(名前・文言・色)、読み方:ほぼ必ず揺れる前提の部分です。、次の手:固定したいなら、文言や色を仕様に書きます。
特に注目したいのは、挙動の空白です。3回とも同じ埋め方になったなら、その部分は仕様に書かなくても安定している可能性があります。割れたなら、そこは作り手が決めるべき設計判断で、書かなければ運任せになります。
仕様項目が3回とも満たされ、空白の埋め方も一致していたら、その仕様書は再現性が高いと言えます。ただし、3回だけでは傾向しか分かりません。「この仕様書なら安定していそうだ」と読むにとどめ、断定しないでください。
構造の層が揺れても慌てる必要はありません。ゲームの遊び心地は挙動で決まります。構造の揺れが問題になるのは、後から機能を足す場面です。3つのうちどれが自分に読みやすいかを、改造のしやすさの観点で選んでください。
起こりやすい誤り
実験を台無しにしやすい点を挙げておきます。
- 同じ会話の中で3回続けて頼むこと。前の出力が文脈に残り、2回目以降は1回目に引きずられる可能性があります。必ず新しい会話で行います。
- 仕様書にサンプルコードを入れること。コードが固定の手がかりになり、再現性を測るはずが、写経の精度を測ることになります。
- 行数だけで差を判断すること。コメントや改行の入れ方で行数は変わります。行数は参考値にとどめます。
- 動かさずに読むだけで済ませること。見た目が似たコードでも、入力検証の一行で挙動が変わります。
- 関数検出の限界を忘れること。上のスクリプトは function 名前 の形だけを拾います。const check = () => {} のような書き方は、関数としては数えられず、変数として数えられます。関数の数は、コードを読んで補正してください。
- 動かないコードを直してから保存すること。直した時点で、それは生成結果ではなく自分の編集になります。
次の行動
まず上の仕様書を使って、新しい会話で3回生成し、run1〜3.html を保存してください。挙動の表を埋めたあと、揺れた項目だけを仕様書に書き足して、もう一度3回試すと、書き足しが効いたかを確かめられます。安定した部分と、毎回書き添えるべき部分の一覧ができれば、次に自分のゲームを作るときの仕様書の雛形として使えます。

