ハトネコエ Web がくしゅうちょう

プログラミングやサーバー・Web制作、チームマネジメントなど得た技術のまとめ

Claude Fable 5 を使って麻雀ウェブアプリを作ってみたのでその記録メモ

指示

4人対戦の麻雀ができるWebアプリケーションを作って。東風戦と半荘戦に対応。
プレイヤーは1人で、他の3人はCPU。
CPUのキャラ設定は、「振り込みがちな初心者」「守りが堅い中級者」「打点高めを狙う中級者」「バランスの取れた中級者」「プロレベルの上級者」から選べるようにしたいです。

ユーザーへのヒアリング

Plan mode で指示したので確認があった。

ルールをユーザーが設定画面から選べるようにする方針もアリかなーと思ったけど、
実装難易度上げて完成しないのもちょっとヤダかなー、と思って、素直に全て推奨項目を選択した。

プランエージェントへの指示

プラン作成にあたって Claude Code は以下のような指示をプランエージェントに(?)投げていた

あなたはソフトウェアアーキテクトです。以下の要件で4人対戦リーチ麻雀Webアプリの詳細な実装計画を設計してください。

## 要件
- 場所: ~/Programs/fable-5-try/mahjong-app(空ディレクトリ、グリーンフィールド)
- 技術スタック: Vite + React + TypeScript、全てクライアントサイド完結(サーバー不要)
- 4人対戦: 人間1人 + CPU 3人
- 東風戦と半荘戦を選択可能
- CPUキャラ設定(ゲーム開始時に各CPUへ割当可能):
  1. 振り込みがちな初心者
  2. 守りが堅い中級者
  3. 打点高めを狙う中級者
  4. バランスの取れた中級者
  5. プロレベルの上級者
- ルール: 標準的なリーチ麻雀フル実装
  - 喰いタンあり・後付けあり・赤ドラ3枚(5萬5筒5索 各1枚)・裏ドラ・カン裏あり
  - 鳴き(ポン・チー・カン[暗槓/明槓/加槓])、リーチ、フリテン(同巡フリテン・リーチ後フリテン含む)
  - 流局: 通常流局(ノーテン罰符)、九種九牌、四風連打、四家立直、四槓散了、流し満貫
  - 連荘、本場、供託、トビ終了、オーラス和了止めは任意
  - 役: 全標準役(1翻〜役満、ダブル役満は任意)、符計算、点数計算(切り上げなし標準)

## 設計してほしいこと
1. **ディレクトリ構成とモジュール分割**: ゲームエンジン(純粋ロジック、React非依存)とUI層の分離方針
2. **コアデータ構造**: 牌の表現(数値エンコーディング推奨か)、手牌・河・副露の表現、ゲーム状態マシン(局の進行: 配牌→ツモ→打牌→鳴き判定→...→和了/流局)
3. **アルゴリズム設計**:
   - シャンテン数計算(標準形・七対子・国士無双)の実装方針
   - 和了判定・面子分解・役判定・符計算・点数計算の設計
   - 待ち牌(テンパイ時の和了牌)列挙
4. **CPU AI設計**: 5キャラの性格をどうパラメータ化するか
   - 共通基盤: 牌効率(シャンテン数を下げる打牌選択)、安全牌判定(現物・スジ・壁・ワンチャンス)
   - キャラ差分: 押し引き閾値、ベタオリ判定、鳴き頻度、打点志向(ドラ・役狙い)、リーチ判断などのパラメータ表を具体的に提案
   - 初心者キャラは「危険牌を平気で切る」「テンパイ即リー」など自然な弱さの表現方法
5. **ゲームフロー制御**: 人間の入力待ち(打牌・鳴き選択・リーチ宣言・和了宣言)とCPUの自動行動を統合する設計(async/await ベースか、イベントキューか、ステートマシンか)
6. **UI設計**: 麻雀卓レイアウト(自分の手牌は下、対面・左右のCPU)、牌の描画方法(Unicode麻雀牌文字 vs CSS/SVG自作)、アクションボタン(ポン/チー/カン/ロン/ツモ/リーチ/パス)、点数表示・結果画面
7. **実装フェーズ分割**: 段階的に動くものを作る順序(例: エンジン→AI→UI)とマイルストーン
8. **テスト方針**: シャンテン計算・点数計算など複雑なロジックのユニットテスト(Vitest)

既存ライブラリ(riichi 等の npm パッケージ)を使うか自作するかの判断も含めてください。自作推奨の場合はその理由を。教育目的というよりは「ちゃんと動く完成品」が目標です。

返答は日本語で、具体的なファイルパス・型定義例・パラメータ表を含む詳細な実装計画を返してください。

ライブラリの吟味

プランを作るに当たってライブラリを使用するか吟味する記録が残っていたのがおもしろかった。

