mod common;
use pinch_points::sim::{
DEFAULT_NODE_BUDGET, Effort, Level, PuzzleOutcome, SolveOutcome, solve_with,
};
fn unposted_outcome(level: &Level) -> (PuzzleOutcome, u64) {
level.play_out(&mut level.board())
}
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let effort = match args.iter().any(|a| a == "--exhaustive") {
true => Effort::Exhaustive,
false => Effort::Budget(DEFAULT_NODE_BUDGET),
};
let lanes = common::lanes(&args, |cores| cores / 2);
let paths = common::paths(&args);
eprintln!("{} boards on {lanes} lane(s)", paths.len());
let bar = common::bar(paths.len() as u64, "boards");
std::thread::scope(|scope| {
for lane in 0..lanes {
let mine: Vec<&String> = paths.iter().copied().skip(lane).step_by(lanes).collect();
let bar = &bar;
scope.spawn(move || {
for path in mine {
let line = report(path, effort);
bar.suspend(|| {
use std::io::Write;
print!("{line}");
let _ = std::io::stdout().flush();
});
bar.inc(1);
}
});
}
});
bar.finish_and_clear();
}
fn report(path: &str, effort: Effort) -> String {
use std::fmt::Write;
let mut out = String::new();
let text = std::fs::read_to_string(path).expect("readable level file");
let level = match Level::parse(&text) {
Ok(level) => level,
Err(e) => {
let _ = writeln!(out, "{path}: PARSE ERROR: {e}");
return out;
}
};
match Level::parse(&level.to_text()) {
Ok(back) if back.board().state_hash() == level.board().state_hash() => {}
Ok(_) => {
let _ = writeln!(out, "{path}: ROUND TRIP: rebuilds a different board");
}
Err(e) => {
let _ = writeln!(out, "{path}: ROUND TRIP: will not parse back: {e}");
}
}
let (outcome, ticks) = unposted_outcome(&level);
let _ = writeln!(out, "{path}: unposted -> {outcome:?} after {ticks} ticks");
match solve_with(&level, effort) {
SolveOutcome::Found(placements) => {
let text: Vec<String> = placements
.iter()
.map(|(x, y, d)| format!("{x},{y} {d:?}"))
.collect();
let _ = writeln!(
out,
" solver ({} posts): {}",
placements.len(),
text.join(" | ")
);
}
SolveOutcome::Unsolvable => {
let _ = writeln!(out, " solver: NO SOLUTION within {} posts", level.posts);
}
SolveOutcome::GaveUp => {
let _ = writeln!(
out,
" solver: gave up at {DEFAULT_NODE_BUDGET} simulations, too big to ship"
);
}
}
out
}