# UNO Game - Bot Integration Guide ## Overview This UNO online game has been enhanced with a comprehensive bot integration system that exports the game state in JSON format at every turn. This allows external AI systems (like Miku Discord Bot) to play as Player 2. ## Table of Contents 1. [Game State JSON Structure](#game-state-json-structure) 2. [Card Format Codes](#card-format-codes) 3. [API Endpoints](#api-endpoints) 4. [Integration Flow](#integration-flow) 5. [Example Bot Decision Logic](#example-bot-decision-logic) --- ## Game State JSON Structure The game exports a comprehensive state object with the following structure: ```json { "game": { "isOver": false, "winner": null, "currentTurn": "Player 2", "turnNumber": 15 }, "currentCard": { "code": "5R", "type": "number", "value": 5, "color": "R", "colorName": "red", "displayName": "5 red", "currentColor": "R", "currentNumber": 5 }, "recentlyPlayed": [ { "code": "5R", "type": "number", "value": 5, "color": "R", "colorName": "red", "displayName": "5 red", "position": 1 } ], "player1": { "name": "Player 1", "cardCount": 7, "isCurrentTurn": false, "cards": [] }, "player2": { "name": "Player 2", "cardCount": 5, "isCurrentTurn": true, "cards": [ { "code": "3R", "type": "number", "value": 3, "color": "R", "colorName": "red", "displayName": "3 red", "isPlayable": true }, { "code": "7B", "type": "number", "value": 7, "color": "B", "colorName": "blue", "displayName": "7 blue", "isPlayable": false } ], "playableCards": [ { "code": "3R", "type": "number", "value": 3, "color": "R", "colorName": "red", "displayName": "3 red", "isPlayable": true } ] }, "deck": { "drawPileCount": 78, "playedPileCount": 15 }, "botContext": { "canPlay": true, "mustDraw": false, "hasUno": false, "isWinning": false, "actions": [ { "action": "play_card", "card": { "code": "3R", "type": "number", "value": 3, "color": "R", "displayName": "3 red" }, "requiresColorChoice": false } ] } } ``` ### Key Fields Explanation #### `game` object - `isOver`: Boolean indicating if the game has ended - `winner`: Player name who won, or null if game is ongoing - `currentTurn`: "Player 1" or "Player 2" - whose turn it is - `turnNumber`: Approximate turn count (based on played cards) #### `currentCard` object - Complete information about the card currently on top of the pile - `currentColor` and `currentNumber` are the active game rules #### `player2.cards` array - All cards in the bot's hand with full details - Each card has `isPlayable` flag based on current game rules #### `player2.playableCards` array - Filtered list of only cards that can be played right now #### `botContext` object - `canPlay`: Can the bot play a card this turn? - `mustDraw`: Must the bot draw a card? - `hasUno`: Should the bot press the UNO button? (2 cards remaining) - `isWinning`: Does the bot have only 1 card left? - `actions`: Array of valid actions the bot can take --- ## Card Format Codes Cards are represented as 2-4 character codes: ### Number Cards (0-9) - Format: `[number][color]` - Examples: `0R`, `5G`, `9B`, `3Y` - Colors: R (Red), G (Green), B (Blue), Y (Yellow) ### Special Cards #### Skip - Format: `skip[color]` - Examples: `skipR`, `skipG`, `skipB`, `skipY` - Value code: 404 #### Reverse - Format: `_[color]` - Examples: `_R`, `_G`, `_B`, `_Y` - Value code: 0 #### Draw 2 - Format: `D2[color]` - Examples: `D2R`, `D2G`, `D2B`, `D2Y` - Value code: 252 #### Wild - Format: `W` - Value code: 300 - No color until played #### Draw 4 Wild - Format: `D4W` - Value code: 600 - No color until played ### Card Types Each parsed card has a `type` field: - `number`: Regular number cards (0-9) - `skip`: Skip next player's turn - `reverse`: Reverse play direction (in 2-player, acts as skip) - `draw2`: Next player draws 2 cards - `wild`: Change color - `draw4_wild`: Change color and next player draws 4 --- ## API Endpoints ### 1. Get Game State (HTTP) **Endpoint:** `GET /api/game/:roomCode/state` **Description:** Retrieve the current game state for a specific room. **Example:** ```bash curl http://localhost:5000/api/game/ABC123/state ``` **Response:** ```json { "success": true, "gameState": { /* full game state object */ }, "timestamp": "2026-01-25T10:30:00.000Z" } ``` ### 2. Submit Bot Action (HTTP) **Endpoint:** `POST /api/game/:roomCode/action` **Description:** Submit a bot's action (play card or draw card). **Request Body (Play Card):** ```json { "action": "play_card", "cardCode": "5R", "chosenColor": null } ``` **Request Body (Play Wild Card):** ```json { "action": "play_card", "cardCode": "W", "chosenColor": "R" } ``` **Request Body (Draw Card):** ```json { "action": "draw_card" } ``` **Example:** ```bash curl -X POST http://localhost:5000/api/game/ABC123/action \ -H "Content-Type: application/json" \ -d '{"action":"play_card","cardCode":"5R","chosenColor":null}' ``` ### 3. Socket.IO Events The game also supports real-time Socket.IO events: #### Client → Server Events - `botGameState`: Emitted by Player 2, contains full game state - `botAction`: Bot submits an action - `requestGameState`: Bot requests current state #### Server → Client Events - `botActionReceived`: Action received from HTTP API, forwarded to game - `gameStateRequested`: Server requests updated state from game --- ## Integration Flow ### For Socket.IO Integration 1. **Connect as Player 2:** ```javascript const socket = io.connect('http://localhost:5000') socket.emit('join', {room: 'ABC123'}, (error) => { if (error) console.error(error) }) ``` 2. **Listen for game state updates:** ```javascript socket.on('updateGameState', (gameState) => { // Process game state and make decision console.log('Turn:', gameState.turn) }) ``` 3. **Request game state:** ```javascript socket.emit('requestGameState', (response) => { console.log(response) }) ``` 4. **Submit action:** ```javascript socket.emit('botAction', { action: 'play_card', cardCode: '5R' }, (response) => { console.log(response) }) ``` ### For HTTP API Integration (Recommended for Miku Bot) 1. **Get Room Code:** User shares the game room code 2. **Poll Game State:** ```python import requests room_code = "ABC123" response = requests.get(f"http://localhost:5000/api/game/{room_code}/state") game_state = response.json()['gameState'] ``` 3. **Check if it's bot's turn:** ```python if game_state['game']['currentTurn'] == 'Player 2': # Bot's turn - make decision ``` 4. **Analyze playable cards:** ```python playable = game_state['player2']['playableCards'] if len(playable) > 0: # Choose a card to play chosen_card = playable[0]['code'] else: # Must draw action = {'action': 'draw_card'} ``` 5. **Submit action:** ```python action = { 'action': 'play_card', 'cardCode': chosen_card, 'chosenColor': 'R' if 'wild' in playable[0]['type'] else None } response = requests.post( f"http://localhost:5000/api/game/{room_code}/action", json=action ) ``` --- ## Example Bot Decision Logic Here's a simple decision-making algorithm for a bot: ```python def make_uno_decision(game_state): """ Simple bot logic for playing UNO """ bot_context = game_state['botContext'] # Not our turn if not game_state['player2']['isCurrentTurn']: return None # Check if we must draw if bot_context['mustDraw']: return { 'action': 'draw_card' } # Get playable cards playable = game_state['player2']['playableCards'] if len(playable) == 0: return { 'action': 'draw_card' } # Strategy: prioritize special cards, then high numbers chosen_card = None # 1. Try to play Draw 4 or Draw 2 for card in playable: if card['type'] in ['draw4_wild', 'draw2']: chosen_card = card break # 2. Try to play Skip if not chosen_card: for card in playable: if card['type'] == 'skip': chosen_card = card break # 3. Play highest number card if not chosen_card: number_cards = [c for c in playable if c['type'] == 'number'] if number_cards: chosen_card = max(number_cards, key=lambda x: x['value']) # 4. Play wild card as last resort if not chosen_card: chosen_card = playable[0] # Determine color for wild cards chosen_color = None if chosen_card['type'] in ['wild', 'draw4_wild']: # Count colors in hand colors = {} for card in game_state['player2']['cards']: if card.get('color'): colors[card['color']] = colors.get(card['color'], 0) + 1 # Choose most common color chosen_color = max(colors, key=colors.get) if colors else 'R' return { 'action': 'play_card', 'cardCode': chosen_card['code'], 'chosenColor': chosen_color } ``` --- ## Miku Bot Integration Example For integrating with the Miku Discord bot: ```python # In Miku bot's commands folder, create uno_player.py import requests import time class MikuUnoPlayer: def __init__(self, game_server_url, room_code): self.base_url = game_server_url self.room_code = room_code def get_game_state(self): """Fetch current game state""" url = f"{self.base_url}/api/game/{self.room_code}/state" response = requests.get(url) return response.json()['gameState'] def play_turn(self): """Play one turn as Miku""" state = self.get_game_state() # Check if it's our turn if state['game']['currentTurn'] != 'Player 2': return "Not my turn yet!" # Use LLM to decide (pass game state to Miku's LLM) decision = self.make_llm_decision(state) # Submit action url = f"{self.base_url}/api/game/{self.room_code}/action" response = requests.post(url, json=decision) return response.json() def make_llm_decision(self, game_state): """ Pass game state to Miku's LLM for decision-making """ # Format prompt for LLM prompt = f""" You are playing UNO. Here's the current game state: Current card: {game_state['currentCard']['displayName']} Your cards ({len(game_state['player2']['cards'])}): {', '.join([c['displayName'] for c in game_state['player2']['cards']])} Playable cards: {', '.join([c['displayName'] for c in game_state['player2']['playableCards']])} What should you do? Respond with JSON: {{"action": "play_card", "cardCode": "5R"}} or {{"action": "draw_card"}} """ # Query Miku's LLM from utils.llm import query_llama response = query_llama(prompt, model="llama3.1") # Parse LLM response to extract decision # ... parse JSON from response ... return decision ``` --- ## Testing the Integration ### 1. Start the Game Server ```bash cd /home/koko210Serve/docker/uno-online npm install npm start ``` ### 2. Start the Client (in another terminal) ```bash cd /home/koko210Serve/docker/uno-online/client npm install npm start ``` ### 3. Create a Game - Open browser to `http://localhost:3000` - Click "CREATE GAME" - Note the room code (e.g., "ABC123") ### 4. Test API Endpoints ```bash # Get game state curl http://localhost:5000/api/game/ABC123/state # Submit action (after joining as Player 2) curl -X POST http://localhost:5000/api/game/ABC123/action \ -H "Content-Type: application/json" \ -d '{"action":"draw_card"}' ``` ### 5. Monitor Console Output - Open browser console (F12) - Look for `🎮 UNO GAME STATE` logs - Copy JSON for testing with bot --- ## Next Steps for Miku Integration 1. **Create UNO command in Miku bot** (`/uno join [room_code]`) 2. **Implement polling loop** to check when it's Miku's turn 3. **Pass game state to LLM** with structured prompt 4. **Parse LLM response** to extract action decision 5. **Submit action via API** 6. **Add chat integration** to announce moves in Discord 7. **Handle edge cases** (disconnects, game over, etc.) --- ## Troubleshooting ### Game State Not Appearing - Ensure you've joined as Player 2 - Check browser console for logs - Verify Socket.IO connection ### API Returns 404 - Check that the room code is correct - Ensure the game has started (both players joined) - Verify game state has been emitted at least once ### Actions Not Working - Ensure the action JSON format is correct - Check that it's Player 2's turn - Verify the card code exists in the bot's hand --- ## License This integration system is part of the UNO Online project. See main README for license information.