Files
miku-discord/uno-online/BOT_ACTION_SPEC.md

231 lines
5.3 KiB
Markdown
Raw Normal View History

# Bot Action Specification for UNO Game
## Overview
The bot (Miku) can interact with the UNO game by sending JSON action commands. The game state is provided in a structured JSON format, and the bot responds with action commands.
## Action Types
### 1. Play a Card
```json
{
"action": "play",
"card": "4R",
"color": null
}
```
- `action`: Must be `"play"`
- `card`: The card code to play (e.g., "4R", "D2G", "skipB", "W", "D4W")
- `color`: Optional. Only required for wild cards ("W" or "D4W"). Values: "R", "G", "B", or "Y"
**Examples:**
```json
// Play a number card
{"action": "play", "card": "4R", "color": null}
// Play a Draw 2 card
{"action": "play", "card": "D2G", "color": null}
// Play a Skip card
{"action": "play", "card": "skipB", "color": null}
// Play a Wild card (must specify color)
{"action": "play", "card": "W", "color": "R"}
// Play a Wild Draw 4 (must specify color)
{"action": "play", "card": "D4W", "color": "B"}
```
### 2. Draw a Card
```json
{
"action": "draw"
}
```
- `action`: Must be `"draw"`
- No other parameters needed
### 3. Call UNO
```json
{
"action": "uno"
}
```
- `action`: Must be `"uno"`
- Call this when you have exactly 2 cards in hand and are about to play one
- **Important:** Call UNO *before* playing your second-to-last card, or call it as a separate action
### 4. Combined Actions (Play + UNO)
```json
{
"action": "play",
"card": "4R",
"color": null,
"callUno": true
}
```
- Add `"callUno": true` when playing a card that will leave you with 1 card
## Card Code Reference
### Number Cards
- Format: `{number}{color}`
- Numbers: 0-9
- Colors: R (Red), G (Green), B (Blue), Y (Yellow)
- Examples: `0R`, `5G`, `9B`, `3Y`
### Action Cards
- **Skip**: `skipR`, `skipG`, `skipB`, `skipY`
- **Reverse**: `_R`, `_G`, `_B`, `_Y`
- **Draw 2**: `D2R`, `D2G`, `D2B`, `D2Y`
- **Wild**: `W`
- **Wild Draw 4**: `D4W`
## Color Codes
- `R` - Red
- `G` - Green
- `B` - Blue
- `Y` - Yellow
## Strategy Tips for Bot
### When to Play Cards
1. Check `botContext.canPlay` - if false, you must draw
2. Look at `player2.playableCards` for available moves
3. Match by color or number with `currentCard`
### Priority Strategy
1. **Action cards first** (Skip, Draw 2, Wild Draw 4) to disrupt opponent
2. **Match color** if opponent has few cards (to block them)
3. **Match number** to change color advantageously
4. **Save wild cards** for critical moments
### When to Call UNO
- When you have exactly 2 cards and are about to play one
- **Must call UNO** or you'll get 2 penalty cards!
### When to Use Wild Cards
- When you have no other playable cards
- To change to a color you have many of
- Wild Draw 4: When opponent has 1-2 cards (aggressive)
## HTTP API Endpoint
### Get Game State
```
GET http://localhost:5000/api/game/\{roomCode\}/state
```
**Response:**
```json
{
"success": true,
"gameState": { /* full game state */ },
"timestamp": "2026-01-27T..."
}
```
### Submit Bot Action
```
POST http://localhost:5000/api/game/\{roomCode\}/action
Content-Type: application/json
{
"action": "play",
"card": "4R",
"color": null
}
```
**Response:**
```json
{
"success": true,
"message": "Action received and forwarded to game"
}
```
## Error Handling
### Invalid Actions
The game will ignore invalid actions:
- Playing a card not in your hand
- Playing an unplayable card
- Not specifying color for wild cards
- Playing out of turn
### Validation
Before sending an action:
1. Verify it's Player 2's turn (`game.currentTurn === "Player 2"`)
2. Check card is in `player2.cards` array
3. Verify card is in `player2.playableCards` array
4. For wild cards, always specify a valid color
## Example Bot Decision Flow
```python
def make_move(game_state):
# Check if it's our turn
if game_state['game']['currentTurn'] != 'Player 2':
return None
# Check if we can play
if not game_state['botContext']['canPlay']:
return {"action": "draw"}
# Get playable cards
playable = game_state['player2']['playableCards']
if not playable:
return {"action": "draw"}
# Check if we need to call UNO
our_cards = game_state['player2']['cardCount']
call_uno = (our_cards == 2)
# Priority: Draw 2 or Draw 4
for card in playable:
if card['type'] in ['draw2', 'draw4']:
action = {"action": "play", "card": card['code']}
if card['type'] == 'draw4':
action['color'] = choose_best_color(game_state)
if call_uno:
action['callUno'] = True
return action
# Priority: Skip or Reverse
for card in playable:
if card['type'] in ['skip', 'reverse']:
if call_uno:
return {"action": "play", "card": card['code'], "callUno": True}
return {"action": "play", "card": card['code']}
# Play any available card
card = playable[0]
action = {"action": "play", "card": card['code']}
if card['type'] == 'wild':
action['color'] = choose_best_color(game_state)
if call_uno:
action['callUno'] = True
return action
```
## WebSocket Events (Alternative to HTTP)
If the bot maintains a WebSocket connection:
### Emit bot action:
```javascript
socket.emit('botAction', {
action: 'play',
card: '4R',
color: null
})
```
### Receive game state updates:
```javascript
socket.on('updateGameState', (state) => {
// Process new game state
})
```