ディレクトリは空(完全グリーンフィールド)であることを確認しました。以下、実装計画の全体設計です。

## 4人対戦リーチ麻雀 Webアプリ 実装計画

### 0. ライブラリ自作 vs 既存パッケージの判断 → 全面自作を推奨

| 候補 | 評価 |
|------|------|
| riichi (npm) | 文字列入力から役・点数を計算できるが、シャンテン計算・待ち列挙・フリテン・ゲーム進行は対象外。型定義が貧弱でルールオプションの細かい制御(カン裏・流し満貫・赤の枚数指定)に難あり。メンテも停滞気味 |
| syanten / shanten-number 系 | シャンテン単機能。AI が必要とする「打牌ごとの受け入れ枚数」APIがなく、結局ラップ層を自作することになる |

自作推奨の理由(完成品志向の観点で):

1. AIがエンジン内部表現を直接食う必要がある。CPU の打牌選択は「14枚から1枚切った後のシャンテン数と受け入れ枚数」を毎ターン数十回計算する。外部ライブラリの文字列I/Fを挟むと変換コストとバグ面が増える。34種カウント配列で統一すれば全アルゴリズムが同じ表現を共有できる。
2. 点数計算と面子分解は役判定・符計算と密結合(高点法のため全分解を列挙して最大を取る)。ここを外部に出すと「平和+ツモ20符」「七対子25符」などの境界でルール差異の調停が発生する。
3. アルゴリズム自体は枯れており(シャンテン数のバックトラック法、標準形分解)、テストベクタで品質担保できる。フル実装でもコア部は合計 2,500 行程度。

依存は Vite + React + TypeScript + Vitest + Zustand の最小構成。牌画像も外部アセットに依存せず自前SVGにする(後述)。

riichi って npm が更新されていないのは調べに行ったのかな? と思ったけど、
検索してライブラリのページを見に行ったら最終更新が5年前だったので、検索せずに答え出した気がする。
(仮にここ数日以内に更新されていた場合には、更新履歴を見に行かなかったことで少し誤りに近い判断となっているのかもしれない)

作成されたプラン

# 4人対戦リーチ麻雀 Webアプリ 実装計画

## Context

`~/Programs/fable-5-try/mahjong-app`(空ディレクトリ)に、人間1人 + CPU 3人で遊べるリーチ麻雀Webアプリをゼロから作る。

- **モード**: 東風戦 / 半荘戦を選択可能
- **CPUキャラ**: 「振り込みがちな初心者」「守りが堅い中級者」「打点高めを狙う中級者」「バランスの取れた中級者」「プロレベルの上級者」の5種から各CPUに割当
- **ルール**: 標準ルールフル実装 — 喰いタン・後付けあり、赤ドラ3枚、裏ドラ・カン裏、鳴き(ポン/チー/カン全種)、リーチ、フリテン(同巡・リーチ後含む)、途中流局(九種九牌・四風連打・四家立直・四槓散了)、流し満貫、連荘・本場・供託・トビ終了、全標準役 + 符計算 + 点数計算(切り上げ満貫なし)
- **技術**: Vite + React + TypeScript + Zustand + Vitest。全てクライアントサイド完結。**麻雀ロジックは全面自作**(外部ライブラリはAI用の受け入れ計算APIがなく、点数計算と面子分解が密結合のため)

## アーキテクチャ方針

- `src/core/`(ゲームエンジン)と `src/ai/`(CPU)は **React非依存の純粋TypeScript**
- エンジンは純Reducer: `applyAction(state, action) → {state, events}` + `legalActions(state) → Map<Seat, Action[]>`
- 進行の「待ち」はエンジン外の非同期ドライバ(controller)が担当。人間入力はPromise、CPUは思考遅延つき同期計算(Web Worker不要)
- 依存方向: `ui → app → ai → core`(逆流禁止)
- シード可能PRNGでリプレイ・バグ再現可能に

## ディレクトリ構成

