added benchmark
This commit is contained in:
parent
2f3555b303
commit
9dd739ffcc
10
src/main.rs
10
src/main.rs
|
@ -21,6 +21,10 @@ struct Args {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
limit: Option<usize>, // Optional value
|
limit: Option<usize>, // Optional value
|
||||||
|
|
||||||
|
/// Solve n times and averages the results
|
||||||
|
#[arg(long)]
|
||||||
|
benchmark: Option<usize>, // Optional value
|
||||||
|
|
||||||
/// Disable randomization of the collapse
|
/// Disable randomization of the collapse
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
norand: bool,
|
norand: bool,
|
||||||
|
@ -44,7 +48,11 @@ fn main() {
|
||||||
solver.debug_mode(args.debug);
|
solver.debug_mode(args.debug);
|
||||||
solver.random_mode(!args.norand);
|
solver.random_mode(!args.norand);
|
||||||
solver.grid_display_mode(args.grid);
|
solver.grid_display_mode(args.grid);
|
||||||
|
|
||||||
|
if let Some(number) = args.benchmark {
|
||||||
|
solver.benchmark(number);
|
||||||
|
return
|
||||||
|
}
|
||||||
if let Err(reason) = solver.solve(args.limit) {
|
if let Err(reason) = solver.solve(args.limit) {
|
||||||
println!("{}", reason);
|
println!("{}", reason);
|
||||||
if args.debug {
|
if args.debug {
|
||||||
|
|
|
@ -57,10 +57,6 @@ impl Cell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_state(&self) -> Option<usize> {
|
|
||||||
self.state
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_allowed(&self) -> Vec<usize> {
|
pub fn get_allowed(&self) -> Vec<usize> {
|
||||||
self.blocking_states
|
self.blocking_states
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::ops::MulAssign;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub mod sudoku;
|
pub mod sudoku;
|
||||||
|
@ -14,7 +16,7 @@ pub enum WaveError {
|
||||||
impl fmt::Display for WaveError {
|
impl fmt::Display for WaveError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
WaveError::Contradiction => write!(f, "Error: The puzzle contradicts itself."),
|
WaveError::Contradiction => write!(f, "Error: There is a contradiction in the puzzle."),
|
||||||
WaveError::NoHistory => write!(f, "Error: The puzzle is impossible (backtracked to start)."),
|
WaveError::NoHistory => write!(f, "Error: The puzzle is impossible (backtracked to start)."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +35,77 @@ impl fmt::Display for Step {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SolveResult {
|
||||||
|
total_time: Duration,
|
||||||
|
propagation: (usize, Duration),
|
||||||
|
collapse: (usize, Duration),
|
||||||
|
backtrack: (usize, Duration),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SolveResult {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let propagation_percentage = (self.propagation.1.as_secs_f64() / self.total_time.as_secs_f64()) * 100.0;
|
||||||
|
let collapse_percentage = (self.collapse.1.as_secs_f64() / self.total_time.as_secs_f64()) * 100.0;
|
||||||
|
let backtrack_percentage = (self.backtrack.1.as_secs_f64() / self.total_time.as_secs_f64()) * 100.0;
|
||||||
|
|
||||||
|
write!(f, "# total time : {:.2?} ({:.2?}% propagation, {:.2?}% forced collapse, {:.2?}% backtrack)\n- {} propagations ({:.2?}), {} forced collapse ({:.2?}), {} backtrack ({:.2?})",
|
||||||
|
self.total_time, propagation_percentage, collapse_percentage, backtrack_percentage,
|
||||||
|
self.propagation.0, (self.propagation.1.checked_div(self.propagation.0 as u32)).unwrap_or(std::time::Duration::ZERO),
|
||||||
|
self.collapse.0, (self.collapse.1.checked_div(self.collapse.0 as u32)).unwrap_or(std::time::Duration::ZERO),
|
||||||
|
self.backtrack.0, (self.backtrack.1.checked_div(self.backtrack.0 as u32)).unwrap_or(std::time::Duration::ZERO)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign for SolveResult {
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
self.total_time += other.total_time;
|
||||||
|
self.propagation.0 += other.propagation.0;
|
||||||
|
self.propagation.1 += other.propagation.1;
|
||||||
|
self.collapse.0 += other.collapse.0;
|
||||||
|
self.collapse.1 += other.collapse.1;
|
||||||
|
self.backtrack.0 += other.backtrack.0;
|
||||||
|
self.backtrack.1 += other.backtrack.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MulAssign<f32> for SolveResult {
|
||||||
|
fn mul_assign(&mut self, factor: f32) {
|
||||||
|
// Scale the durations
|
||||||
|
self.total_time = scale_duration(self.total_time, factor);
|
||||||
|
self.propagation.1 = scale_duration(self.propagation.1, factor);
|
||||||
|
self.collapse.1 = scale_duration(self.collapse.1, factor);
|
||||||
|
self.backtrack.1 = scale_duration(self.backtrack.1, factor);
|
||||||
|
|
||||||
|
// Scale the usize counts (rounded to nearest integer)
|
||||||
|
self.propagation.0 = scale_usize(self.propagation.0, factor);
|
||||||
|
self.collapse.0 = scale_usize(self.collapse.0, factor);
|
||||||
|
self.backtrack.0 = scale_usize(self.backtrack.0, factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SolveResult {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
total_time: Duration::ZERO,
|
||||||
|
propagation: (0, Duration::ZERO),
|
||||||
|
collapse: (0, Duration::ZERO),
|
||||||
|
backtrack: (0, Duration::ZERO),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to scale `Duration` by `f32`
|
||||||
|
fn scale_duration(duration: Duration, factor: f32) -> Duration {
|
||||||
|
let nanos = duration.as_secs_f64() * (factor as f64);
|
||||||
|
Duration::from_secs_f64(nanos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to scale `usize` by `f32`
|
||||||
|
fn scale_usize(value: usize, factor: f32) -> usize {
|
||||||
|
((value as f64) * (factor as f64)).round() as usize
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Solver {
|
pub struct Solver {
|
||||||
grid: Vec<Vec<cell::Cell>>,
|
grid: Vec<Vec<cell::Cell>>,
|
||||||
history: Vec<Step>,
|
history: Vec<Step>,
|
||||||
|
@ -41,6 +114,7 @@ pub struct Solver {
|
||||||
square_size: usize,
|
square_size: usize,
|
||||||
debug_display: bool,
|
debug_display: bool,
|
||||||
grid_display: bool,
|
grid_display: bool,
|
||||||
|
benchmarking: bool,
|
||||||
collapse_option: cell::CollapseOption,
|
collapse_option: cell::CollapseOption,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +131,19 @@ impl Solver {
|
||||||
square_size: order,
|
square_size: order,
|
||||||
debug_display: false,
|
debug_display: false,
|
||||||
grid_display: false,
|
grid_display: false,
|
||||||
|
benchmarking: false,
|
||||||
collapse_option: cell::CollapseOption::Random,
|
collapse_option: cell::CollapseOption::Random,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_grid(&mut self) {
|
||||||
|
let states = (1..=self.size).collect();
|
||||||
|
let empty_grid = vec![vec![cell::Cell::new(states); self.size]; self.size];
|
||||||
|
self.grid = empty_grid.clone();
|
||||||
|
self.history = vec![];
|
||||||
|
self.last_move_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn random_mode(&mut self, collapse_random: bool) {
|
pub fn random_mode(&mut self, collapse_random: bool) {
|
||||||
if collapse_random {
|
if collapse_random {
|
||||||
self.collapse_option = cell::CollapseOption::Random;
|
self.collapse_option = cell::CollapseOption::Random;
|
||||||
|
@ -69,14 +152,35 @@ impl Solver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve(&mut self, solver_limit: Option<usize>) -> Result<(), WaveError> {
|
pub fn benchmark(&mut self, solve_number: usize) {
|
||||||
|
self.benchmarking = true;
|
||||||
|
|
||||||
|
let mut results_sum: SolveResult = SolveResult::default();
|
||||||
|
let mut error_number: usize = 0;
|
||||||
|
for _ in 0..solve_number {
|
||||||
|
println!();
|
||||||
|
self.reset_grid();
|
||||||
|
match self.solve(None) {
|
||||||
|
Ok(result) => {
|
||||||
|
results_sum += result;
|
||||||
|
}
|
||||||
|
Err(reason) => {
|
||||||
|
println!("{}", reason);
|
||||||
|
error_number += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results_sum *= 1.0 / ((solve_number - error_number) as f32);
|
||||||
|
println!("\n---------\n{}\n\n{} errors", results_sum, error_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn solve(&mut self, solver_limit: Option<usize>) -> Result<SolveResult, WaveError> {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
self.display(ui::DisplayMode::Full);
|
self.display(ui::DisplayMode::Full);
|
||||||
|
|
||||||
let mut propagation_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
|
let mut propagation_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
|
||||||
let mut collapse_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
|
let mut collapse_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
|
||||||
let mut backtrack_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
|
let mut backtrack_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
|
||||||
println!("# started");
|
|
||||||
self.debug("--------");
|
self.debug("--------");
|
||||||
|
|
||||||
while self.history.len() < self.size * self.size {
|
while self.history.len() < self.size * self.size {
|
||||||
|
@ -129,23 +233,18 @@ impl Solver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.debug("--------");
|
self.debug("--------");
|
||||||
let total_elapsed = start_time.elapsed();
|
|
||||||
let propagation_percentage = (propagation_counter.1.as_secs_f64() / total_elapsed.as_secs_f64()) * 100.0;
|
|
||||||
let collapse_percentage = (collapse_counter.1.as_secs_f64() / total_elapsed.as_secs_f64()) * 100.0;
|
|
||||||
let backtrack_percentage = (backtrack_counter.1.as_secs_f64() / total_elapsed.as_secs_f64()) * 100.0;
|
|
||||||
|
|
||||||
println!("\n# finished in {:.2?} ({:.2?}% propagation, {:.2?}% forced collapse, {:.2?}% backtrack)",
|
|
||||||
total_elapsed, propagation_percentage, collapse_percentage, backtrack_percentage
|
|
||||||
);
|
|
||||||
println!("- {} propagations ({:.2?}), {} forced collapse ({:.2?}), {} backtrack ({:.2?})",
|
|
||||||
propagation_counter.0, (propagation_counter.1.checked_div(propagation_counter.0 as u32)).unwrap_or(std::time::Duration::ZERO),
|
|
||||||
collapse_counter.0, (collapse_counter.1.checked_div(collapse_counter.0 as u32)).unwrap_or(std::time::Duration::ZERO),
|
|
||||||
backtrack_counter.0, (backtrack_counter.1.checked_div(backtrack_counter.0 as u32)).unwrap_or(std::time::Duration::ZERO)
|
|
||||||
);
|
|
||||||
if !self.grid_display || self.debug_display {
|
if !self.grid_display || self.debug_display {
|
||||||
self.display(ui::DisplayMode::Full);
|
self.display(ui::DisplayMode::Full);
|
||||||
}
|
}
|
||||||
Ok(())
|
let total_time = start_time.elapsed();
|
||||||
|
let result = SolveResult {
|
||||||
|
total_time,
|
||||||
|
propagation : propagation_counter,
|
||||||
|
collapse : collapse_counter,
|
||||||
|
backtrack : backtrack_counter,
|
||||||
|
};
|
||||||
|
println!("\n{}", result);
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collapse(&mut self) -> Result<(), WaveError> {
|
fn collapse(&mut self) -> Result<(), WaveError> {
|
||||||
|
|
|
@ -21,6 +21,9 @@ pub enum DisplayMode {
|
||||||
|
|
||||||
impl Solver {
|
impl Solver {
|
||||||
pub fn display(&self, display_mode: DisplayMode) {
|
pub fn display(&self, display_mode: DisplayMode) {
|
||||||
|
if self.benchmarking {
|
||||||
|
return
|
||||||
|
}
|
||||||
if let DisplayMode::Erase = display_mode {
|
if let DisplayMode::Erase = display_mode {
|
||||||
let height = self.size + self.square_size + 2;
|
let height = self.size + self.square_size + 2;
|
||||||
print!("\x1b[{}A", height);
|
print!("\x1b[{}A", height);
|
||||||
|
|
12
todo
Normal file
12
todo
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
benchmark 3 (n = 3000)
|
||||||
|
# total time : 10.05ms (62.03% propagation, 37.31% forced collapse, 0.12% backtrack)
|
||||||
|
- 78 propagations (79.96µs), 48 forced collapse (78.16µs), 0 backtrack (0.00ns)
|
||||||
|
|
||||||
|
benchmark 4 (n = 300)
|
||||||
|
# total time : 116.36ms (43.53% propagation, 56.03% forced collapse, 0.23% backtrack)
|
||||||
|
- 265 propagations (191.13µs), 183 forced collapse (356.23µs), 4 backtrack (66.91µs)
|
||||||
|
|
||||||
|
benchmark 5 (n = 100)
|
||||||
|
# total time : 886.94ms (31.81% propagation, 67.66% forced collapse, 0.43% backtrack)
|
||||||
|
- 743 propagations (379.77µs), 506 forced collapse (1.19ms), 27 backtrack (140.68µs)
|
Loading…
Reference in a new issue