267 lines
7.2 KiB
Markdown
267 lines
7.2 KiB
Markdown
|
|
# 🤖 Bot Integration Complete Guide
|
|||
|
|
|
|||
|
|
## Overview
|
|||
|
|
The UNO game now supports bot players (Miku) making moves via JSON API. The bot can play cards, draw cards, and call UNO by sending structured JSON commands.
|
|||
|
|
|
|||
|
|
## Quick Start
|
|||
|
|
|
|||
|
|
### 1. Start a game with 2 players
|
|||
|
|
- Player 1: Create a room (e.g., room code "ABC123")
|
|||
|
|
- Player 2: Join the same room
|
|||
|
|
- Game will auto-start when both players are present
|
|||
|
|
|
|||
|
|
### 2. Get the current game state
|
|||
|
|
```bash
|
|||
|
|
curl http://localhost:5000/api/game/ABC123/state
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Send a bot action (when it's Player 2's turn)
|
|||
|
|
```bash
|
|||
|
|
# Draw a card
|
|||
|
|
curl -X POST http://localhost:5000/api/game/ABC123/action \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"action":"draw"}'
|
|||
|
|
|
|||
|
|
# Play a card
|
|||
|
|
curl -X POST http://localhost:5000/api/game/ABC123/action \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"action":"play","card":"4R"}'
|
|||
|
|
|
|||
|
|
# Play a wild card with color choice
|
|||
|
|
curl -X POST http://localhost:5000/api/game/ABC123/action \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"action":"play","card":"W","color":"R"}'
|
|||
|
|
|
|||
|
|
# Call UNO
|
|||
|
|
curl -X POST http://localhost:5000/api/game/ABC123/action \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"action":"uno"}'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Testing Bot Actions
|
|||
|
|
|
|||
|
|
Use the included test script:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Test drawing a card
|
|||
|
|
node test-bot-action.js ABC123 '{"action":"draw"}'
|
|||
|
|
|
|||
|
|
# Test playing a card
|
|||
|
|
node test-bot-action.js ABC123 '{"action":"play","card":"4R"}'
|
|||
|
|
|
|||
|
|
# Test playing a wild card
|
|||
|
|
node test-bot-action.js ABC123 '{"action":"play","card":"W","color":"B"}'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Action Format Specification
|
|||
|
|
|
|||
|
|
### Play a Card
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"action": "play",
|
|||
|
|
"card": "4R",
|
|||
|
|
"color": null,
|
|||
|
|
"callUno": false
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Parameters:**
|
|||
|
|
- `action` (string, required): Must be `"play"`
|
|||
|
|
- `card` (string, required): Card code (e.g., "4R", "D2G", "skipB", "W", "D4W")
|
|||
|
|
- `color` (string, optional): Required for wild cards ("W", "D4W"). Values: "R", "G", "B", "Y"
|
|||
|
|
- `callUno` (boolean, optional): Set to `true` to call UNO with this play
|
|||
|
|
|
|||
|
|
### Draw a Card
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"action": "draw"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Call UNO
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"action": "uno"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Card Codes Reference
|
|||
|
|
|
|||
|
|
### Number Cards
|
|||
|
|
- `0R`, `1R`, ..., `9R` - Red numbers
|
|||
|
|
- `0G`, `1G`, ..., `9G` - Green numbers
|
|||
|
|
- `0B`, `1B`, ..., `9B` - Blue numbers
|
|||
|
|
- `0Y`, `1Y`, ..., `9Y` - Yellow numbers
|
|||
|
|
|
|||
|
|
### Action Cards
|
|||
|
|
- `skipR`, `skipG`, `skipB`, `skipY` - Skip cards
|
|||
|
|
- `_R`, `_G`, `_B`, `_Y` - Reverse cards
|
|||
|
|
- `D2R`, `D2G`, `D2B`, `D2Y` - Draw 2 cards
|
|||
|
|
|
|||
|
|
### Wild Cards
|
|||
|
|
- `W` - Wild (change color)
|
|||
|
|
- `D4W` - Wild Draw 4 (change color + opponent draws 4)
|
|||
|
|
|
|||
|
|
## Game State Structure
|
|||
|
|
|
|||
|
|
The game state JSON includes:
|
|||
|
|
- `game`: Overall game status (isOver, winner, currentTurn, turnNumber)
|
|||
|
|
- `currentCard`: The card on top of the discard pile
|
|||
|
|
- `player1`: Player 1's info and cards (hidden from bot)
|
|||
|
|
- `player2`: Player 2's (bot's) info and cards (visible)
|
|||
|
|
- `player2.playableCards`: Array of cards the bot can currently play
|
|||
|
|
- `botContext`: Helper info (canPlay, mustDraw, hasUno, actions)
|
|||
|
|
|
|||
|
|
## Validation Rules
|
|||
|
|
|
|||
|
|
The game validates bot actions:
|
|||
|
|
|
|||
|
|
1. **Turn validation**: Must be Player 2's turn
|
|||
|
|
2. **Card validation**: Card must be in Player 2's hand
|
|||
|
|
3. **Playability**: Card must be playable on current card
|
|||
|
|
4. **Wild cards**: Must specify color for "W" and "D4W"
|
|||
|
|
5. **UNO**: Can only call with exactly 2 cards in hand
|
|||
|
|
|
|||
|
|
## Integration with Miku Bot
|
|||
|
|
|
|||
|
|
### From Python (Miku bot)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import requests
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
def get_game_state(room_code):
|
|||
|
|
response = requests.get(f'http://localhost:5000/api/game/{room_code}/state')
|
|||
|
|
return response.json()['gameState']
|
|||
|
|
|
|||
|
|
def send_bot_action(room_code, action):
|
|||
|
|
response = requests.post(
|
|||
|
|
f'http://localhost:5000/api/game/{room_code}/action',
|
|||
|
|
headers={'Content-Type': 'application/json'},
|
|||
|
|
data=json.dumps(action)
|
|||
|
|
)
|
|||
|
|
return response.json()
|
|||
|
|
|
|||
|
|
# Example: Make a strategic move
|
|||
|
|
state = get_game_state('ABC123')
|
|||
|
|
|
|||
|
|
if state['game']['currentTurn'] == 'Player 2':
|
|||
|
|
playable = state['player2']['playableCards']
|
|||
|
|
|
|||
|
|
if playable:
|
|||
|
|
# Play first playable card
|
|||
|
|
card = playable[0]
|
|||
|
|
action = {'action': 'play', 'card': card['code']}
|
|||
|
|
|
|||
|
|
# Handle wild cards
|
|||
|
|
if card['type'] in ['wild', 'draw4']:
|
|||
|
|
action['color'] = 'R' # Choose a color
|
|||
|
|
|
|||
|
|
# Call UNO if needed
|
|||
|
|
if state['player2']['cardCount'] == 2:
|
|||
|
|
action['callUno'] = True
|
|||
|
|
|
|||
|
|
result = send_bot_action('ABC123', action)
|
|||
|
|
print(f"Played {card['displayName']}: {result}")
|
|||
|
|
else:
|
|||
|
|
# Must draw
|
|||
|
|
result = send_bot_action('ABC123', {'action': 'draw'})
|
|||
|
|
print(f"Drew a card: {result}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### LLM Integration Example
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def miku_make_move(game_state):
|
|||
|
|
"""Use LLM to decide Miku's move"""
|
|||
|
|
|
|||
|
|
# Build prompt for LLM
|
|||
|
|
prompt = f"""
|
|||
|
|
You are playing UNO. It's your turn.
|
|||
|
|
|
|||
|
|
Current card on table: {game_state['currentCard']['displayName']}
|
|||
|
|
Your cards: {[c['displayName'] for c in game_state['player2']['cards']]}
|
|||
|
|
Playable cards: {[c['displayName'] for c in game_state['player2']['playableCards']]}
|
|||
|
|
|
|||
|
|
Opponent has {game_state['player1']['cardCount']} cards.
|
|||
|
|
You have {game_state['player2']['cardCount']} cards.
|
|||
|
|
|
|||
|
|
Choose your move as JSON:
|
|||
|
|
{{"action": "play", "card": "CODE"}} or {{"action": "draw"}}
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
# Get LLM response
|
|||
|
|
llm_response = query_llama(prompt)
|
|||
|
|
|
|||
|
|
# Parse JSON from response
|
|||
|
|
action = json.loads(llm_response)
|
|||
|
|
|
|||
|
|
# Send action
|
|||
|
|
return send_bot_action(game_state['room'], action)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Debugging
|
|||
|
|
|
|||
|
|
### Console Logs
|
|||
|
|
The game client logs bot actions:
|
|||
|
|
- `🤖 Received bot action:` - Action received from HTTP API
|
|||
|
|
- `🤖 Bot action result:` - Result of executing the action
|
|||
|
|
- `<60><> Bot playing card:` - Card being played
|
|||
|
|
- `🌈 Bot chose color:` - Color chosen for wild card
|
|||
|
|
- `🔥 Bot called UNO!` - UNO was called
|
|||
|
|
- `📥 Bot drawing a card` - Card was drawn
|
|||
|
|
|
|||
|
|
### Error Messages
|
|||
|
|
- `❌ Bot action rejected: Not Player 2's turn` - Wrong turn
|
|||
|
|
- `❌ Bot play action rejected: Card not in hand` - Invalid card
|
|||
|
|
- `❌ Bot play action rejected: Wild card without color` - Missing color
|
|||
|
|
- `❌ Bot UNO rejected` - Invalid UNO call
|
|||
|
|
|
|||
|
|
## Files Added/Modified
|
|||
|
|
|
|||
|
|
### New Files
|
|||
|
|
- `BOT_ACTION_SPEC.md` - Detailed API specification
|
|||
|
|
- `BOT_INTEGRATION_COMPLETE.md` - This file
|
|||
|
|
- `client/src/utils/botActionExecutor.js` - Bot action executor
|
|||
|
|
- `test-bot-action.js` - Test script
|
|||
|
|
|
|||
|
|
### Modified Files
|
|||
|
|
- `server.js` - HTTP API endpoints for bot actions
|
|||
|
|
- `client/src/components/Game.js` - Bot action listener and integration
|
|||
|
|
|
|||
|
|
## Next Steps
|
|||
|
|
|
|||
|
|
1. **Test the integration**: Use `test-bot-action.js` to verify actions work
|
|||
|
|
2. **Integrate with Miku**: Add UNO game support to Miku bot
|
|||
|
|
3. **Add LLM strategy**: Use Miku's LLM to make strategic decisions
|
|||
|
|
4. **Add personality**: Make Miku trash talk and celebrate!
|
|||
|
|
|
|||
|
|
## Example Full Game Flow
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Terminal 1: Start server
|
|||
|
|
cd /home/koko210Serve/docker/uno-online
|
|||
|
|
npm start
|
|||
|
|
|
|||
|
|
# Terminal 2: Start client dev server
|
|||
|
|
cd /home/koko210Serve/docker/uno-online/client
|
|||
|
|
npm start
|
|||
|
|
|
|||
|
|
# Browser 1: Create game as Player 1
|
|||
|
|
# Open http://localhost:3000
|
|||
|
|
# Create room "MIKU01"
|
|||
|
|
|
|||
|
|
# Browser 2: Join as Player 2
|
|||
|
|
# Open http://localhost:3000
|
|||
|
|
# Join room "MIKU01"
|
|||
|
|
|
|||
|
|
# Terminal 3: Control Player 2 (bot) via API
|
|||
|
|
# Get state
|
|||
|
|
curl http://localhost:5000/api/game/MIKU01/state | jq
|
|||
|
|
|
|||
|
|
# Make a move (when it's Player 2's turn)
|
|||
|
|
node test-bot-action.js MIKU01 '{"action":"play","card":"4R"}'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
🎉 The bot integration is complete and ready to use!
|