```
src/
├── core/
│   ├── tiles.ts              # 牌エンコーディング
│   ├── wall.ts               # 山・王牌・ドラ表示牌
│   ├── random.ts             # シード可能PRNG
│   ├── shanten/
│   │   ├── standard.ts       # 標準形(バックトラック+色別メモ化)★最難関
│   │   ├── chiitoi.ts / kokushi.ts
│   │   ├── index.ts          # 3形式min + API
│   │   └── ukeire.ts         # 打牌別受け入れ枚数(AI用)
│   ├── agari/
│   │   ├── decompose.ts      # 和了形の全面子分解列挙
│   │   ├── waits.ts          # 待ち牌列挙
│   │   ├── yaku.ts           # 全役判定 ★
│   │   ├── fu.ts / score.ts  # 符・点数計算
│   └── engine/
│       ├── types.ts          # GameState / RoundState / PlayerState / Phase
│       ├── actions.ts        # Action / GameEvent
│       ├── legal.ts          # 合法アクション列挙
│       ├── reducer.ts        # 状態遷移本体(鳴き調停・フリテン・流局)★
│       ├── furiten.ts / draws.ts
│       └── match.ts          # 連荘・本場・供託・トビ・東風/半荘
├── ai/
│   ├── types.ts / personalities.ts   # 5キャラパラメータ表
│   ├── efficiency.ts         # 牌効率
│   ├── danger.ts             # 危険度推定(現物・スジ・壁・ワンチャンス)
│   ├── value.ts              # 打点見積もり
│   └── decide.ts             # 押し引き統合 ★
├── app/
│   ├── store.ts              # Zustand: スナップショット + UIプロンプト
│   ├── controller.ts         # ゲームループ駆動 ★
│   └── settings.ts
├── ui/
│   ├── TableView.tsx
│   ├── components/           # Tile(自前SVG), Hand, River, MeldArea,
│   │                         # ActionBar, CenterInfo, ResultModal,
│   │                         # FinalResultModal, SetupScreen
│   └── tiles/glyphs.ts       # 牌SVGデータ
└── main.tsx
```

## コアデータ構造

**牌は二層エンコーディング**:
- アルゴリズム用: `TileKind` 0..33(34種)+ `Counts = Uint8Array(34)`
- 状態管理用: `TileId` 0..135(物理牌。`kind = id >> 2`、赤5は各色5の0番個体)

**Phase(局の状態マシン)**:
```
dealing → draw(actor) → [九種九牌|ツモ和了|カン→(槍槓窓)→新ドラ→rinshanDraw|打牌/リーチ]
  → discarded(クレーム窓) → [ロン(ダブロン両者成立)|明槓|ポン/チー|全員パス]
  → [四風連打/四家立直/四槓散了チェック] → [山空→荒牌流局(ノーテン罰符/流し満貫)|次家draw]
  → roundEnd → (match.tsで連荘/次局/gameEnd)
```

同時クレームは全席の応答を集めてから ロン > ポン/カン > チー の優先度で調停。

**PlayerState** は手牌(TileId[]ソート保持)・ツモ牌・副露・河(ツモ切り/リーチ/鳴かれフラグ付き)・リーチ状態(一発含む)・フリテン(permanent/temporary)・流し満貫資格を持つ。

## 主要アルゴリズム

- **シャンテン**: min(標準形, 七対子, 国士)。標準形は再帰バックトラック + 色ごと9要素部分問題のメモ化。テーブル事前計算は不要(メモヒット時 <0.05ms)
- **受け入れ**: 「各牌を足す/引いてシャンテン再計算」の素朴実装で十分高速
- **和了パイプライン**: WinContext → 全面子分解列挙 → 分解ごとに役判定 → 符計算 → **高点法で最大採用** → 点数・支払い分配。待ちの取り方(両面/嵌張/単騎...)も分解ごとに列挙(平和・符に影響)
- **符**: 副底20 + 待ち + 雀頭 + 面子符 + 門前ロン10/ツモ2、平和ツモ20符・七対子25符・喰い平和30符の特例。**切り上げ満貫なし**(7700/11600そのまま)
- **ダブル役満**(国士13面・四暗刻単騎・大四喜・純正九蓮): デフォルトON

## CPU AI 設計

全キャラ共通パイプライン: 状況分析 → 押し引き判定(PUSH/FOLD/MIXED)→ 打牌選択(受け入れ最大 or 危険度最小)→ 鳴き/リーチ/和了判断 → ミス注入。**性格はパラメータのみで差別化**。

| パラメータ | 初心者 | 守備型中級 | 打点型中級 | バランス中級 | 上級 |
|---|---|---|---|---|---|
| safetyAwareness | 0.05 | 0.95 | 0.45 | 0.70 | 0.95 |
| foldShanten | 99(降りない) | 1 | 2 | 2 | 動的EV |
| pushDanger | 1.0 | 0.10 | 0.45 | 0.30 | 0.35 |
| callEagerness | 0.8(鳴きすぎ) | 0.25 | 0.55 | 0.45 | 0.50 |
| yakuAwareness | 0.2(役無し鳴き) | 0.9 | 0.85 | 0.9 | 1.0 |
| valueOrientation | 0.1 | 0.3 | 0.85 | 0.5 | 0.6 |
| riichiStyle | always(即リー) | standard | standard | standard | damaten-aware(跳満級ダマ) |
| mistakeRate/depth | 0.25 / 3 | 0.05 / 1 | 0.07 / 1 | 0.05 / 1 | 0 |
| readsWalls | ✗ | ✓ | ✗ | ✓ | ✓ |
| considersFuriten読み | ✗ | ✗ | ✗ | ✗ | ✓ |

