第6章 ゲーム内容の作り込み/2 本目 / 全 10/約25分

札や敵などの中身を増やす

こより

壊れるのは、手で数字を書いた場所だけ。見つけたらデータから作り直してみて。

案内役 こより
この術でできるようになること

種類が増えても壊れない

画像は押すと原寸で開きます。

なぜ要るか

前の術でデータの形にしました。では増やしてみます。 山を1〜5の5枚から1〜7の7枚にして、配る枚数も3枚から4枚にします。

変えるのは2つの数字だけです。DECK_SIZE が山に入れる札の枚数、DEAL が最初に配る枚数です。

やってみると分かりますが、データを足しただけでは壊れます。 壊れる場所は決まっていて、手で数字を書いた場所です。

この術は、そこを1つずつ潰す回です。

使う道具: なし

AIプロンプト

この術の作業をAIへ任せるときは、これをそのまま貼ってください。

DECK_SIZE を7、DEAL を4にして。takerOf と chooseCpuMove は変更しないで。
画面の文字と手番の数をデータから作る形に直して。

takerOf と chooseCpuMove の部分は、自分のゲームで変えたくない関数の名前に置き換えてください。

山5枚を7枚にした結果

先に見せます。データを2つ変えただけの前後です。

山5枚・3枚配りのとき。

ゲーム画面。サブタイトルは「山5枚から3枚・3手勝負」、相手の手札が3枚

DECK_SIZE を7、DEAL を4にしたあと。

ゲーム画面。サブタイトルは「山7枚から4枚・4手勝負」、相手の手札が4枚

画面の文字、配られる枚数、手番の数、相手の思考、全部が付いてきています。 コードは触っていません。

これが自動で付いてくるようにするのが、この術の中身です。

数字を手で書いた場所が全部バグになります

いちばん分かりやすいのが画面の文字です。

<p class="sub">山5枚から3枚・3手勝負</p>

山を増やした瞬間、この表示は嘘になります。 5枚でもないし3手でもありません。

だからデータから作ります。

el('subtitle').textContent = '山' + DECK.length + '枚から' + DEAL + '枚・' + DEAL + '手勝負';

el('subtitle') は id で画面の部品を取ってくる書き方で、textContent はその中の文字を差し替える指示です。+ は文字をつなぐ記号なので、この1行は「いまの山の枚数と配る枚数から説明文を組み立てて、画面に入れ直す」という意味になります。

山そのものも、枚数のデータから作れます。

var DECK = [];
for (var d = 1; d <= DECK_SIZE; d++) DECK.push({ id: 'c' + d, label: String(d), power: d });

手番の数を 3 と書かないのも要点です。 このゲームは1手番に1枚置くので、配る枚数=手番の数です。DEAL から出せば、配りを変えても勝負の長さが勝手に付いてきます。

これは記事の外でも同じで、この講座のサイトも看板に出る「◯◯の術」の数を手書きしていません。術を1本増やすと、看板の数字が勝手に変わります。 数字を手書きすると、増やすたびに直す場所を探す作業が生まれます。

増やすと壊れる場所の一覧

僕が実際に踏んだものです。同じ順で壊れます。

壊れる場所 症状 直し方
画面の文字 「山5枚から3枚」のまま データから作る
手番の判定 3手で終わる/終わらない DEAL を使う
手札の並び 横に溢れてはみ出す flex-wrap: wrap を入れる
相手の思考 増えた札を考慮しない 手札を全部見て点数を付ける形にする

最後の1つが盲点です。 3-4で作った相手の思考が「3枚のうちから選ぶ」前提で書かれていると、4枚になっても3枚しか見ません。

いまの形はこうなっています。手札の枚数に依存していません。

function legalMoves(hand) {
  var moves = [];
  hand.forEach(function (cardId) {
    TARGETS.forEach(function (t) { moves.push({ card: cardId, target: t.id }); });
  });
  return moves;
}

この関数は、手札1枚ごとに的の数だけ組み合わせを作り、「打てる手の一覧」として返します。枚数を数字で書いていないので、手札が3枚でも4枚でもそのまま通ります。

残っている手札を全部並べて点数を付ける形なら、枚数が変わっても動きます。 3枚×的3つで9通りだった合法手が、4枚×的3つで12通りになるだけです。

データの間違いを起動時に見つける

増やすときに一番多いのが、書き写しの間違いです。idの重複、powerの書き忘れ、そして今回増えたのが「配る枚数を山より多くしてしまう」です。

