Build an AI
Bring your own Pokémon TCG AI (bot) and have it play in this arena. A reference implementation + connection client is published. Clone it and run connect, and you can put your own bot into the arena right now.
⧉ View on GitHub: PTCG_AI_Battle_Client
What is it?
- Communication with the server is pure JSON protocol (src/wire/), with no dependency on the rules engine itself. That keeps it lightweight, with no special build dependencies.
- Card facts (ex detection, attack index) are read from pokemon-card-data (a submodule).
- The bundled bots' logic doubles as a worked example of how to write a Pokémon TCG bot.
Quick start
Requires Rust 1.80+. Clone together with the submodule (card data) and build:
git clone --recurse-submodules https://github.com/1ulce/PTCG_AI_Battle_Client.git
cd PTCG_AI_Battle_Client
cargo build --release
Simplest example — challenge a built-in arena bot:
cargo run --release --bin connect -- \
--server wss://arena.ptcgtools.com \
--vs dragapult-yopifutto --bot dragapult-takeuchi \
--deck decks/dragapult-ex.yaml --games 3
Bundled bots
| Name | Description |
|---|---|
random | Picks uniformly at random from legal actions / choices. Baseline and fallback. |
dragapult-takeuchi | Fixed strategy for the Dragapult ex deck — "Kuchiito Takeuchi" persona. |
dragapult-yopifutto | Fixed strategy for the Dragapult ex deck — "Dr. Yopifutto" persona. |
Finding an opponent (intent)
| Option | Meaning |
|---|---|
--room | --room ID: pairs the two clients with the same room (best for your-own-bots). |
--vs | --vs NAME: name a built-in server bot as the opponent. |
--participant-id | --participant-id ID --auth-token TOK: ladder (rated play; token issued under "Sign up"). Join with your own --deck. |
Anonymous auto-matching (open) has been removed. Specify one of room / vs / ladder. Because each connection brings its own --deck, you can also run asymmetric matches with different decks on each side.
Time control (answer in time)
The arena runs sudden death: 10 minutes total per player + a 30-second cap per move. One move = one round-trip (request→response / prompt→choice). You lose on time (FlagFall) if a single response exceeds 30s or your total exceeds 10 min. A bot that stays connected but never answers also loses after 30s. Every request/prompt carries a clock, so you can read my_deadline_unix_ms to budget your time.
Writing your own bot
The main purpose of this repo is to be the foundation on which you write your own bot. There are only two steps.
1. Implement the BotPolicy trait in src/bots/<your_bot>.rs (choose_action picks your turn's move from req.legal_actions; choose_prompt answers choices during effect resolution).
impl BotPolicy for MyBot {
fn choose_action(&mut self, req: &RequestMsg, rng: &mut ChaCha20Rng)
-> Result<ActionDto, TransportError> {
// req.state … board masked to your view / req.legal_actions … legal moves
Ok(req.legal_actions.iter()
.find(|a| matches!(a, ActionDto::EndTurn))
.cloned()
.unwrap_or_else(|| req.legal_actions[0].clone()))
}
fn choose_prompt(&mut self, p: &PromptMsg, rng: &mut ChaCha20Rng) -> PromptChoice {
crate::bots::RandomPolicy.choose_prompt(p, rng) // when in doubt, random
}
}
2. Register it with one line each in build / available in src/bots/mod.rs. Then it runs with --bot my-bot.
// src/bots/mod.rs
&["random", "dragapult-takeuchi", "dragapult-yopifutto", "my-bot"] // available()
"my-bot" => Some(Box::new(MyBot)), // build()
What a bot can see
- req.state (StateDto): the board masked to your point of view. You see your own hand, but the opponent's hand and decks are card: null (not peekable). Field, discard, and stadium are public.
- req.legal_actions: the legal moves the server (referee) enumerated. You cannot send illegal moves, so the AI just picks from the list.
- Card facts: HP and attack indices are not entirely contained in the board DTO. CardFacts looks up is_ex / attack_index from a slug.
Discipline (faithfulness)
- Pick from legal actions. Don't invent a plausible-looking move in an unknown situation — delegating to RandomPolicy is the safe default.
- Use only ChaCha20Rng for randomness (reproducible with a fixed seed). Don't bring in non-determinism such as Instant/SystemTime.
Protocol
The complete reference for the JSON exchanged with the server is in docs/protocol.md in the repo (every message's keys, types, error codes, prompt responses, and information masking). You can implement a bot in another language just by reading it.
License: MIT OR Apache-2.0. Issues / Pull Requests welcome. Bring your own new bots or different archetype decks.