A Rust-based card game simulation implementing the President/Asshole card game with pluggable AI strategies.
Roosevelt simulates the classic "President" (also known as "Asshole") card game where players compete to empty their hands first. The game features:
- Multiple AI strategies (Random, Default, and Interactive)
- Role-based card passing between games (President/VP/Secretary/Vice-Asshole/Asshole)
- Configurable player setups via YAML files
- Real-time game visualization with optional delays
- Rust 1.92.0 or later
- Cargo (included with Rust)
- (Optional) SQLite for database persistence
# Clone the repository
git clone <repository-url>
cd roosevelt
# Build the project
cargo build --release- Create a configuration file
config.yaml:
players:
- name: "Alice"
strategy: "default"
- name: "Bob"
strategy: "random"
- name: "Charlie"
strategy: "input" # Interactive player (you!)- Run the simulation:
cargo run --release --bin run_simulation -- --config config.yaml- For a slower game with delays between moves:
cargo run --release --bin run_simulation -- --config config.yaml --delay-ms 500Roosevelt supports database persistence for recording games, players, and actions.
- Set up database URL (optional - defaults to in-memory):
export DATABASE_URL="sqlite:roosevelt.db"- Run with database (will create database automatically):
cargo run --release --bin run_simulation -- --config config.yaml --database sqlite:roosevelt.db- Player registration flow on first run:
- Found existing player: Reuse (y/n)?
- Type
yto reuse existing player record - Type
nto create new player with same name
Configuration sources are checked in this priority order:
- CLI flags (highest priority)
--database <url>- Database connection URL--force-new-players- Always create new player records--auto-reuse-players- Skip prompts and auto-reuse existing players
- Environment variables
DATABASE_URL- Database connection string
- YAML config file
database:field for database URLplayers:and other game settings
- Hardcoded defaults
- Database:
sqlite::memory:(no persistence, useful for testing) - Player registration: Auto-prompt for existing players
- Database:
Use sqlite::memory: as the database URL for testing without persistence.
game_config:
players:
- name: "Alice"
strategy: "default"
- name: "Bob"
strategy: "random"
- name: "Charlie"
strategy: "input"
delay_ms: 500 # Optional: delay between moves (ms)
database: sqlite:roosevelt.db # Optional: database URLBe the first player to play all cards from your hand. Players are ranked based on finishing order, which determines card passing in the next game.
- Two is the highest card (unlike standard poker rankings)
- Standard descending order: Two → Ace → King → ... → Three (lowest)
- Starting Player: The player with 3♣ (or 3♠/3♥/3♦/4♣ in that priority) starts
- First Play: Must contain the starting card
- Card Plays: You can play:
- Single cards
- Pairs (two matching ranks)
- Triples (three matching ranks)
- Quads (four matching ranks)
- Beating Cards: Played cards must be higher rank than the current top cards
- Passing: You can pass on your turn
- New Round: When everyone passes, the last player starts a new round
After each game, players receive roles based on finishing order:
| Role | Cards to Send | Cards to Receive |
|---|---|---|
| President | 2 cards (best) | From Asshole |
| Vice President | 1 card (best) | From Vice-Asshole |
| Secretary | - | - |
| Vice-Asshole | 1 card (worst) | To Vice President |
| Asshole | 2 cards (worst) | To President |
Conservative AI that:
- Always plays the lowest allowable card(s)
- Sends the lowest card during pregame passing
- Good for learning the game basics
Chaotic AI that:
- Randomly selects from all valid actions
- Useful for testing game mechanics
- Unpredictable gameplay
Interactive human player that:
- Prompts you via the command line
- Enter actions as:
play <cards>- e.g.,play 3♠ 4♥orplay K♠ K♥send <card>- e.g.,send 2♦(during card passing phase)pass- Pass your turn
Card input format: Use standard suit symbols (♠ ♥ ♦ ♣) or abbreviations (S H D C)
- Examples:
3♠,K♥,2♦,10♣
The config.yaml file defines players and their strategies:
players:
- name: "Player 1"
strategy: "default" # Conservative AI
- name: "Player 2"
strategy: "random" # Chaotic AI
- name: "Player 3"
strategy: "input" # Interactive humandefault- Conservative AI (plays worst allowable cards)random- Random action selectioninput- Interactive human player (requires terminal input)
cargo run --bin run_simulation -- [OPTIONS]| Option | Description | Default |
|---|---|---|
--config <path> |
Path to YAML config file | config.yaml |
--delay-ms <ms> |
Delay between moves (milliseconds) | No delay |
--database <url> |
Database connection URL | sqlite::memory: |
--force-new-players |
Always create new player records | Auto-prompt |
--auto-reuse-players |
Auto-reuse existing players without prompts | Prompt each player |
- Automatic player registration on first run
- Player name lookup (returns existing UUID)
- Unique player IDs via UUID v4
- Automatic game start/end timestamping
- Deck seed generation and storage
- Player order tracking
- Configuration snapshot (JSON)
- All actions recorded: PlayCards, SendCard, Pass
- Turn order tracking (global counter)
- Phase tracking: "pregame" and "ingame"
- Card play serialization (JSON format)
- Finishing place (1st, 2nd, etc.)
- Role assignment (President, VP, Secretary, Vice-Asshole, Asshole)
- Per-player result tracking
- Retry logic with exponential backoff (100ms → 200ms → 400ms → 800ms → 1600ms)
- Failed write tracking for debugging
- Connection pooling (max 20 connections)
players:
- name: "You"
strategy: "input"
- name: "Bot 1"
strategy: "default"
- name: "Bot 2"
strategy: "random"players:
- name: "President"
strategy: "default"
- name: "VP"
strategy: "default"
- name: "Secretary"
strategy: "random"
- name: "Vice-Asshole"
strategy: "random"
- name: "Asshole"
strategy: "input"players:
- name: "Random 1"
strategy: "random"
- name: "Random 2"
strategy: "random"
- name: "Random 3"
strategy: "random"
- name: "Random 4"
strategy: "random"- Start with low cards: Don't waste your high cards early
- Save Twos: Twos are the highest cards, save them to beat powerful plays
- Plan your exit: Try to play out cards so you can go out first
- Watch the passing phase: As Asshole, you must give away your best cards!
- Use
--delay-ms: If the game moves too fast to follow, add a delay
- Assumes exactly 5 players for full role system
- No shuffle option for seating order between games
- No tournament or league mode (runs infinitely until interrupted)
- Card passing assumes all roles are present
- No seeded shuffling for reproducible games (seed generated but not used)
- Database uses SQLite (future: PostgreSQL/MySQL support)
For developers interested in extending Roosevelt:
- Adding strategies: Implement the
Strategytrait instrategies/src/lib.rs - Modifying game rules: Edit
types/src/game_state.rs - CLI changes: Update
simulation/src/bin/run_simulation.rs - Database integration: Use
database::DatabaseWritertrait for persistence (seedatabase/README.mdfor details)
The project uses a Rust workspace with four crates:
types- Core game logic and data structuresstrategies- AI player implementationssimulation- CLI game runnerdatabase- Database persistence layer
The database tracks:
- Players: Unique player IDs with timestamps
- Games: Start/end times, deck seeds, player order, configuration
- Game Results: Player finishing place and role
- Actions: All plays (card plays, sends, passes) with turn order and phase
- Failed Writes: Database write errors for debugging
When using a persistent database:
- On first run, prompts to create new player records
- On subsequent runs:
- Checks if player name exists in database
- Prompts: "Found existing player: (). Reuse existing player? (y/n)"
- If
y: Reuses existing player ID - If
n: Creates new player with new UUID
- Use
--auto-reuse-playersto skip prompts and auto-reuse - Use
--force-new-playersto always create new records
Run the test suite:
# Run all tests
cargo test --workspace
# Run database tests
cargo test --package database
# Run simulation integration tests
cargo test --package simulation --test integration_tests
# Run with output
cargo test --workspace -- --nocaptureSee LICENSE file for details.
Contributions welcome! Areas for improvement:
- Additional AI strategies
- Tournament mode
- Network multiplayer
- GUI interface
- Statistics and replay analysis