目次
- はじめに
- 作ったもの
- 出来事は、書式に書けば増える
- 統計どおりか、確率が動いていないか
- 浪人できないテストと、時間切れのテスト
- 共有ボタン
- 遊ばれた人生の数を数える
- Claude Code への頼み方
- 公開してから直したもの
- まだ直していないもの
- おわりに
はじめに
「12万分の1 — 野球人生シミュレーション」というブラウザゲームを、Claude Code で作って公開しました。野球人生を、公開データにもとづく確率で最後まで進めるゲームです。
私はプログラミングの初心者です。アプリを作ってみたかったのと、人に使ってもらって、フィードバックをもらいながら直す体験をしてみたかったのが理由です。
コードを書かずに作った、と書くと少し格好いいですが、正確には、書けないので書いていません。仕組みの多くは Claude Code が考えて、作って、報告してくれたもので、私はそれを読んで「はい」か「待って」を言っていました。
この記事は、その「何を作り、何をやったか」の記録です。何を考えていたかは note に書きました。
作ったもの
| 分野 | 中身 |
|---|---|
| 人生 | 生まれてから亡くなるまで。高校→大学・社会人・独立リーグ→プロ→引退後。浪人、ドラフト、FA、トレード、ポスティング。多種多様な野球以外の人生 |
| 遊びの幅 | ☆1〜☆10 の才能を選ぶポテンシャルモード、海外出身モード、出来事は約1000件(1.6.0 で職業16種、出来事約240件を追加)、親子2代の野球 |
| データ | 公式の資料を優先。実在の学校・球団名は、出典と確認日があるものだけ。選手は架空 |
| そのほか | 途中保存と「続きから」、X への共有、4言語、ダークモード、ご意見フォーム |
構成は、Vite + React + TypeScript の静的サイトを Cloudflare Pages で公開し、保存はブラウザの localStorage だけです。端末を変えると人生は引き継がれません。生まれ変わりの仕様としては、わりと正しい気がしています。
1年ごとの流れは、だいたい次のとおりです。
出来事を抽選 → 選ぶ(自分で / おまかせ)→ 結果を能力・お金・家族などに反映 → 次の年へ
乱数には seed(種)を付けていて、同じ seed なら同じ人生が再現されます。
アイデアは「80億分の1」からお借りしました。規約(アイデアから自分の作品を作るのは自由、プログラム・文章・絵の複製と改変は禁止)に従い、画面の並びと操作の流れを一から作り直しています。
技術選定の欄が、ほぼ空欄な理由
技術の判断は、ほとんどしていません。判断できるほど知らなかった、というほうが正確です。
計画書には「既存の React + TypeScript + Vite のプロジェクトを使う」と書いてあります。書いてある欄は「ユーザー指定の要件(必ず守る)」で、ユーザーは私です。ただ、「既存の」とあるとおり、そこにあったものを使っただけです。
公開先は、GitHub Pages、Cloudflare Pages、Netlify などの候補から Cloudflare Pages にしました。なぜかと聞かれたら、候補の一覧に載っていたから、と答えるしかありません。
出来事は、書式に書けば増える
1.6.0 で出来事を約240件まとめて足せたのは、決まった書式に書けば、自動でゲームに組み込まれる仕組みにしてもらったからです。実際の定義を、少し削って載せます。
{
id: 'x.sns.flame',
stages: ['adult'],
ages: [20, 64],
maxPerLife: 1,
title: '投稿が燃えている',
body: '何気なく書いた短い投稿が、意図とは違う意味に受け取られて広まりました。…',
choices: [
{ label: '言い訳をせず、すぐに謝る', flag: 'talk.flame.sorry', effects: { happy: -3, rel: 1 },
outcomes: [{ follow: [{ id: 'x.sns.flameBack', after: 1, within: 2 }], text: '…' }] },
// …(ほかの選択肢)
{ label: '何もせず、画面を閉じる',
outcomes: [{ text: 'スマートフォンを伏せて置き、その夜は早く寝ることにしました。' }] },
],
}
起きる年齢、1人生に何回までか、選択肢ごとの効果、1〜2年後の後日談(follow)を書いておくと、compileSpec がゲームの中の出来事に変換します。
このゲームでは、ネット炎上にも年齢制限があります(20〜64歳)。
統計どおりか、確率が動いていないか
統計どおりかどうかは、テストで確かめています。高校で3年間続ける率(90.3%)のような段階ごとの率を、調整に使っていない40万人の人生で測り、目標の範囲に入っているかを判定するテストを作ってもらいました。
もう1つが「指紋」です。決まった seed で10万人分の人生を走らせ、1人1行の結果を残しておきます。コードを変えたら同じ10万人をもう一度走らせて比べ、差が0行なら、誰の人生も変わっていません。
「これで確率は守れる。完全に理解した」
そう思っていたら、人生の記録に文章を1行足すだけで、乱数を1つ余分に使ってしまう作りになっていました。そのあとに引く能力の値がずれ、判定がずれ、進路がずれます。文章を1行足しただけで、その人は少し別人になります。
「なにもわからない」
新しく足す文章や出来事には、専用の乱数を使うようにしてもらいました。
高校の大会を、学校のある都道府県で戦うように直したときは、39人の指紋が変わりました。その変更だけを戻すと0人だったので、原因が確定し、私が承認して基準を作り直しました。39人には、新しい人生を生きてもらっています。
結婚も測りました。「待つ」を選ぶたびにプロポーズのやり直しが来るようにしたら、50歳時点の未婚率が31.23%から24.62%に下がり、基準の28.25%(±3)を外れました。今は、おまかせで結婚が増える変更には私の承認が要ります。本人たちの合意だけでは足りません。
浪人できないテストと、時間切れのテスト
浪人の分岐は、テストで一度も通っていませんでした。テストがいつも既定の選択肢を選んでいて、既定の選択では誰も浪人しないからです。
「浪人してもいいですか?」
🦖<だめ
わかりましたティラノサウルス
テストの中では、毎回このやりとりで終わっていました。難しい志望校をわざわざ選ぶテストに直して、ようやく浪人してもらえました。
テストが1,000件を超えると、時間切れで落ちるテストが出てきました。Claude Code の調べでは、20個のワーカーで並列に動かし、CPU とメモリを取り合っていたのが原因でした。制限時間は延ばさず、並列の数を変えて測ってもらいました。
| 並列数 | 時間 | 失敗 |
|---|---|---|
| 20 | 8.5分 | 4件 |
| 10 | 8.2分 | 1件 |
| 6 | 8.8分 | 0件 |
6にしました。18秒延びて、失敗は4件消えました。
共有ボタン
結果を X に投稿する文は、X の数え方(日本語1文字を2、リンクを23と数え、上限280)に収めます。収まらないときは、確率→称号→問いかけの順に削り、リンクは必ず残します。
統計のゲームなのに、真っ先に削るのは確率です。
スマホでは、ブラウザ版の X はログイン画面になり、リンク投稿には画像も付けられませんでした。そこで、端末の共有メニュー(navigator.share)に画像と短い文を渡す方式を主にしました。PC では、カード画像をクリップボードに入れて貼れるようにしています。
遊ばれた人生の数を数える
ホーム画面に「これまでに作られた人生の数」を出しています。ここだけはサーバーが要るので、Cloudflare Workers と D1(データベース)を使いました。無料プランで始められることを、公式の料金表で確かめてからです。
- 人生を始めるときに ID を送り、同じ ID は何回届いても1人と数える
- 許可したサイトからしか呼べない
- 送れなかったら ID を手元に残し、次に開いたときに送り直す
公開後、このカウンターが止まりました。遊んでくれる人が一気に増えたからです。前の日は約30人だった登録が、24時間で約3万件届きました。D1 の無料枠は読み取り1日500万行ですが、約719万行を読んだところで止まっています。止まっている間の分は読めていないので、本当に読もうとしていた量はもっと多かったはずです。
調べてもらうと、登録のたびに「直近1分の登録数」を数える作りでした。遊ぶ人が増えるほど1回に読む行も増え、混雑時は1回で最大約600行を読んでいました。
遊んでもらえるほど、数えるのが大変になる仕組みです。うれしい悲鳴を、データベースがあげていました。
10秒ごとの集計表を読む方式に変えて、1分に2,000件送る検証では、1件あたりの読み取りが平均約1,000行から約7行に減りました。
Claude Code への頼み方
決めた範囲の中は、細かい確認で止めずに進め、段階の最後にまとめて報告してもらいました。その代わり、削除や上書き、本番への公開、承認済みの仕様の変更、指紋の基準の更新は、必ず先に聞いてもらいます。
検証は待ち時間を減らすために最小限にし、指紋の比較も関係するときだけにしました。
「関係するテストは通った。ほかは見ていないけど、ヨシ!
」
……とならないように、飛ばした検証は報告に「未検証」と書いてもらいます。
Claude Code は、範囲を超えそうなときに自分で止まることができます。止まらないこともあります。
文章の食い違いを直してもらったときは、計画では5ファイルだった修正が13ファイルに広がっていました。全文章を状態と照らし合わせる走査で候補は7,514件から712件に減り、そのうち646件は判定の誤検出でした。
いちばん食い違っていたのは、食い違いを探す仕組みでした。
範囲の外の変更が混ざると、初心者の私には何が変わったのか追いきれません。そこで止めて、差分と範囲外の修正の一覧を出してもらい、公開前には、ファイルを書き換えない約束で、もう一度点検してもらいました。
利用上限で止まったときは、新しいセッションに計画書を読ませて引き継ぎました。前の日の私が何を頼んでいたかは、私より計画書のほうが詳しく覚えていました。
公開してから直したもの
フィードバックの中に、68歳で160キロを投げている、というものがありました。
独立リーグや社会人野球にいる間のドラフトの判定に、年齢の上限がありませんでした。戦力外になるたびに独立リーグを選ぶと、60代まで指名され続けます。スカウトの熱意だけは本物です。
しかも球速の表示は23歳から変わらない作りで、その選手は45年間、球速が1km/hも落ちていなかったことになります。30歳を超えた人と2回目以降のドラフトでは指名しないようにし、球速の表示も31歳から下がるようにしました。
版は、10月22日の1.1.0から10月24日の1.6.0まで、2日で5つ進みました。私のほうは、たぶん1.0.1くらいです。
まだ直していないもの
1.6.0 の時点では、ゲームの中の失業給付は、実際には支払われていません。
仕事を探す場面では、どの選択肢を選んでも必ず何かの仕事に就く作りなので、給付をもらう暇がありません。仕様です。……と言えたら楽なのですが、不具合です。
直すと貯金や生涯年収の数字が動くので、保留中です。
おわりに
コードはほぼ書いていませんし、技術もほとんど選んでいません。やっていたのは、報告を読んで「はい」か「待って」を言うことでした。
AI に書いてもらうほど、何を変えてよくて何を変えてはいけないかを決める仕事が、手元に残りました。
「Qiita を書き終えたので、note のほうも読んでもらっていいですか?」
🦖<いいよ
ありがとうティラノサウルス