# turnbase-simulator - Complete API Documentation > Interactive retroglyph terminal client for the Turnbase engine, over a turnbase-match turn loop **Version:** 0.0.0 **Authors:** Matan Lurey **License:** MIT OR Apache-2.0 **Repository:** https://github.com/crates-lurey-io/turnbase **Keywords:** game-engine, turn-based, deterministic, gamedev, ai Generated: 2026-07-25 17:07:55 UTC Created by: [cargo-llms-txt](https://github.com/masinc/cargo-llms-txt) ## Table of Contents ### src/lib.rs - pub use session::run_session - pub use session::{BotOption, SessionApp, ismcts_bot, mcts_bot, random_bot, standard_bots} - pub use turnbase_match::{PlayerAgent, Simulator} - pub use ui::run - pub use ui::{PrintableGame, SimulationRunner} ### src/dashboard.rs - pub const GUTTER - pub struct Layout - impl Layout - pub fn draw_menu - pub fn menu_start - pub fn print_rows - pub fn panel - pub fn panel_inner - pub struct LogGeometry - pub fn log_geometry - pub fn actions_panel - pub fn draw_log - pub fn log_start - pub fn draw_board_stats_log ### src/session.rs - pub struct BotOption - impl BotOption - pub fn random_bot - pub fn mcts_bot - pub fn ismcts_bot - pub fn standard_bots - impl LogScroll - pub struct SessionApp - impl SessionApp - impl App for SessionApp - pub fn run_session - impl Game for tests::TwoSeat - impl PrintableGame for tests::TwoSeat - impl Game for tests::LongGame - impl PrintableGame for tests::LongGame ### src/ui.rs - pub trait PrintableGame - pub struct SimulationRunner - impl SimulationRunner - impl App for SimulationRunner - impl SimulationRunner - pub fn run --- ## README.md ### turnbase-simulator Interactive retroglyph terminal client for the Turnbase engine, over a turnbase-match turn loop Part of the [turnbase](https://github.com/crates-lurey-io/turnbase) workspace. --- ## src/lib.rs ### session::run_session ```rust pub use session::run_session; ``` ### session::{BotOption, SessionApp, ismcts_bot, mcts_bot, random_bot, standard_bots} ```rust pub use session::{BotOption, SessionApp, ismcts_bot, mcts_bot, random_bot, standard_bots}; ``` ### turnbase_match::{PlayerAgent, Simulator} ```rust pub use turnbase_match::{PlayerAgent, Simulator}; ``` ### ui::run ```rust pub use ui::run; ``` ### ui::{PrintableGame, SimulationRunner} ```rust pub use ui::{PrintableGame, SimulationRunner}; ``` ## src/dashboard.rs ### GUTTER ```rust pub const GUTTER: u16 ``` Blank cells left between adjacent panels. ### Layout ```rust pub struct Layout { pub viewport: Rect, pub stats: Rect, pub actions: Rect, pub log: Rect, pub status: Rect, } ``` The fixed panels of the dashboard. Plain rect arithmetic, not a constraint solver: viewport left 70%, stats and action menu stacked in the remaining top-right 30%, a log strip across the bottom, and a one-row status bar reserved along the very bottom edge. ### impl Layout ```rust impl Layout { pub fn new(full: Rect) -> Self; } ``` ### draw_menu ```rust pub fn draw_menu(term: &mut Terminal, rect: Rect, top_offset: u16, items: &[String], selected: usize) ``` Draws a selectable menu of `items` into `rect` (below `top_offset` rows), scrolling so the `selected` row stays visible when the list is taller than the panel, and marking the selection with a `>`. A long legal-action list (Risk's fortify options, say) otherwise clips at the panel's bottom edge and can hide the very row the cursor is on; this windows the list around the selection instead. The caller shows the total count in the panel header, so the window is a plain slice with no in-panel scroll chrome. ### menu_start ```rust pub fn menu_start(capacity: usize, len: usize, selected: usize) -> usize ``` The index of the first item visible in a `capacity`-row window over a `len`-item menu with `selected` on screen. Keeps `selected` visible: scrolls only once it would fall past the last visible row, and never past the point that leaves a blank tail. Shared by [`draw_menu`] and the click hit-test, so a click maps to the row the same window put there. ### print_rows ```rust pub fn print_rows(term: &mut Terminal, rect: Rect, top_offset: u16, lines: impl Trait) ``` Prints `lines` one per row starting `top_offset` rows below `rect`'s top edge, clipping to `rect`'s width and dropping rows past its bottom edge, so overlong content clips instead of spilling into the next panel. ### panel ```rust pub fn panel(term: &mut Terminal, rect: Rect, theme: Theme, title: &str) -> Rect ``` Draws a themed, titled border in `rect` and returns the interior content rect (the area inside the border). The interior is deliberately left on the terminal's own background rather than filled with the theme's panel color: a game draws its viewport (and the dashboard prints its rows) with the default background, so a fill would only show through on the cells nothing was drawn on, giving a patchy look. A border-only frame keeps every cell on one consistent background. Returns a degenerate (possibly zero-sized) inner rect when `rect` is too small to hold a border; the row/menu helpers all clip to it safely. ### panel_inner ```rust pub fn panel_inner(rect: Rect) -> Rect ``` The interior of a bordered panel occupying `rect`: one cell in on every side. Split out of [`panel`] so hit-testing (which pixel of the log strip a click landed on, say) derives its rects from the same arithmetic the drawing does, rather than a second copy that can drift. Degenerate (possibly zero-sized) when `rect` is too small to hold a border; the row/menu helpers all clip to it safely. ### LogGeometry ```rust #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct LogGeometry { pub text: Rect, pub bar: Option, pub visible: usize, } ``` Where the log panel's text rows and scrollbar sit inside its outer rect. Returned by [`log_geometry`] so an interactive caller can hit-test a click or a wheel event against the same rects [`draw_log`] drew. ### log_geometry ```rust pub fn log_geometry(rect: Rect, total: usize) -> LogGeometry ``` Splits the log panel at `rect` into its text and scrollbar rects for a `total`-line history. ### actions_panel ```rust pub fn actions_panel(term: &mut Terminal, rect: Rect, theme: Theme, position: Option<(usize, usize)>) -> Rect ``` Draws the actions panel frame and returns its interior rect for the caller to fill with a menu or a status line. `position`, when given, is shown as `selected/total` in the title so a scrolled menu still says how many actions there are. ### draw_log ```rust pub fn draw_log(log: &[String], term: &mut Terminal, rect: Rect, theme: Theme, offset: usize) -> usize ``` Draws the log panel: a themed frame, the tail of `log` windowed by `offset` (0 pins to the newest line, each increment scrolls one line back), and a one-cell [`Scrollbar`] on the right edge once there is more history than fits. Returns the number of visible log rows, so an interactive caller can clamp its scroll offset and size a page jump. ### log_start ```rust pub fn log_start(total: usize, visible: usize, offset: usize) -> usize ``` The index of the first visible log line for a scroll `offset` counted back from the newest line. The two coordinate systems meet here: the dashboard tracks "lines back from the tail" (so appending to the log leaves a pinned view alone), while the scrollbar and the text slice want an index from the top. ### draw_board_stats_log ```rust pub fn draw_board_stats_log(game: &G, view: &G::View, log: &[String], term: &mut Terminal, layout: &Layout, theme: Theme, log_offset: usize) -> usize where G: PrintableGame, B: Backend ``` Draws the viewport, the stats panel, and the log strip -- the parts every dashboard renders identically from one seat's `view`, each in a themed titled frame. `log_offset` scrolls the log back through history (0 pins to the newest line); the return value is the number of visible log rows, for an interactive caller to clamp scrolling against. ## src/session.rs ### BotOption ```rust pub struct BotOption { } ``` One selectable AI type for a seat. Names an AI and how to build it for a seat seed. Carried by [`SessionApp`] so the setup modal can offer a per-game set of bots (see [`random_bot`]/[`mcts_bot`]/[`ismcts_bot`]). ### impl BotOption ```rust impl BotOption { pub fn new(name: &'static str, make: impl Trait) -> Self; pub fn name(&self) -> &'static str; } ``` ### random_bot ```rust pub fn random_bot() -> BotOption ``` The uniform-random bot option, available for every game. ### mcts_bot ```rust pub fn mcts_bot() -> BotOption where G: Game, G::State: Clone, G::Action: Clone ``` The MCTS bot option, for any game whose state and actions are [`Clone`]. ### ismcts_bot ```rust pub fn ismcts_bot() -> BotOption where G: Determinize, G::State: Clone, G::Action: Clone ``` The information-set MCTS bot option, for a game that implements [`Determinize`] (so it can search under hidden information). ### standard_bots ```rust pub fn standard_bots() -> Vec> where G: Game, G::State: Clone, G::Action: Clone ``` The bot set every `Clone`-stated game can offer: [`random_bot`] and [`mcts_bot`]. A game that also implements [`Determinize`] can push [`ismcts_bot`] on top. ### impl LogScroll ```rust impl LogScroll { } ``` ### SessionApp ```rust pub struct SessionApp { } ``` An interactive session over a [`Simulator`]: the dashboard plus a setup modal, Auto/Step control, speed, reset, and a human-playable seat. Fixes the dashboard's viewing seat at the lowest human seat (or a neutral spectator if there is none) each time the match is (re)built, so a human never sees another seat's hidden information. That single fixed viewer means a hidden-info game configured with two or more human seats is local pass-and-play that shows one seat's view throughout; it is not safe for competitive hot-seat play of a game with private state (the same limitation the CLI's text `play` documents). ### impl SessionApp ```rust impl SessionApp { pub fn new(game: G, bots: Vec>, seed: u64) -> Self; pub fn with_human_seat(self, seat: usize) -> Self; pub fn with_setup_open(self, open: bool) -> Self; pub fn with_step_mode(self) -> Self; pub fn is_terminal(&self) -> bool; pub fn into_simulator(self) -> Simulator; } ``` ### impl App for SessionApp ```rust impl App for SessionApp { } ``` ### run_session ```rust pub fn run_session(app: SessionApp) -> std::io::Result<()> where G: PrintableGame + Clone, G::Action: Debug ``` Runs `app` on a real terminal via `retroglyph-crossterm` until it quits. #### Errors Returns an `std::io::Error` if the terminal backend fails to initialize. ### impl Game for TwoSeat ```rust impl Game for TwoSeat { } ``` ### impl PrintableGame for TwoSeat ```rust impl PrintableGame for TwoSeat { } ``` ### impl Game for LongGame ```rust impl Game for LongGame { } ``` ### impl PrintableGame for LongGame ```rust impl PrintableGame for LongGame { } ``` ## src/ui.rs ### PrintableGame ```rust pub trait PrintableGame: Game { fn draw_viewport(&self, view: &Self::View, term: &mut Terminal, area: Rect); fn get_stats(&self, view: &Self::View) -> Vec<(String, String)>; fn format_action(&self, action: &Self::Action) -> String; } ``` A [`Game`] that knows how to render itself, for [`SimulationRunner`]. Every method takes `&self` (the rules) and an explicit `view`: what a player or spectator is allowed to see ([`Game::view`]), never the raw `Self::State`. Games with no hidden information can have `View` be a clone of `State` (as the perfect-information examples in this workspace already do); games with hidden hands or decks get that redaction for free, since [`SimulationRunner`] always renders from one fixed seat's perspective (see [`Simulator::primary_human`]) rather than the full state. ### SimulationRunner ```rust pub struct SimulationRunner { } ``` Drives a [`Simulator`] behind a fixed dashboard. The game's own viewport sits on the left, a stats panel and (when a human is up) an action-select menu stack on the right, and a scrolling log strip runs along the bottom. Implements [`App`] for every [`Backend`], so the same runner drives a real terminal (`retroglyph-crossterm`) or an in-memory [`retroglyph_core::backend::Headless`] test session unchanged; [`run`] is the convenience entry point for the former. Escape ends the loop early regardless of whose turn it is; once the match reaches a terminal state the dashboard keeps rendering the final position (rather than exiting the instant it happens, which would hide it) and waits for Enter or Escape to close. ### impl SimulationRunner ```rust impl SimulationRunner { pub fn new(simulator: Simulator, ai_tick: Duration) -> Self; pub fn is_terminal(&self) -> bool; pub fn into_simulator(self) -> Simulator; } ``` ### impl App for SimulationRunner ```rust impl App for SimulationRunner { } ``` ### impl SimulationRunner ```rust impl SimulationRunner { } ``` ### run ```rust pub fn run(simulator: Simulator, ai_tick: Duration) -> std::io::Result<()> where G: PrintableGame, G::Action: Debug ``` Runs `simulator` on a real terminal via `retroglyph-crossterm`, polling an AI-controlled seat every `ai_tick`, until the match ends or the terminal closes. #### Errors Returns an `std::io::Error` if the terminal backend fails to initialize.