- 初心者の弱さは「危険牌を選ぶ」のではなく「安全を**見ない**」結果として自然に振り込む設計
- 上級は閾値でなく簡易EV(和了率×打点 − 放銃率×失点期待)+ オーラス着順条件を考慮
- 危険度: 現物0 / 字牌0.05〜0.3 / スジ0.15〜0.3 / 無スジ中張0.6〜0.8、壁×0.4・ワンチャンス×0.6

## UI 設計

- **牌は自前SVG**(Unicode麻雀牌は品質バラバラ・赤5表現不能のため不採用)。萬子=漢数字テキスト、筒子=円パターン生成、索子=竹形状、字牌=テキスト。裏向き・横向き(リーチ宣言牌・鳴き出所)はtransform
- **卓レイアウト**: 自分=下(手牌大きめ・表)、対面=上、左右CPU=縦(河はrotate(±90deg))。中央に局数/本場/供託/残り山/ドラ表示/4人の点数。固定アスペクト比でスケール
- **ActionBar**: 合法アクション時のみ ロン/ツモ/リーチ/ポン/チー/カン/パス を表示。リーチ宣言時は切れる牌のみハイライト。チー複数候補は組を提示して選択。リーチ後はツモ切り自動
- **ResultModal**: 手牌公開・役一覧・符翻・点数移動・裏ドラめくり。流局時はテンパイ者公開とノーテン罰符
- **SetupScreen**: 東風/半荘トグル、CPU 3枠 × キャラ5種ドロップダウン
- 人間に合法クレームが無い鳴き窓は即パス(プロンプトしない)。CPU思考遅延500〜1200msは演出

## 実装フェーズ

| Phase | 内容 | 検証 |
|---|---|---|
| P1 | 牌・シャンテン・分解・待ち | Vitest: ランダム手 vs 素朴全探索リファレンス照合 10,000ケース |
| P2 | 役・符・点数 | 点数表テーブル駆動テスト(全役成立/不成立 + 代表点数60ケース) |
| P3 | 局エンジン(reducer/legal/furiten/draws) | シード固定シナリオテスト(槍槓・嶺上・海底・ダブロン・各流局・フリテン) |
| P4 | match.ts + **ランダム自走1000半荘** | クラッシュ0・不変条件(牌136枚・点数+供託=100,000)assert |
| P5 | 最小UI(卓・打牌のみ) | ブラウザで1局打てる |
| P6 | フルUI(全アクション・結果画面・セットアップ) | 人間が半荘完走 |
| P7 | AI 5キャラ | キャラ自走対戦で上級>中級>初心者の着順序列を統計確認 |
| P8 | 仕上げ(アニメーション・レスポンシブ) | 完成 |

P4の自走テストが品質の要 — UIとAIより先にエンジンを統計的に叩いて稀パスのバグを潰す。

## 検証方法

1. `npm test`(Vitest): シャンテン照合・点数表・シナリオ・自走不変条件テスト全通過
2. `npm run dev` でブラウザ起動 → セットアップ画面でキャラ割当 → 半荘を人間として完走(鳴き・リーチ・カン・和了を実際に行使)
3. AI自走統計で性格差(初心者の放銃率が最高、上級の平均着順が最良)を確認

稀パスのバグを潰す

という変な日本語が誕生していた。

プラン自体は良さそうなのでそのまま auto mode で実装開始させたけれど、はたして完成するのか・・・!!!

実装を観察

平気で30分はかかっていて、「どうなるかな〜」の気持ち。

コンテキストのリミットを少し超えてもがんばってくれている・・・。

npm install は失敗し続けている……。どこかで私が手動でやらないといけなさそう。

claude-fable-5 is temporarily unavailable, so auto mode cannot determine the safety of Bash right now. Wait briefly and then try this action again. If it keeps failing, continue with other tasks that don't require this action and come back to it later. Note: reading files, searching code, and other read-only operations do not require the classifier and can still be used.

最後の方はずっと npm install を繰り返していたけど、
これは無理だとわかったのか、 ScheduleWakeup で再実行を予約していったん止まってくれた。

実装時間は60分くらい。

いったんこの状態で .gitignore だけ加え、 git init して全ファイルを commit。

Fable は git commit も許可されてないっぽい。

動作テスト

ほぼほぼ動いてた! すごい!

60分かかったとはいえ、この量のコードを記述したのすごい。
特に CPU の挙動を書いたのがすごい。そこまで出来るんだ……。

https://github.com/nekonenene/mahjong-app-with-fable-5/commit/3fb166472a11e4c567a48672102aaa82d5c9e4ef

npm コマンドの実行をおこなえないから、
テストやローカルサーバの起動の試行をできないという、
なかなかハードモードの中の実装だったけど、それなのに動くものが作れていたのがすごい。

テストプレイしてみるといくつか問題があって、最初はこんな指示で直してもらった。

いくつか問題点があります。

