Files
miku-discord/readmes/UNO_FLOW_DIAGRAMS.md
koko210Serve c708770266 reorganize: consolidate all documentation into readmes/
- Moved 20 root-level markdown files to readmes/
- Includes COMMANDS.md, CONFIG_README.md, all UNO docs, all completion reports
- Added new: MEMORY_EDITOR_FEATURE.md, MEMORY_EDITOR_ESCAPING_FIX.md,
  CONFIG_SOURCES_ANALYSIS.md, MCP_TOOL_CALLING_ANALYSIS.md, and others
- Root directory is now clean of documentation clutter
2026-03-04 00:19:49 +02:00

575 lines
30 KiB
Markdown

# 🎮 Miku UNO Bot - Visual Flow Diagrams
## High-Level Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Discord User │
│ (Types: !uno create) │
└──────────────────────────┬──────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Miku Discord Bot │
│ (bot.py + commands) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ bot.py │───▶│commands/uno.py│──▶│utils/uno_game│ │
│ │ (!uno route) │ │ (cmd handler) │ │(MikuUnoPlayer│ │
│ └──────────────┘ └──────────────┘ └───────┬────────┘ │
│ │ │
└──────────────────────────────────────────────────┼──────────┘
┌──────────────────────────────┼───────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌─────────────┐ ┌─────────┐
│ Playwright │ │ HTTP API │ │ LLM │
│ (Browser Control)│ │ (Actions) │ │(Strategy│
└────────┬─────────┘ └──────┬──────┘ └────┬────┘
│ │ │
│ │ │
▼ ▼ │
┌────────────────────────────────────────────┐ │
│ UNO Game (Frontend) │ │
│ http://192.168.1.2:3002 │ │
│ │ │
│ ┌──────────────────────────────────────┐ │ │
│ │ React Components │ │ │
│ │ - Game.js (main game logic) │ │ │
│ │ - botActionExecutor.js │ │ │
│ └──────────────┬───────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌──────────────────────────────────────┐ │ │
│ │ WebSocket Connection │ │ │
│ └──────────────┬───────────────────────┘ │ │
└─────────────────┼────────────────────────┘ │
│ │
▼ │
┌────────────────────────────────────────────┐ │
│ UNO Game (Backend) │ │
│ http://localhost:5000 │ │
│ │ │
│ ┌──────────────────────────────────────┐ │ │
│ │ Express Server │ │ │
│ │ - WebSocket (Socket.IO) │ │ │
│ │ - HTTP API endpoints │ │ │
│ │ GET /api/game/:code/state ────┼─┼───┘
│ │ POST /api/game/:code/action │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ Game State Manager │ │
│ │ - rooms{} dictionary │ │
│ │ - player management │ │
│ │ - turn logic │ │
│ └──────────────────────────────────────┘ │
└────────────────────────────────────────────┘
```
---
## Command Flow: !uno create
```
User Types: !uno create
┌─────────────────┐
│ Discord.on() │
│ message event │
└────────┬────────┘
┌─────────────────────────────────────┐
│ bot.py: Check if starts with !uno │
└────────┬────────────────────────────┘
┌──────────────────────────────────────────┐
│ commands/uno.py: handle_uno_command() │
│ Parse command: "create" │
└────────┬───────────────────────────────┘
┌──────────────────────────────────────────┐
│ utils/uno_game.py: MikuUnoPlayer │
│ player = MikuUnoPlayer(channel, user) │
└────────┬───────────────────────────────┘
┌──────────────────────────────────────────┐
│ await player.create_and_join_game() │
└────────┬───────────────────────────────┘
├─────────────────┐
│ │
▼ ▼
┌──────────────┐ ┌───────────────────────┐
│ Generate │ │ Launch Playwright │
│ Room Code │ │ Headless Browser │
│ (6 chars) │ │ (Chromium) │
└──────┬───────┘ └───────┬───────────────┘
│ │
│ ▼
│ ┌─────────────────────────┐
│ │ Navigate to: │
│ │ http://192.168.1.2:3002│
│ └───────┬─────────────────┘
│ │
│ ▼
│ ┌─────────────────────────┐
│ │ Click "Join Room" │
│ │ Enter room code │
│ │ Submit │
│ └───────┬─────────────────┘
│ │
▼ ▼
┌──────────────────────────────────────┐
│ Send Discord Embed: │
│ "🎮 Created UNO room: ABC123 │
│ Join at: http://192.168.1.2:3002 │
│ I'm joining now as Player 2! ✨" │
└──────────────────────────────────────┘
┌──────────────────────────────────────┐
│ Start game loop (play_game()) │
│ Poll every 2 seconds │
└──────────────────────────────────────┘
```
---
## Game Loop Flow
```
┌─────────────────────┐
│ play_game() loop │
│ while game_active │
└──────────┬──────────┘
┌─────────────────────────────┐
│ await asyncio.sleep(2) │ ◄─────┐
└──────────┬──────────────────┘ │
│ │
▼ │
┌─────────────────────────────┐ │
│ get_game_state() │ │
│ GET /api/game/:code/state │ │
└──────────┬──────────────────┘ │
│ │
▼ │
┌─────────────────────────────┐ │
│ Is it Miku's turn? │ │
│ (currentPlayer == "Player 2"?) │ │
└──────┬────────────┬─────────┘ │
│NO │YES │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ get_miku_decision() │ │
│ │ (Call LLM) │ │
│ └──────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Build strategy prompt│ │
│ │ - Current hand │ │
│ │ - Top card │ │
│ │ - Opponent cards │ │
│ │ - Strategic tips │ │
│ └──────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Query LLM │ │
│ │ (utils.llm) │ │
│ └──────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Parse JSON response │ │
│ │ {"action": "play", │ │
│ │ "card": "R5"} │ │
│ └──────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ Validate action │ │
│ │ (is card in hand?) │ │
│ └──────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ send_action() │ │
│ │ POST /api/game/... │ │
│ └──────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ send_trash_talk() │ │
│ │ (Discord message) │ │
│ └──────┬──────────────┘ │
│ │ │
└──────────┴───────────────────┘
┌─────────────────────────────┐
│ Check if game ended │
│ (winner exists?) │
└──────┬───────────┬──────────┘
│NO │YES
│ │
└───────────┤
┌─────────────────────────────┐
│ Send final message │
│ "Game ended! Winner: ..." │
└──────────┬──────────────────┘
┌─────────────────────────────┐
│ cleanup() │
│ - Close browser │
│ - Remove from active games │
└─────────────────────────────┘
```
---
## LLM Strategy Decision Flow
```
┌─────────────────────────────┐
│ get_miku_decision() │
└──────────┬──────────────────┘
┌─────────────────────────────────────────┐
│ build_strategy_prompt(game_state) │
└──────────┬──────────────────────────────┘
┌─────────────────────────────────────────┐
│ Construct prompt with: │
│ "You are Miku, a cheerful idol..." │
│ │
│ Current Game State: │
│ - Your hand: R5, G2, B7, WD4 │
│ - Top card: Y3 │
│ - Opponent has: 4 cards │
│ │
│ Strategic Tips: │
│ - Match color or number │
│ - Use Wild cards strategically │
│ - Save action cards for impact │
│ │
│ Output ONLY valid JSON: │
│ {"action":"play","card":"R5"} │
└──────────┬──────────────────────────────┘
┌─────────────────────────────────────────┐
│ await query_llama(prompt) │
│ (utils.llm.query_llama) │
└──────────┬──────────────────────────────┘
┌─────────────────────────────────────────┐
│ LLM Response: │
│ "{"action":"play","card":"Y5"}" │
└──────────┬──────────────────────────────┘
┌─────────────────────────────────────────┐
│ Try parse JSON │
└──────┬───────────────┬──────────────────┘
│ Success │ Fail
│ │
▼ ▼
┌──────────────┐ ┌────────────────────┐
│ Validate: │ │ Log error │
│ - action OK? │ │ Fallback: draw │
│ - card valid?│ └────────────────────┘
│ - in hand? │
└──────┬───────┘
│ Valid
┌─────────────────────────────┐
│ Return action dict │
│ {"action":"play","card":..}│
└─────────────────────────────┘
```
---
## Trash Talk Selection Flow
```
┌─────────────────────────────┐
│ send_trash_talk(action) │
└──────────┬──────────────────┘
┌─────────────────────────────┐
│ Check card type │
└──┬──┬──┬──┬──┬──────────────┘
│ │ │ │ │
│ │ │ │ └──────────┐
│ │ │ └─────────┐ │
│ │ └────────┐ │ │
│ └───────┐ │ │ │
▼ ▼ ▼ ▼ ▼
┌──────┐ ┌────┐ ┌──┐ ┌──┐ ┌────┐
│ WD4 │ │ D │ │S │ │W │ │REG │
│Draw 4│ │Dr 2│ │Sk│ │Wi│ │Norm│
└──┬───┘ └──┬─┘ └┬─┘ └┬─┘ └─┬──┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────────────────────────────────┐
│ "Take four "Draw "Sorry~ │
│ cards! 💙✨ two Skipping │
│ I hope cards! your turn! │
│ you're Don't Maybe next │
│ ready for worry, time? 🎶" │
│ a comeback~" I still │
│ believe │
│ in you~ │
│ ✨" │
└────────┬──────────────────────┬────┘
│ │
▼ ▼
┌──────────────────┐ ┌───────────────┐
│ Send to Discord │ │ Random choice │
│ channel.send() │ │ from variants │
└──────────────────┘ └───────────────┘
```
---
## Data Flow: Game State
```
┌────────────────────────────────────────┐
│ UNO Backend (server.js) │
│ │
│ rooms = { │
│ "ABC123": { │
│ roomId: "ABC123", │
│ users: ["Player 1", "Player 2"], │
│ gameState: { │
│ currentPlayer: "Player 2", │
│ topCard: "Y3", │
│ players: [ │
│ { │
│ name: "Player 1", │
│ hand: [...], (hidden) │
│ cardCount: 5 │
│ }, │
│ { │
│ name: "Player 2", │
│ hand: ["R5","G2",...], │
│ cardCount: 7 │
│ } │
│ ], │
│ winner: null, │
│ direction: 1 │
│ } │
│ } │
│ } │
└─────────┬──────────────────────────────┘
│ HTTP GET /api/game/ABC123/state
┌────────────────────────────────────────┐
│ JSON Response (filtered for Player 2) │
│ { │
│ "currentPlayer": "Player 2", │
│ "topCard": "Y3", │
│ "myHand": ["R5","G2","B7",...], │
│ "myCardCount": 7, │
│ "opponentCardCount": 5, │
│ "direction": 1, │
│ "winner": null │
│ } │
└─────────┬──────────────────────────────┘
┌────────────────────────────────────────┐
│ MikuUnoPlayer processes state │
│ - Detects it's her turn │
│ - Sees available cards │
│ - Checks top card │
│ - Asks LLM for strategy │
└─────────┬──────────────────────────────┘
┌────────────────────────────────────────┐
│ LLM returns decision │
│ {"action":"play","card":"R5"} │
└─────────┬──────────────────────────────┘
│ HTTP POST /api/game/ABC123/action
│ Body: {"action":"play","card":"R5"}
┌────────────────────────────────────────┐
│ Backend validates and executes │
│ - Check if R5 is in Player 2's hand │
│ - Check if R5 can be played on Y3 │
│ - Move R5 from hand to pile │
│ - Update topCard to R5 │
│ - Switch currentPlayer to Player 1 │
│ - Emit WebSocket event to all clients │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ Frontend updates UI │
│ - Card animation │
│ - Update piles │
│ - Show turn indicator │
└────────────────────────────────────────┘
```
---
## File Dependency Graph
```
bot.py
├─> commands/uno.py
│ │
│ └─> utils/uno_game.py
│ │
│ ├─> playwright
│ │ └─> (browser control)
│ │
│ ├─> utils/llm.py
│ │ └─> query_llama()
│ │
│ ├─> aiohttp
│ │ └─> (HTTP requests)
│ │
│ └─> discord.py
│ └─> (Discord messages)
└─> globals.py
└─> (configuration)
uno-online/
├─> server.js
│ ├─> express
│ ├─> socket.io (WebSocket)
│ └─> HTTP API (/api/game/...)
└─> client/
├─> src/App.js
│ └─> React Router
└─> src/components/Game.js
├─> Socket.IO client
└─> utils/botActionExecutor.js
└─> (action validation & execution)
```
---
## Discord Command Hierarchy
```
!uno
├─> create
│ └─> MikuUnoPlayer.create_and_join_game()
│ ├─> Generate room code
│ ├─> Launch browser
│ ├─> Join room
│ └─> Start game loop
├─> join <code>
│ └─> MikuUnoPlayer.join_game(code)
│ ├─> Launch browser
│ ├─> Join existing room
│ └─> Start game loop
├─> list
│ └─> Show active_uno_games dict
│ └─> Format as Discord embed
├─> quit <code>
│ └─> Find game by code
│ ├─> Call cleanup()
│ ├─> Remove from active_uno_games
│ └─> Send confirmation
└─> help
└─> Show command list
└─> Format as Discord embed
```
---
## Timing Diagram
```
Time User Bot UNO Server LLM
─────────────────────────────────────────────────────────
0s !uno create
1s ├─> Generate ABC123
2s ├─> Launch browser
3s ├─> Navigate to URL
4s ├─> Join room ──────> Create room
│ Add Player 2
5s ├─> Send embed
Sees embed │ "Room: ABC123"
6s Opens URL │
Joins room ─────────────────────> Add Player 1
Start game
Deal cards
10s ├─> Poll state <──── Game state
│ (Player 1 turn)
12s ├─> Poll state <──── Game state
│ (Player 1 turn)
15s Plays card ─────────────────────> Update state
Player 2 turn
16s ├─> Poll state <──── Game state
│ (Player 2 turn!)
17s ├─> Strategy? ─────────────────> LLM
│ │
18s │ │ Think
│ │
19s │ <─────────────────── JSON ──┘
20s ├─> Play card ──────> Execute
│ Validate
│ Update
21s ├─> Trash talk
Sees message │ "Playing card~"
22s ├─> Poll state <──── Game state
│ (Player 1 turn)
... ... ...
```
---
These diagrams should help visualize how everything connects! 🎮✨