「ターミナルで遊べる某有名JRPG風のドローポーカーを作って」。個人開発でAIエージェントに渡した指示はこれだけだった。

テキサスホールデムのようなポーカーには馴染みがない。好きなのは子どもの頃に某有名JRPGのカジノで遊んだドローポーカーで、ダブルアップがあるのも気に入っている。指示が「某有名JRPG風」だったのは、たまたまではなくこの好みからだ。

できあがるまでの数日で、AIエージェントはシリーズごとのルール差を質問し返し、役を揃えた回数やダブルアップの成功数に応じてバッジが増える実績システム(いわゆるゲームのアチーブメント機能)を追加する段になると、使っていたスキルの判定でADR(Architecture Decision Record)が生成され、アーキテクチャが作り直されていた。 途中で紛れ込んだ商標名は、こちらが気づいてAIエージェントに指示して消させたもので、これは人間側の判断だった。 人間がやったのは、仕様の質問に答えることと、商標名に気づいて直させたこと、要所でモデルを切り替えることだけだった。

作ったのはdraw-poker というNode.js製のCLIアプリで、コインを賭けて手札を交換し、役を揃えて配当を得るドローポーカーだ。

依存管理を増やしたくなかったので、言語はTypeScriptではなくJavaScriptにする、という制約だけは自分で決めていた。 package.jsondependenciesは一つもなく、pnpm buildnode --checkで構文を確認するだけでトランスパイルもバンドルもしない。 AGENTS.mdにも「ユーザーが明示的に求めない限り依存ゼロを保つ」という実装ノートが残っている。 TypeScriptにすれば型定義パッケージやtsconfig、ビルドステップが増える。ドローポーカー1本のためにその管理コストを払う理由はなかった。

起動時に表示されるドローポーカーのメイン画面とペイテーブル

この記事で分かること

  • grill-with-docsスキルが、一言の指示からシリーズごとの仕様差をどう引き出したか
  • 放っておいたらgrill-with-docsの判定でADRが生まれ、イベント駆動アーキテクチャへ作り直された経緯

「某有名JRPG風のポーカーを作って」に、シリーズを問い返された

最初に使ったのは、ちょうどXで見かけて使い始めていたgrill-with-docs というスキルだった。

Matt Pocockのmattpocock/skillsに収録されている、有名な部類のスキルだと思う。 計画を既存のドメインモデルに照らして問い詰め、決着した用語をCONTEXT.mdに、後戻りしにくい決定をADRに残していく。 一度に一問ずつ聞いてくるのが特徴で、こちらが答えるまで先に進まない。

「某有名JRPG風のポーカーを作って」という指示に対して、AIエージェントはすぐには作り始めなかった。 某有名JRPGのカジノはナンバリングごとにペイテーブルやハンドの扱いが違う、と指摘し、どのタイトルの仕様に寄せるか質問してきた。

正直に言うと、そのときどのタイトルを挙げて答えたかは覚えていない。 一番メジャーなタイトルを基準にしたと思うが、記録には残っていない。

その結果、AGENTS.mdの実装ノートには次のような記述が残った。

Any pair returns Pair (rank 1) — a famous JRPG’s casino rules, no Jacks-or-Better distinction.

最低役はワンペアで、役の強さに関係なく無条件に配当が出る。 一般的なビデオポーカーの「Jacks or Better」(Jペア以上でないと配当なし)とは違うルールで、これは自分で決めたのではなく、AIエージェントが「どのルールにするか」を聞いてきた末に確定したものだ。

ペイテーブルも同様で、最大ベット時のロイヤルストレートフラッシュだけ突出したボーナス配当になっている。

1×コイン5×コイン10×コイン
ロイヤルストレートフラッシュ50025008000
ストレートフラッシュ1005001000
フォーカード50250500
フルハウス1050100
ワンペア1510

10コインベット時のロイヤルストレートフラッシュだけ、単純な10倍(500×10=5000)ではなく8000という「マックスベットボーナス」が付く。 これも一言の指示からは出てこない仕様で、シリーズの実装を踏まえてAIエージェントが提案してきた。

商標のまずさに気づいて、AIエージェントに公開ドキュメントから消させた

