A high-performance Rust implementation of the Pokemon Showdown battle simulator.
This project is a 1-to-1 port of the Pokemon Showdown battle engine from TypeScript to Rust. The goal is to achieve exact behavioral parity with the original JavaScript implementation while benefiting from Rust's performance and safety guarantees.
Key Features:
- Complete battle simulation engine for Generation 9 (Scarlet/Violet) random battles
- Compatible PRNG implementation (ChaCha20-based, matching Showdown's sodium library)
- Comprehensive test suite comparing Rust output against JavaScript reference
- ~50 battles/second single-threaded, with parallel execution support via Rayon
showdown/
├── pokemon-showdown-rs/ # Rust implementation
│ ├── src/
│ │ ├── battle/ # Core battle logic
│ │ ├── battle_actions/ # Move execution, damage calculation
│ │ ├── battle_queue/ # Turn order and action queue
│ │ ├── dex/ # Move, ability, item data access
│ │ ├── pokemon/ # Pokemon state and mutations
│ │ └── ...
│ ├── data/ # JSON data files (moves, abilities, items, etc.)
│ ├── tests/ # Battle comparison test suite
│ └── examples/ # Profiling and testing utilities
├── pokemon-showdown-ts/ # Original TypeScript implementation (reference)
├── Dockerfile # Development environment
└── docker-compose.yml
- Docker and Docker Compose
- Node.js (for running comparison tests)
-
Start the Docker container:
docker-compose up -d
-
Build the project:
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo build --release 2>&1"
-
Run the test suite:
./tests/test-unified.sh 1 100
All Rust commands must be run inside the Docker container:
# Build
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo build 2>&1"
# Run tests
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo test 2>&1"
# Check for errors
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo check 2>&1"
# Run clippy lints
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo clippy 2>&1"The test infrastructure compares Rust battle output against the JavaScript reference implementation to ensure exact behavioral parity.
# Run 100 random battles and compare results
./tests/test-unified.sh 1 100# Compare JS vs Rust for a specific battle seed
./tests/compare-battles.sh 42This generates detailed logs at /tmp/js-battle-seed42-full.txt and /tmp/rust-battle-seed42-full.txt for side-by-side comparison.
The tests/minimized/ directory contains minimal reproduction cases for known behavioral differences:
# Run all minimized test cases
./tests/test-minimized-seeds.sh
# Analyze which moves/abilities/items appear in failing tests
node tests/analyze-minimized.jsCurrent benchmarks (single-threaded, release build):
| Metric | Value |
|---|---|
| Battles/sec | ~50 |
| Turns/sec | ~2,400 |
| ms/battle | ~20 |
| Avg turns/battle | ~49 |
Run the profiler:
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo run --release --example profile_battle 1000 2>&1"See PERFORMANCE.md for detailed analysis and optimization proposals.
When implementing new move/ability/item callbacks:
- Find the callback with
// TODO: Implement 1-to-1 from JScomment - Read the JavaScript implementation from the comment block
- Implement the Rust version as an exact 1-to-1 port
- Build and verify:
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo build 2>&1" ./tests/test-unified.sh 1 500
- Debug failures with:
./tests/compare-battles.sh [seed_number]
Two-phase borrow pattern (common in Rust port):
// Phase 1: Extract data immutably
let data = {
let pokemon = battle.pokemon_at(pos.0, pos.1)?;
pokemon.some_field
};
// Phase 2: Mutate
let pokemon_mut = battle.pokemon_at_mut(pos.0, pos.1)?;
pokemon_mut.modify_something(data);EventResult variants:
EventResult::Continue- Continue executionEventResult::Stop- Stop executionEventResult::Boolean(bool)- Return booleanEventResult::Number(i32)- Return numberEventResult::Null- Prevent default behavior
Enable verbose debug logging (impacts performance):
docker exec pokemon-rust-dev bash -c "cd /home/builder/workspace && cargo build --features debug-logging 2>&1"The Rust port mirrors the JavaScript architecture:
- Battle - Main battle state container
- Side - Player side (team, active Pokemon slots)
- Pokemon - Individual Pokemon state
- Dex - Data access layer for moves, abilities, items, species
- PRNG - Deterministic random number generator (ChaCha20)
- BattleActions - Move execution, damage calculation, effects
- Event System - Callback dispatch for abilities, items, moves
The port is under active development. Current focus areas:
- Implementing remaining move/ability/item callbacks
- Fixing behavioral differences found in battle comparison tests
- Improving test coverage across different battle scenarios
See BATTLE.md for detailed testing status and known issues.
MIT License - see Cargo.toml for details.
- Pokemon Showdown - Original battle simulator
- Smogon - Competitive Pokemon community