2026/06 - 現在
X連動型AIゲーム(個人)
TypeScript React SQLite Drizzle Tailwind Railway
近畿の「ポップな妖怪」を毎日繰り出して合戦し、X のいいね=育成・リプライ=投票として観客の介入をゲームに取り込む。
Xアカウント: https://x.com/ayakashi_kassen
観戦 Web UI: https://ayakashi-kassen.up.railway.app
概要
- 妖怪は 卵 → 孵化 の流れで X に登場。卵への返信で「どの職に孵るか」を投票でき、誕生投稿への1いいねでランダムなステが1%開眼(複利・上限なし)
- 1日 8 取組のチーム戦(最大3vs3・ターン制オートバトル)を日次バッチで先計算 + 実況を1プロンプトで一括生成し、約3時間おきに1番ずつ X へ公開して勢力ゲージを動かす
- シーズン(場所)制で星取表を積み上げ、引退した妖怪はヤジ・語り部として実況に残る
- X 連携は集計締切方式(リプライ/いいねは締切時に1回だけ取得して食わせる)で pay-per-use 課金を抑える
設計のポイント
- イベントソーシング + ステップ粒度の進行 — 1 ステップ = 1 トランザクションで
eventsと投影を同時コミット。eventsだけから投影と進行カーソルを decisive に replay できるので、任意の tick へロールバック可能 - LLM 障害時はフォールバックで完走させない — UsageLimit に当たった tick は最終スナップショットへ巻き戻し、「偽の1日」を永続化しない
- リプライは untrusted input 前提 — X 経由の文字列を prompt injection の媒介として扱い、スキーマ強制 + 検証で job 投票だけを抽出
- 揺らぎは汎用ローテ基盤に集約 — ハッシュタグ揺らぎ・口上の署名区切りなどを
pickRotation(state.rotationCursors, key, pool)で名前空間ごとに管理し、RotationAppliedを投影して replay 可能に。場当たり乱数で切り替えるコードを増やさない - watch-only の公開設計 — 状態を変える HTTP エンドポイントは存在しない。進行は自走ワーカーだけが行う
Xのシャドウバン
別アカウントからの検索でヒットしない、いわゆる「シャドウバン」が発生した。ハッシュタグを除いたり、投稿日時を動的にずらすなどの工夫をしたが、改善せず。アカウント若さのペナルティが入っている可能性があるため、一旦様子見でPendingとしている。
規模感
- 期間: 2026/06/07 〜 公開運用中(同月内に X 公開まで到達)
- コミット: 250+
- TypeScript / TSX: 180ファイル・約30,000行
- 1人開発(世界設計・実装・キャラ絵生成・X 連携・運用すべて)