実装の途中では某有名JRPGの名前がそのまま使われていた。 商標的にまずいと気づいて指示すると、AIエージェントはREADME.mdCONTEXT.mdAGENTS.mdから該当する言及を削除した。

量産フェーズは安いDeepSeek、判断が要る場面はCodexのGPT-5.5

このプロジェクトでは、機能を量産する場面と、リファクタリングやレビューのように判断が要る場面で使うモデルを分けた。 普段の実装はoh-my-pi経由で安価なDeepSeek V4 Flashを使い、main()の分割やADR起点の設計変更、コードレビューのような場面ではCodex CLI経由でGPT-5.5に切り替えた。

2つのハーネスを併用していたので、リポジトリには.omp/mcp.json.codex/config.tomlの両方があり、context7codegraphserenaという同じMCPサーバー構成がほぼそのまま重複している。

refactor: split main() into focused phase functionsや、後日のrefactor: improve robustness, extract testable functions, fix ADRといったコミットは、判断の比重が大きい作業だ。 コミットのたびにどちらのモデルを使ったかまでは記録していないが、機能追加より設計判断が絡む場面でモデルを切り替える、という運用は以前書いたAIサブスクの使い分け と同じ発想だ。 サブスクの間だけでなく、ハーネスそのものも作業の性質で切り替えていた。

放っておいたら、grill-with-docsの判定でADRが生まれ、イベントバスへ作り直されていた

grill-with-docsはADRを乱発しない設計になっている。 「後戻りしにくい」「文脈がないと驚く」「本物のトレードオフの結果」という3条件を満たすときだけADRを提案する。 このプロジェクトには3本のADRがあり、データ永続化の置き場所、ダブルアップのルール、そして実績システムのアーキテクチャを扱っている。

一番作り込まれているのは3本目のADRだった。 実績を30個(ハンド系10、ダブルアップ系5、累計系5、マイルストーン系4、チャレンジ系6)追加する話になったとき、AIエージェントは「70行のmain()にこのまま実績チェックを差し込むと破綻する」と自分で判断し、イベントバスへのリファクタを提案してきた。

実装は自作の15行程度のモジュールで、Node標準のEventEmitterは使っていない。

const listeners = new Map();

export function on(event, fn) {
  if (!listeners.has(event)) listeners.set(event, new Set());
  listeners.get(event).add(fn);
}

export function off(event, fn) {
  const set = listeners.get(event);
  if (set) set.delete(fn);
}

export function emit(event, data) {
  const set = listeners.get(event);
  if (set) {
    for (const fn of set) {
      fn(data);
    }
  }
}

main()handleBetplayDrawhandlePayoutという3つのラッパー関数に分割され、それぞれがbet:placedhand:evaluatedなど14種類のイベントを発行する。 実績を管理するachievements.jsはこのイベントを購読するだけの独立したモジュールで、ゲームの進行ロジックには一切触れない。

ADRにはNode標準のEventEmitterを選ばなかった理由も書かれている。 「使わないremoveListenerやエラー処理を含む400行超のAPIより、このゲームに必要な機能だけを持つ15行の自作のほうが分かりやすい」という判断だ。 実績チェックをmain()に直接書き足す案、コールバックだけで済ませる案も検討した上で外れている。

このADRを書くようにと指示した覚えはない。 実績システムを頼んだら、後戻りしにくい設計判断が発生し、grill-with-docsの条件に当てはまったのでADRが生まれた、という順番だ。

ダブルアップ画面でハイ&ローのカードを選ぶ様子

人間がやったのは、答えることと、直させることと、切り替えることだけだった

「ターミナルで遊べる某有名JRPG風のポーカーを作って」という一言から、シリーズごとの仕様差の質問と実績システムのアーキテクチャ変更は、実質的にAIエージェント側の判断で進んだ。 商標だけは、こちらが気づいてAIエージェントに指示を出した。

人間がやったことを数えると3つしかない。 grill-with-docsが投げてくる質問に答えたこと、ドキュメントに残った商標名に気づいて直させたこと、そして機能量産と設計判断とで使うモデル・ハーネスを切り替えたことだ。

3本のADRとCONTEXT.mdという形でプロジェクトの決定が文書として残っているので、数ヶ月後に自分で読み返しても「なぜこうなっているか」を追える状態になっている。 コードはGitHub で公開しているので、遊んでみてほしい。