UNO card game web app (Node.js/React) with Miku bot integration. Previously an independent git repo (fork of mizanxali/uno-online). Removed .git/ and absorbed into main repo for unified tracking. Includes bot integration code: botActionExecutor, cardParser, gameStateBuilder, and server-side bot action support. 37 files, node_modules excluded via local .gitignore.
5.3 KiB
5.3 KiB
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
{
"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:
// 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
{
"action": "draw"
}
action: Must be"draw"- No other parameters needed
3. Call UNO
{
"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)
{
"action": "play",
"card": "4R",
"color": null,
"callUno": true
}
- Add
"callUno": truewhen 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- RedG- GreenB- BlueY- Yellow
Strategy Tips for Bot
When to Play Cards
- Check
botContext.canPlay- if false, you must draw - Look at
player2.playableCardsfor available moves - Match by color or number with
currentCard
Priority Strategy
- Action cards first (Skip, Draw 2, Wild Draw 4) to disrupt opponent
- Match color if opponent has few cards (to block them)
- Match number to change color advantageously
- 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:
{
"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:
{
"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:
- Verify it's Player 2's turn (
game.currentTurn === "Player 2") - Check card is in
player2.cardsarray - Verify card is in
player2.playableCardsarray - For wild cards, always specify a valid color
Example Bot Decision Flow
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:
socket.emit('botAction', {
action: 'play',
card: '4R',
color: null
})
Receive game state updates:
socket.on('updateGameState', (state) => {
// Process new game state
})