sashite-feen 0.1.0

Field Expression Encoding Notation (FEEN): a compact, ASCII-only, no_std, zero-allocation validator and encoder for board-game positions in abstract strategy games, built on EPIN and SIN.
Documentation
//! Parse a position, read it, make a move on the owned model, and re-encode.
//!
//! This walks the FEEN <-> Qi round trip. FEEN and `sashite-qi` are
//! rule-agnostic: they record what a position is, not whether a move is legal.
//!
//! Requires the `alloc` feature:
//!
//! ```sh
//! cargo run --example basic --features alloc
//! ```

use sashite_feen::{encode, Feen, Side};

fn main() -> Result<(), sashite_feen::ParseError> {
    // The chess starting position. `^` marks the kings as terminal pieces; the
    // `+`/`-` prefixes are state modifiers the rule system is free to interpret.
    let start = "-rnbqk^bn-r/+p+p+p+p+p+p+p+p/8/8/8/8/+P+P+P+P+P+P+P+P/-RNBQK^BN-R / W/w";

    // Parsing borrows the input and allocates nothing.
    let feen = Feen::parse(start)?;
    println!(
        "Parsed a {}-square board holding {} pieces; {} to move.",
        feen.square_count(),
        feen.piece_count(),
        describe(feen.active_side()),
    );
    println!("Shape: {:?}", feen.shape());

    // `to_qi` materializes an owned, transformable position (this allocates).
    let position = feen.to_qi();

    // Re-encoding an unchanged position reproduces the input exactly.
    assert_eq!(encode(&position), start);

    // White's e-pawn sits at flat index 52 (rank 6, file 4); move it to e4,
    // flat index 36. Indices are row-major: index = rank * files + file.
    let pawn = *position.square(52).expect("a pawn on the e-file");
    let after = position
        .board_diff([(52, None), (36, Some(pawn))])
        .expect("both squares are on the board")
        .toggle();

    println!("\nAfter relocating the e-pawn:\n{}", encode(&after));
    Ok(())
}

/// A human-readable label for the side to move.
fn describe(side: Side) -> &'static str {
    match side {
        Side::First => "the first player (uppercase)",
        Side::Second => "the second player (lowercase)",
    }
}