approaches/fair-expectimax/rust-engine/src/bin/gate_leaf.rsRust80 lines · 2.6 KB
// Leaf equivalence gate: reads state/value pairs emitted by the C++ fast
// leaf (cpp/leaf_trace) and compares the Rust fair leaf's uint64 bit pattern
// on every state.  Any nonzero mismatch count fails.
//
//   gate-leaf --trace <path>

use drop7_rs::board::Board;
use drop7_rs::engine::State;
use drop7_rs::leaf::{fair_leaf, LeafScratch};

use std::env;
use std::fs;

fn main() {
    let mut trace_path = String::new();
    let args: Vec<String> = env::args().collect();
    let mut i = 1;
    while i + 1 < args.len() {
        if args[i] == "--trace" {
            trace_path = args[i + 1].clone();
        }
        i += 2;
    }
    if trace_path.is_empty() {
        eprintln!("usage: gate-leaf --trace <c++ leaf trace file>");
        std::process::exit(2);
    }
    let text = fs::read_to_string(&trace_path).expect("read trace");

    let mut compared = 0u64;
    let mut mismatches = 0u64;
    let mut first_failure = String::new();
    let mut scratch = LeafScratch::default();

    let mut lines = text.lines();
    while let Some(state_line) = lines.next() {
        let Some(rest) = state_line.strip_prefix("s ") else {
            continue;
        };
        let tokens: Vec<&str> = rest.split_whitespace().collect();
        let board = Board::from_serialized(tokens[0]).expect("board parses");
        let state = State {
            board,
            next_disc: tokens[1].parse().unwrap(),
            score: 0,
            level: tokens[3].parse().unwrap(),
            moves_remaining: tokens[2].parse().unwrap(),
            moves_played: 0,
            game_over: tokens[4] == "1",
        };
        let value_line = lines.next().expect("value line");
        let hex = value_line.strip_prefix("v ").expect("value prefix");
        let expected_bits = u64::from_str_radix(hex, 16).expect("hex bits");

        let value = fair_leaf(&state, &mut scratch);
        let bits = value.to_bits();
        compared += 1;
        if bits != expected_bits {
            mismatches += 1;
            if first_failure.is_empty() {
                first_failure = format!(
                    "state {} next {} mr {}: rust {value:e} ({bits:#x}) != c++ ({expected_bits:#x})",
                    tokens[0], tokens[1], tokens[2],
                );
            }
        }
    }

    println!("leaf values compared: {compared}");
    println!("mismatches: {mismatches}");
    if mismatches > 0 {
        println!("first failure: {first_failure}");
    }
    let ok = mismatches == 0 && compared > 0;
    println!(
        "{}",
        if ok { "LEAF GATE PASSED" } else { "LEAF GATE FAILED" }
    );
    std::process::exit(if ok { 0 } else { 1 });
}