* 音が鳴らないため進行が少しわかりにくいです
* チーやポンを選択できるとき、どのプレイヤーの捨て牌に対しての鳴きかがわからないです
* リーチになったとき「リーチ」や「捨てる牌を選んでください」というメッセージウィンドウと手牌が重なってしまっていて、捨てる牌を選びにくいですし、自分の手牌がわからないので、何待ちかがわかりにくいです。現代の麻雀ゲームのように、どの牌を捨てると何待ちになるのか表示されるといいと思います
* ツモなどの演出がないため、CPUの誰が上がったのかがわかりにくかったです

これ含めて、麻雀部分の修正指示に関しては5回くらいで満足いくものが出来上がったから優秀だった。

鳴きのときのメッセージウィンドウと手牌がかぶる問題、
最終的に盤面を少し小さくしてメッセージウィンドウを盤面の下に表示するという解決方法をとっていて、
スマホの麻雀ゲームでは見ないUIながらも「たしかにその方法があったか!」と感心した。

完成物(リポジトリ)

リポジトリはこちら ⬇️

github.com

コミットメッセージに、どういう指示で直したか書いてるときもあるのでご参考に。

git や npm のコマンドが使えなく、Web検索できなく、MCPも使えないという、
Fable 5 はなかなかハードな環境で動作するにも関わらず、
もともとのモデルの優秀さゆえに、早くいいものを作れていた感じがある。
(この問題、Claude Desktop のバージョンアップをしたら解消した気がする)

完成物(ウェブアプリ)

リポジトリの README にも記載しているけれど、麻雀ウェブアプリはこちら ⬇️

nekonenene.github.io

綿密な動作検証をしたわけではないけれど、
東風戦を通して違和感ないくらいにはなってたので大丈夫なはず。

イラスト生成AIをガッツリ1週間さわってみたメモ

はるか昔(いつだっけ……)にさわったことあったけれど、
この1週間触ってみたのでメモ。

ComfyUI を導入

昔は Stable Diffusion Web UI がデファクトスタンダードな感じだったけど、
今は ComfyUI を使う人が多いと聞いた気がしたので ComfyUI を使ってみた。

Windows や Mac 用のアプリケーションがあるから、
ダウンロードするだけでだいたい使えるようになるのはいい感じ。

テンプレートがいっぱいあって、用途に合わせて選べば、
必要なモデルやVAE、テキストエンコーダなどのダウンロードをスタートしてくれる。
ただし、それぞれ数GBあって重たいので、まずダウンロードに時間がかかった。

マシンスペックの関係で動かないように見えるのもあるので注意。

