Guidance for AI coding agents working in this repository.
Rust/Yew WebAssembly app that solves the "numbers round" from the British game show Countdown. Users input 6 numbers and a target (1-999); the solver finds arithmetic solutions using +, -, *, /. Built with Yew 0.22 (CSR mode), compiled to WASM via Trunk, styled with Tailwind CSS v4.
# Prerequisites (one-time setup)
rustup target add wasm32-unknown-unknown
cargo install trunk wasm-bindgen-cli
npm install
# Run all unit tests
cargo test
# Run a single test by name (substring match)
cargo test test_name
# Run tests in a specific module
cargo test board::tests
# Check formatting (CI enforces this — must pass before merge)
cargo fmt --check
# Fix formatting
cargo fmt
# Build WASM for production
trunk build --release
# Local development server with hot-reload
trunk serveTailwind CSS compiles automatically via a Trunk pre-build hook (npm run build runs before each trunk build/trunk serve). Source: styles/main.css. Output: styles/output.css (generated and committed).
# Install dependencies (first time)
cd tests/e2e && uv sync
uv run playwright install chromium --with-deps
# Start the server for E2E tests (requires a built dist/)
python3 -m http.server 8080 --directory ./dist
# Run all E2E tests (server must be running on port 8080)
cd tests/e2e && uv run pytest tests/
# Run a single E2E test file
cd tests/e2e && uv run pytest tests/test_solver.py
# Run a single E2E test by name
cd tests/e2e && uv run pytest tests/test_solver.py -k "test_name"Push to main runs: cargo fmt --check -> cargo test (with coverage) -> trunk build --release -> E2E tests -> deploy to Azure Static Web Apps. All checks must pass.
src/
main.rs # WASM entry point, tracing setup
app.rs # Root Yew component, screen routing via use_state
lib.rs # Public library crate (game, solver modules)
components.rs # Module re-exports for GameBoard, GameProvider, ManualEntry
components/
game_board.rs # Game display, solve/compete, solution rendering
game_provider.rs # Game setup screen (random, custom split, manual entry)
manual_entry.rs # Number picker with validation, use_reducer state
game.rs # Module file
game/
board.rs # Board type, BoardBuilder (validated), BoardAdjuster (solver)
model.rs # Game type wrapping Board + target, Problem impl
solver.rs # Module file
solver/
traits.rs # Problem, Solver traits; Operation, Solution, Instruction types
iterative_deepening.rs # IDDFS solver with arena allocation
Dual-crate pattern: src/main.rs is the binary crate (UI components in src/components/). src/lib.rs is the library crate (game, solver modules). Components import from the library crate using yew_countdown_solver::game::... and yew_countdown_solver::solver::....
- Board: Exactly 6 numbers, sorted ascending. Built via
BoardBuilder(validates game rules) orBoardAdjuster(solver use, no rule validation). - Game: Board + target (1-999). Implements
Problemtrait. - IterativeDeepeningSolver: IDDFS over board states, depth 1-6. Uses
typed_arena::Arenafor allocation. - Operation:
{ left, operator, right, result }-- subtraction/division always expressed as larger op smaller.
- Use
cargo fmt(rustfmt default settings). CI enforcescargo fmt --check. - No custom rustfmt configuration exists; standard defaults apply.
- Group imports in this order: (1)
stdlibrary, (2) external crates, (3) crate/local imports. - Use
use crate::for library-internal imports withinsrc/game/andsrc/solver/. - Use
use yew_countdown_solver::for library imports from the binary crate (src/components/,src/app.rs,src/main.rs). - Use glob imports sparingly —
yew::prelude::*is acceptable; avoid elsewhere. - Conditional test imports use
#[cfg(test)]on theusestatement (seeiterative_deepening.rs:1-2).
- Types:
PascalCase(e.g.,BoardBuilder,SolutionState,GameBoardProps). - Functions/methods:
snake_case(e.g.,generate_children,is_solved). - Constants:
SCREAMING_SNAKE_CASEfor associated consts (e.g.,Board::SMALL_NUMBERS). - Yew components:
PascalCasefunction name with#[component]attribute (e.g.,fn GameBoard). - Yew props: struct named
{ComponentName}Propswith#[derive(Properties, PartialEq)]. - Test functions: descriptive
snake_casenames (e.g.,small_numbers_can_be_reused_twice).
- Use
u32for board numbers,u32for targets (validated to 1-999 range). - Use
Vec<u32>for board number storage, returned as&[u32]via accessors. - Use
Option<T>for nullable values;Result<T, E>for fallible operations. - Derive
Debug, Clone, PartialEq, Eqon domain types. AddHashwhere needed forHashSetusage. - Use
thiserror::Errorfor error enums with#[error("...")]display messages.
- Domain errors use dedicated enums:
BoardError,GameError(both derivethiserror::Error). - Builder methods return
Result<Self, Error>to allow chaining with?. .unwrap()is acceptable inDefaultimplementations and test code, but production paths should propagate errors.- Tests verify both error variant (
matches!(err, ErrorType::Variant(_))) and display message (format!("{}", err)).
- Components use function-style with
#[component]attribute. - State management:
use_statefor simple values,use_reducerfor complex state (seeManualEntry). - Callbacks: clone state handle into a block, then create
Callback::from(move |..| { ... }). - Effects:
use_effect_withfor side effects keyed on state changes. - Props: passed via
#[derive(Properties, PartialEq)]structs.
- Tests live in
#[cfg(test)] mod testsat the bottom of each source file. - Use
use super::*;in test modules. - Test helper macros (e.g.,
game!macro initerative_deepening.rs) are defined inside test modules. - Tests returning
Result<(), Error>use?for builder chains; assertion-only tests return(). - Descriptive assertion messages are required:
assert!(condition, "context message", args).
- All styling uses Tailwind CSS v4 utility classes directly in Rust
html!macro strings. - Source CSS:
styles/main.css(imports Tailwind). Output:styles/output.css(committed). - After changing CSS classes in
.rsfiles, the Trunk pre-build hook regeneratesoutput.cssautomatically. - Buttons include focus-visible ring styles for accessibility:
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2. - Use semantic HTML and ARIA attributes (
aria-label,aria-busy,role).
- Use
tracingmacros (tracing::info!,tracing::debug!) for runtime logging, notprintln!. - Tracing outputs to browser console via
tracing-web.