A Rock-Paper-Scissors-Plus chatbot referee built with Google ADK. Best of 3 rounds with a special bomb move.
- Moves: rock, paper, scissors, bomb (shortcuts: r/p/s/b)
- Bomb beats everything but can only be used once per game
- Bomb vs bomb = draw
- Invalid input = you lose that round
- Game ends after 3 rounds
cd "upliance ai submission"
python -m venv .venv
.venv\Scripts\Activate.ps1 # Windows
pip install -r requirements.txt
# Add your API key
copy .env.example .env
# Edit .env with your GOOGLE_API_KEY from https://aistudio.google.com/app/apikeyadk run rps_plus_agent
# or
adk web # then open http://localhost:8000rps_plus_agent/
agent.py - ADK agent + tools
game_logic.py - game rules (RPS logic, bomb handling)
game_state.py - state tracking with dataclass
main.py - optional CLI wrapper
test_game.py - tests
Using a dataclass to track game state:
@dataclass
class GameState:
round_number: int = 0
user_score: int = 0
bot_score: int = 0
user_bomb_used: bool = False
bot_bomb_used: bool = False
game_over: bool = False
history: List[Dict] = []State lives in a module-level singleton so it persists across tool calls (not just in the prompt).
7 tools handle different parts of the game:
| Tool | What it does |
|---|---|
| validate_move | checks if input is valid, handles aliases |
| resolve_round | figures out who won the round |
| update_game_state | updates scores, bomb usage, etc |
| get_bot_move | picks the bot's move (has some strategy) |
| start_new_game | resets everything |
| get_current_status | returns game status |
| get_game_history | shows past rounds |
Why a singleton for state? Simplest way to persist state across tool calls without a database. Tradeoff: not thread-safe.
Why 7 tools instead of 1? Separation of concerns. Each tool does one thing. Makes it easier to debug and test.
Bot strategy: Not pure random - slightly higher bomb chance when losing. Makes the game more interesting.
Move shortcuts: Accept r/p/s/b and even emojis because typing "scissors" every time is annoying.