FLUX.2 [dev] を動かそうとして全然反応がなかったんだけど、
パラメータ数が 32B (320億) もあるらしくて、
4Bや9Bのパラメータ数の FLUX.2 [klein] とかを動かすべきだったっぽい。
(参考: FLUX 2.0を32GBユニファイドメモリMacBookで使ってみた - RAKSUL TechBlog

……まあ、その FLUX.2 [klein] も動かせてないんだけど。
私の待つ時間が少ないのか、本当に動かない現象があるのかは謎。
→ その後できました。klein-4B, 20step, 800x1200px の画像で生成に約6分、長い……

ComfyUI のワークフロー(ノードをつなげる仕組み)は便利なんだろうけど、
今のところテンプレートのままでしか使えてなくてわかってない。

公式でガイドも用意してくれてるので、 ControlNet の活用の仕方とかは覚えたい……。

https://docs.comfy.org/ja/tutorials/controlnet/controlnet

ComfyUI で作ってみる

イラストを生成するにあたって Nova Anime XL を使ってみた。

作っていてすごいなぁと思ったのが、
ComfyUI は生成した画像にワークフローの情報をメタ情報として含めるそうで、
画像を ComfyUI にドラッグ・アンド・ドロップすると、
作成したときのワークフローを完全に再現してくれる!

かなり便利で、作成したときのプロンプトや設定をどこかにメモっておく必要がない!!
プロンプト作ることの大変さをかなり感じているので、
「あんな感じのキャラをもう一度作りたい」ってときに、画像から探せるというのが便利すぎると感じた。すごい!

プロンプト作成の難しさ

好きな絵を作るためにプロンプトを作っていくのがすごく難しい!

最初は ChatGPT とかに聞こうかなと思ったんだけど、
「少女」などのワードでも BAN のリスクがありそうなので、ローカルLLMを活用してみた。

しかし、 Qwen や GPT-OSS などのローカルLLMも、
実は自由じゃなくて『その種のコンテンツや表現を生成することはできません』と出てくる。

具体的には「レイプ目を表現するプロンプトってなんですか?」と聞いたときに怒られて、
「た……たしかに、オタクだと普通に使う表現だけど『レイプ目』って言葉、ヤバすぎだよね?!」と思った・・・。

それはそうと、Gemini や ChatGPT、ローカルLLMなどが作るプロンプトも実はあんまり役に立たないことがだんだんとわかってきた。

彼らが生成するのは、あくまで英語への変換なだけで、
wearing very loose fluffy cream colored off-shoulder knee-length dress とかイラスト生成AIには通じない。

大事なのは、画像にタグ付けした人がどういう単語でタグ付けしたのか
を考えることだとわかってきた。

だから例えば、ハワイアンな服で腰に布を巻く衣装を「パレオ」と言うけれど、
イラスト生成AIには「pareo」じゃなくて「sarong」じゃないとあんまり描いてくれなかったりする。

Pareo

Sarong

そういうわけなので、なんだかんだ、
「(描きたいもの) プロンプト」というワードで検索して、
ありがたいことに解説記事を書いてくれている人のプロンプトを見るほうが効果的だったりする。

「牛娘を描きたい」とか、最初の取っかかりが難しいところに関しては、
Gemini や ChatGPT に聞くと、こんなプロンプトで描けそう、と要素を分解してくれるのでいい感じ。

プロンプトを指定してもガチャ

よく画像生成AIはガチャと言われるけれど、本当にそうだなあと感じる。

とはいえ、この1週間さわっていたことで、
いいプロンプトを使うことでガチャの成功確率を上げられることもわかった。
さっきの Pareo と Sarong のように、生成に失敗しやすいと思っていたことが、違う単語を使うことで解決したりする。

良いプロンプトを知ることで、まだ精度を上げられる可能性はあるけれど、
今のところは2人を描こうとすると、特徴が混ざってしまうことがあることに悩んでいる。

【初心者向け】2人キャラの描き分けプロンプト解説|実例つき」の記事の
BREAK 構文には助けられたのだけど、それでも混ざるときはあって、2人を描くのはだいぶガチャ。

あと、これは Nova Anime XL の学習元が原因だと思うのだけれど、
ナース服を生成するときにどれだけプロンプトをがんばっても、純白のナース服にならず、
下の画像のようにコスプレ衣装すぎるものになりがち。なんでや……。

イラスト生成AIの限界

特に全身絵を描かせているからという原因はあるんだけれど、
瞳を描くことが特に苦手!
プロンプトを気を付けても、左右対称の瞳になりにくい!

顔がアップになる構図ほど、この問題は低減されるんだけれど、
全身絵を描くのは向いてないかな。

あとは、顔の描き方と体の描き方のタッチが微妙に違くなりやすいというのもあるかも。

このイラストは、顔がアップになっているからパット見は違和感少ないんだけど、
よく見ると顔が厚塗りっぽいのに、膝のあたりはアニメ塗りっぽくなっていて、
人が描いたらこうはならないよなぁ、というのを感じてしまう。

同じタッチで描くのが難しいわけだけれど、
上手いこと指定できればいいわけで、それの1つの解決策が漫画作品だと感じる。

最近Xで以下のポストがあったのを見て、FanzaのAI生成作品のランキングを見てみたら、
たしかにマンガがいっぱいで、試しに読んでみると
白黒マンガはあんまりAIっぽさを感じなかった。

出力している人ががんばってるってのももちろんあるんだろうけれど、
カラーでAIマンガを描いている人はAIらしい違和感を生んでたから、
白黒マンガはAI向きなのかもしれない。

たしかに、マンガで色を塗るのってトーン貼りだから、
イラストみたく塗り方に種類があって、1作品内に塗り方が複数混じるということが起こりにくいのかもね。

プロンプトで画風を安定させるやり方とか見つけて感想が変わるかもだけれど、
今のところの感想としてはこんなところで。

否定的なことも書いたけれど、
「プロンプトをこう変えたらどうなるかな?」を何度も実験して試すのがおもしろくて、
遊んでいるとあっという間に時間が溶けてしまいます。楽しいです。

「Cursor」ハンズオンイベントを開催しました! 主催者として振り返るよ

6月24日(火曜日)、株式会社SmartHRさんのオフィスにて、
AIコードエディター「Cursor」のハンズオンイベントを開催いたしました。
(ハンズオン=実際に手を動かしてもらいながら順番に沿って教えること)

【Cursorハンズオン】AIコードエディターわいわい勉強会 #1

イベントを振り返りつつ、初めてハンズオンイベントを主催した学びを記録します。

イベント当日

18:30 現地入り

開場の30分前に私は会場入りしました。
ハンズオンのナビゲーターをしてくださるほりゆうさんが先に準備を始めてくださっていました。

名札シールが用意されていて驚きました!
これあるといいですね。次回のために覚えておきます。

2人で机とイスを並べて、PCを充電したい人のために、いくつか延長コードの設置をおこないました。
机は、登壇者を向くような並べ方でなく、話しやすいように4人1テーブルで、
横を向くと登壇者が見られるような置き方にしました。

その後、Wi-Fi のパスワードとトイレの場所を確認し、会場案内スライドに追加するなどしていました。
「←イベント会場」の立て看板を立てたのは、わかりやすくてすごくよかったです。

19:00 開場

私が開会用のスライドを作っていたり、ほりゆうさんが登壇の最終練習などしていたら、
気が付いたら19:00を迎えていました。早い。

受け付けを担当してくれる人を探し忘れていて、受け付けは私がやりました。
名札シールに名前を書いてくれるよう案内するのと、参加者名簿のチェックを入れるだけでしたが、
SNSで「始まりました〜」とか告知している余裕もなくなったので、受付を担当してくれる友達は次回は探しておかねば……。

19:30 開演

  • トイレの場所
  • Wi-Fi情報の案内
  • 運営者の案内
  • 録音・写真に関する注意事項
  • コンテンツ紹介(タイムテーブル)

をお話しました。スライドのどのページにも Wi-Fi 情報を載せることで、
少し遅れて来た人でも Wi-Fi にアクセスできるよう配慮していました。

録音・写真に関する注意事項はこんな感じ

写真NGの登壇者さんがいるかはその場で聞いたのですが、今回は1人もいなくて珍しかったです。
最後に集合写真を撮るときも写真NGの人がいなくて、それも珍しかったです。
Instagram の普及によるものですかね?

19:37〜19:50 3分LT

3分という、ライトニングトーク(稲妻のごとく速い登壇)の中でもかなり短めのLTを
3名にしていただきました! ちなみに世の中には1分LTも存在するらしい……。

みなさん時間を守ってくださったのと、接続も異常が起こらなかったおかげで、
予定していた15分間より少し早く終わりました! すごい!

開会のご挨拶後は、自分は受け付けから近いところの席に座って、遅れて到着した人への対応が出来るようにしつつ、
イベントレポート用の写真も撮影していました。
入り口に人が来るか気にしながら写真を撮っていて、ゆっくりLTの話を聞けなかったので、改めて受け付けは必要です!!😂

LT1: taigaさん

1人目の登壇は taiga さんで、
『CursorはMCPを使った方が良いぞ』のタイトルで登壇してくださいました。

先日開催された「Cursor Meetup Tokyo」での司会もされていた taiga さんのお話は、
Cursor で MCP を使うといいぞ! って話と、最新のライブラリ情報を持っているMCPContext7」のご紹介でした。

MCPサーバーを追加するときは
CursorのDeeplinksに対応されていると、ボタンクリックだけで済むから楽! って話もされていました。

LT2: RyoCaさん

2人目の登壇は RyoCa さんで、
『Vibe Codingを行うためのVibe Prompt』のタイトルで登壇してくださいました。

曖昧な指示はAIの結果も曖昧にしてしまうため、例えば単に「ログイン機能を作って」ではなく、
「ReactでTypeScriptのJWTを使ったフォームバリデーション付きのログイン機能を作って」のように具体的に指示をする必要があるけど、それはなかなかに大変。
ということで、ChatGPTなどでプロンプト生成自体も生成AIに任せよう、という話がされました。

エラー監視プラットフォームである「Sentry」と連携することで、
エラー発生時の自動解決も可能になるような Next.js 用の Vibe Coding テンプレートを開発中だそうです。
リポジトリはこちら → https://github.com/rc-code-jp/vibe-coding-boilerplate

LT3: Shigeruさん

3人目の登壇は Shigeru さんで、
ツンデレさんと考える MCP tools の実装』のタイトルで登壇してくださいました。

LINE の MCP を開発中ということで、
メッセージ送信機能やリッチメニュー作成機能についての紹介がされました。

リッチメニューのデモンストレーションとして、
「リッチメニューを一つだけ作って、ボタンは『告白する』にして」と指示して作成された「告白する」ボタンが表示されると、会場に笑いが起こっていました!
「ツンモードとデレモードのボタンを2つ作って」と依頼して、実際にボタンに応じてツンメッセージとデレメッセージが表示されるなど、スライドも完成物もおもしろかったです。

まとめとして、MCPをしっかり作り込むと、Cursorなどに雑なリクエストをしても
しっかりした動くものを作ってくれるようになる、というお話をされていました。

19:50〜20:00 スポンサーLT

予定より少し早く進行したので、
このタイミングで SmartHR さんご提供のドリンクをみなさんに持っていってもらい、
そこからスポンサーLTを始めることにしました。

今回はハンズオンの登壇をする
ほりゆうさんがスポンサーLTもやっていらっしゃいました。

魅力ある会社として伝わったおかげか、イベント後アンケートでは「カジュアル面談を受けてみたい」方が複数名いらっしゃる結果になっていました! すごい!
私もこのイベント中やイベントの告知など、できるだけ会場を貸してくださっているSmartHRさんの名前は言うようにしていました。えらい!

20:00〜 ハンズオンイベント

LTがひととおり終わって、いよいよ ほりゆう さんによる
「Cursor」のハンズオンです!

Cursorには「Agent」「Ask」「Manual」の3つのモードがあるという基礎の話から始まり、
「Rules」機能の実践、
「Notepads」機能の実践、
「Docs」機能の実践といったことを踏まえ、
最後には GitHubMCP を使ってアクセスして Issue の内容を読み取るというところまでやりました!

特に最後の MCP など、難しいところも多く、
途中途中で、ほりゆうさんや私が見廻って詰まっている人にアドバイスしたり、
テーブル内で教え合ったりする時間が設けられていました。

エンジニア経験の長い人からエンジニア未経験の人まで幅広くいらっしゃり、
Rubyをインストールしていないんですが……」や、「FizzBuzzってなんですか?」という質問もあり、
「た…たしかに……!」と思いました。

今回は必ずしも動作検証までは必要ない範囲なのでよかったですが、
ハンズオンの言語選定としては、そこも気を付けないといけないことに気が付きました。
初めてのハンズオンイベント、学びが多かったです。

21:10〜21:40 懇親会

最後に、テーブルごとで自己紹介やAIコードエディターへの興味を話す懇親会がおこなわれました。
写真撮影の予定はなかったのですが、最後まで残っていらっしゃる方も多かったため、
せっかくですので最後に記念写真を撮りました!📸

人数がほどよく、すごくなごやかな会になって、満足度が非常に高い勉強会になったようで、
主催者としてとてもうれしかったです!!

アンケート結果

イベントページの参加者は19名、実際にいらしてくださったのが13名(約70%)、
アンケート回答者は12名で、ほぼ全員がアンケート回答をしてくださいました!!
ありがとうございます!🙇‍♂️

紹介して問題なさそうな範囲のアンケート結果を公開いたします。

最後の MCP のところがどうしても難易度高かったようですが、
会場の雰囲気としても、感想のコメントを見ても、満足度は非常に高そうでよかったです。

Cursor が気になっている人が多いのは勉強会のテーマ的にそうとして、
次に「Claude Code」について気になっている方が多いようですね!

Claude Code、マイブームなんですが本当にいいツールです!

ハンズオンイベント主催としての振り返り

主催者としての次に活かすための振り返りです!
次回のとき見られるよう、やるべきだったことに加えて、今回やってよかったことも踏まえて書いておきます。

  • イベントページの参加者向けのところに Google Docs の URL を貼っておいて、それの編集権限をハンズオン担当者に渡しておくことで、当日コピペしてほしい文字列を共有しやすいようにしておく
  • 名札シールはあると便利! 今回はSmartHRさんが用意してくれていたけど、前日までに買っておく!
  • ボイスレコーダーでの録音大事! NotebookLM によって要約を作れるようになるので、こういうイベントレポートを作るときにすごく助かる!
  • 前日までにデジカメとボイスレコーダー充電をやっておく
  • 可能なら卓上マイクスタンドを用意できると、PCの操作をしてるときにマイクを手持ちできない問題を解消できる
  • 会場に電源があるかは聞いておく(参加者のPC充電用)
  • 会場を理解している人が当日いてくれるかは重要!! 備品の位置や照明スイッチなどがわからないと困る
  • 受付をやってくれる人は必要! 知り合いに声をかけておく!!
  • 受け付けの人が登壇を聞けるよう、受け付けを置く位置も大事
  • トイレの場所の把握は大事! 会場に着いたら真っ先に確認すべきは Wi-Fi とトイレ!
  • 開会スライドのすべてのページに Wi-Fi 情報を書いておくといい!
  • 開会スライドでイベントのハッシュタグを紹介すると良い
  • 自分のイベントレポ以外に、誰かがSNSで実況する可能性もあるから、登壇者に写真撮影の許可を聞くことは登壇までにすることを忘れない!
  • ハンズオン、1時間だと少し足りないかも。でもLTがある今回の形式がよかったので、なんだかんだ次回も1時間かも
  • 参加者20人でよかった。これ以上だとハンズオンで詰まっている人へ廻りきれなくなっちゃう
  • テーブルは開会のタイミングで詰めて座ってもらうようにする。テーブル内が2名だけだと教え合うことが難しいので、テーブルは必ず3〜4名になるようにする
  • AIコードエディターの勉強会、思ったより来る層が幅広いことに留意しておく

オフライン勉強会を開催するの、あまりにも久しぶりすぎて
『みんなで解く ISUCON勉強会』はオフラインだったんですが、6名という小規模で受け付けいらずだったので……)
「あ、これ必要だった」が発生することもあったのですが、
今回でいろいろ思い出したので、次回はさらによい勉強会にできるようがんばります!