どれも黙って壊れます。だから起動時に見ます。

function checkDeck() {
  var errors = [];
  var seen = {};
  DECK.forEach(function (card) {
    if (seen[card.id]) errors.push(card.id + ': idが重複');
    seen[card.id] = true;
    if (typeof card.power !== 'number') errors.push(card.id + ': powerがない');
  });
  if (DEAL > DECK.length) errors.push('配る枚数が山より多い');
  return errors;
}

見ているのは3つです。同じidの札が2枚ないか、power が数字として入っているか、配る枚数が山の枚数を超えていないか。おかしい所を見つけるたびに errors へ1行ずつ足していき、最後にその一覧を返します。何も無ければ空の一覧が返るので、「中身が0件なら正常」と判断できます。

起動時に呼んで、間違っていたら画面に出して止めます。

var deckErrors = checkDeck();
if (deckErrors.length) {
  el('msg').textContent = '札のデータが壊れています: ' + deckErrors.join(' / ');
} else {
  // ここから普通の起動
}

僕が試しに壊してみたとき(山3枚のまま5枚配ろうとした)は、こう出ました。

札のデータが壊れています: 配る枚数が山より多い

遊べる状態で黙って壊れているより、起動しない方が親切です。 原因がその場で分かります。

やってみる

  1. データを2つ変える

    var DECK_SIZE = 7;
    var DEAL = 4;
    
  2. 手で書いた数字を探す

    grep -nE "3枚|3手|全 3" index.html
    

    grep は、ファイルの中から指定した文字を探して、見つかった行を行番号つきで出すコマンドです。ここでは手書きで埋め込んだ「3枚」「3手」が残っていないかを探しています。

    出てきた場所を、データから作る形に置き換えます。

  3. データから作り直す

    画面の文字と手番の数を DECK.lengthDEAL から出します。上のコードのとおりです。

  4. チェックを入れる

    checkDeck を足して、起動時に呼びます。わざと配る枚数を山より多くして、止まるか確かめてください。

  5. 遊んで確かめる

    open index.html
    

    見るのはこの4つです。

    • 画面の文字が「山7枚から4枚・4手勝負」になっているか
    • 配られる手札が4枚か
    • 4手で決着するか
    • 手札がはみ出していないか
  6. 保存する

    git add .
    git commit -m "山と配る枚数をデータにして、画面と手番を追従させた"
    git push
    

つまずきどころ

手札が横にはみ出す

flex-wrap: wrap を入れてください。

.myhand { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

flex-wrap: wrap は「横に並べた部品が入り切らなくなったら、次の行へ折り返す」という指定です。これが無いと、札が増えたときに画面の外へ押し出されて見えなくなります。

増やしたら読み合いが薄くなった気がする

正しい感覚かもしれません。山が大きいほど、相手の手札は読めなくなります。 対策は2つあります。

どちらが良いかは、6-6で数字を見て決めます。 ここで勘で決めないでください。

増やしたら急に簡単/難しくなった

起きます。種類を増やすとバランスは必ず動きます。 これも6-6で測ります。

AIに増やさせたら、判定のコードも書き換えられた

頼み方の問題です。

claude -p "DECK_SIZE を7、DEAL を4にして。takerOf と chooseCpuMove は変更しないで。
画面の文字と手番の数をデータから作る形に直して。"

「変更しないで」を明示するのが効きます。 データだけ変える作業なのか、仕組みも直す作業なのかを分けて頼んでください。

Windows の場合

grep は PowerShell では Select-String です。

Select-String -Path index.html -Pattern "3枚|3手"

次の一歩

DECK_SIZEDEAL をいろいろ変えて開いてみてください。

山10枚から2枚だけ配ると、ほぼ運のゲームになります。山4枚から4枚配ると、全部見えている読み切りのゲームになります。データを2つ変えるだけで、別の遊び味を試せる——これがルールをデータで持った成果です。

中身が増えても壊れない形になりました。次は増えた画面を、説明なしで操作できる形に整えます!

ここまでの三つの的

この講座は、見本ゲーム「三つの的」を術ごとにすこしずつ育てながら進みます。下にあるのはこの術を終えた時点の実物です。そのまま遊べます。

別の画面でこのゲームを開く

術の一覧へ戻る

メルマガ(無料)忖度なしのAI事情を、メールでお届けします。ご登録の方に、プレゼントをお渡ししています