diff --git a/src/main.rs b/src/main.rs index 1d728f1..0684013 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,116 +1,121 @@ use std::time::Instant; +const BLOCK_SIZE: usize = 3; +const SIZE: usize = BLOCK_SIZE * BLOCK_SIZE; + struct SField { - field: Vec, - fixed: Vec, + field: Vec, + fixed: Vec, size: usize, block_size: usize, pos: usize, pos_last_placed: usize, num_fields: usize, - possible_values: Vec>, + possible_values: Vec>, } +/// Some test fields: +/// +/// EMPTY FIELD +/// let field = vec![0; SIZE * SIZE]; +/// +/// MANY SOLUTIONS +/// #[rustfmt::skip] +/// let field = vec![ +/// 0,0,0,0,0,0,0,0,9, +/// 0,0,0,0,8,9,0,2,0, +/// 0,0,0,0,2,0,4,0,0, +/// 0,0,4,0,6,0,0,0,8, +/// 0,0,0,5,0,0,0,0,0, +/// 0,6,5,0,0,2,0,7,4, +/// 0,3,0,0,0,5,0,4,0, +/// 0,0,1,8,0,0,0,0,0, +/// 0,0,8,2,0,0,0,6,0, +/// ]; +/// +/// HARD +/// #[rustfmt::skip] +/// let field = vec![ +/// 8,4,0,0,6,0,5,0,1, +/// 0,0,0,0,0,3,0,4,0, +/// 0,0,6,9,0,0,0,0,7, +/// 0,2,0,7,1,0,0,0,6, +/// 0,0,0,6,3,0,0,0,0, +/// 9,0,0,0,0,0,0,5,0, +/// 0,0,0,0,4,0,0,6,0, +/// 2,0,0,0,0,0,1,8,0, +/// 0,0,5,0,0,0,3,0,0, +/// ]; +/// +/// EASY +/// #[rustfmt::skip] +/// let field = vec![ +/// 5,9,0,6,1,3,0,0,0, +/// 0,0,0,9,0,0,5,0,0, +/// 8,0,3,0,5,7,6,4,0, +/// 0,7,5,0,0,0,4,0,6, +/// 0,6,0,7,4,0,2,0,8, +/// 2,0,8,0,0,0,7,5,3, +/// 0,0,0,5,6,1,0,7,0, +/// 0,0,1,0,7,0,9,0,0, +/// 7,3,6,0,2,0,0,0,0, +/// ]; +/// +/// // BLOCK_SIZE 4 MANY SOLUTIONS (DOESN'T FINISH) +/// #[rustfmt::skip] +/// let field = vec![ +/// 0,15,0,0,0,0,11,4,0,5,0,0,0,0,12,8, +/// 12,0,0,9,0,1,0,5,0,0,8,15,0,0,0,13, +/// 0,0,3,0,0,10,13,0,0,11,4,0,15,0,6,0, +/// 13,10,0,11,0,0,0,14,0,3,2,0,0,9,0,0, +/// 0,0,15,10,8,0,0,0,0,0,14,0,0,6,2,0, +/// 0,0,14,0,0,0,6,0,0,0,0,0,1,0,0,0, +/// 0,11,0,8,3,0,15,1,6,0,0,0,0,0,0,7, +/// 0,6,0,7,0,0,0,0,8,13,0,0,10,0,0,0, +/// 0,14,0,2,0,0,0,0,3,0,5,0,11,15,9,0, +/// 0,0,0,12,11,0,2,0,0,0,0,0,0,0,0,0, +/// 0,8,0,0,6,14,1,0,13,15,0,0,0,0,0,10, +/// 10,0,0,3,9,0,7,0,0,1,0,0,13,12,8,0, +/// 7,0,0,0,0,0,14,0,0,0,0,0,0,0,0,9, +/// 0,3,0,14,0,0,9,6,0,0,0,0,0,4,0,0, +/// 0,4,6,0,0,7,0,0,0,0,0,0,0,0,0,0, +/// 5,2,0,0,0,4,10,15,1,0,3,0,0,0,7,0, +/// ]; + impl SField { - pub fn new(block_size: usize) -> SField { - let size = block_size * block_size; + pub fn new() -> SField { + let field = vec![0; SIZE * SIZE]; - // EMPTY FIELD - // let field = vec![0; size * size]; - - // BLOCK_SIZE 4 - // #[rustfmt::skip] - // let field = vec![ - // 0,15,0,0,0,0,11,4,0,5,0,0,0,0,12,8, - // 12,0,0,9,0,1,0,5,0,0,8,15,0,0,0,13, - // 0,0,3,0,0,10,13,0,0,11,4,0,15,0,6,0, - // 13,10,0,11,0,0,0,14,0,3,2,0,0,9,0,0, - // 0,0,15,10,8,0,0,0,0,0,14,0,0,6,2,0, - // 0,0,14,0,0,0,6,0,0,0,0,0,1,0,0,0, - // 0,11,0,8,3,0,15,1,6,0,0,0,0,0,0,7, - // 0,6,0,7,0,0,0,0,8,13,0,0,10,0,0,0, - // 0,14,0,2,0,0,0,0,3,0,5,0,11,15,9,0, - // 0,0,0,12,11,0,2,0,0,0,0,0,0,0,0,0, - // 0,8,0,0,6,14,1,0,13,15,0,0,0,0,0,10, - // 10,0,0,3,9,0,7,0,0,1,0,0,13,12,8,0, - // 7,0,0,0,0,0,14,0,0,0,0,0,0,0,0,9, - // 0,3,0,14,0,0,9,6,0,0,0,0,0,4,0,0, - // 0,4,6,0,0,7,0,0,0,0,0,0,0,0,0,0, - // 5,2,0,0,0,4,10,15,1,0,3,0,0,0,7,0, - // ]; - - // MANY SOLUTIONS - #[rustfmt::skip] - let field = vec![ - 0,0,0,0,0,0,0,0,9, - 0,0,0,0,8,9,0,2,0, - 0,0,0,0,2,0,4,0,0, - 0,0,4,0,6,0,0,0,8, - 0,0,0,5,0,0,0,0,0, - 0,6,5,0,0,2,0,7,4, - 0,3,0,0,0,5,0,4,0, - 0,0,1,8,0,0,0,0,0, - 0,0,8,2,0,0,0,6,0, - ]; - - // HARD - // #[rustfmt::skip] - // let field = vec![ - // 8,4,0,0,6,0,5,0,1, - // 0,0,0,0,0,3,0,4,0, - // 0,0,6,9,0,0,0,0,7, - // 0,2,0,7,1,0,0,0,6, - // 0,0,0,6,3,0,0,0,0, - // 9,0,0,0,0,0,0,5,0, - // 0,0,0,0,4,0,0,6,0, - // 2,0,0,0,0,0,1,8,0, - // 0,0,5,0,0,0,3,0,0, - // ]; - - // EASY - // #[rustfmt::skip] - // let field = vec![ - // 5,9,0,6,1,3,0,0,0, - // 0,0,0,9,0,0,5,0,0, - // 8,0,3,0,5,7,6,4,0, - // 0,7,5,0,0,0,4,0,6, - // 0,6,0,7,4,0,2,0,8, - // 2,0,8,0,0,0,7,5,3, - // 0,0,0,5,6,1,0,7,0, - // 0,0,1,0,7,0,9,0,0, - // 7,3,6,0,2,0,0,0,0, - // ]; - - let mut fixed = Vec::with_capacity(size * size); + let mut fixed = Vec::with_capacity(SIZE * SIZE); field.iter().for_each(|e| { if *e == 0 { - fixed.push(0); + fixed.push(false); } else { - fixed.push(1); + fixed.push(true); } }); SField { field, fixed, - size, - block_size, + size: SIZE, + block_size: BLOCK_SIZE, pos: 0, pos_last_placed: 0, - num_fields: size * size, - possible_values: vec![vec![]; size * size], + num_fields: SIZE * SIZE, + possible_values: vec![vec![]; SIZE * SIZE], } } fn build_possible_values_db(&mut self) { for idx in 0..self.num_fields { - if self.fixed[idx] == 1 { + if self.fixed[idx] { continue; } self.pos = idx; // try all values between 1 and =self.size and remember the good ones let mut good_ones = Vec::with_capacity(self.size); - for nr in 1..=self.size { + for nr in 1..=(self.size as u8) { if self.ok(nr) { good_ones.push(nr); } @@ -122,26 +127,44 @@ impl SField { pub fn solve_backtracking(&mut self) -> bool { let mut found_solution = false; - let mut num_solutions = 0; - //let mut loop_count = 0; + let mut num_solutions = 0; + let mut last_num_solutions = num_solutions; + + let mut loop_count = 0; + let mut last_loop_count = loop_count; + + let mut now = Instant::now(); loop { - //std::thread::sleep_ms(30); - //self.print_clear(); - //self.print(); - //loop_count += 1; - //if loop_count % 1000000 == 0 { - //self.print_clear(); - //self.print(); - //} + loop_count += 1; + const MILLIS_PER_SEC: u128 = 1_000; + const UPDATE_DELAY_MS: u128 = 80; + if now.elapsed().as_millis() >= UPDATE_DELAY_MS { + self.print_clear(); + self.print(); + println!( + "{} loops/sec", + (loop_count - last_loop_count) * (MILLIS_PER_SEC / UPDATE_DELAY_MS) + ); + println!( + "{} solutions/sec", + (num_solutions - last_num_solutions) * (MILLIS_PER_SEC / UPDATE_DELAY_MS) + ); + last_loop_count = loop_count; + last_num_solutions = num_solutions; + now = Instant::now(); + } if self.is_end() { found_solution = true; num_solutions += 1; + + /* if num_solutions % 100 == 0 { self.print_clear(); println!("Solutions: {}", num_solutions); self.print(); } + */ if self.is_fixed() { continue; @@ -261,7 +284,7 @@ impl SField { fn _put_valid_nr(&mut self) -> bool { let current_nr = self.get_field_at_pos(self.pos); - for nr in current_nr..self.size + 1 { + for nr in current_nr..(self.size + 1) as u8 { if self.ok(nr) { self.set(nr); return true; @@ -299,7 +322,7 @@ impl SField { } #[inline(always)] - fn ok(&self, nr: usize) -> bool { + fn ok(&self, nr: u8) -> bool { self.block_ok(nr) && self.row_ok(nr) && self.col_ok(nr) } @@ -315,11 +338,11 @@ impl SField { // safety: self.pos can be used to index the field unchecked // since the only methods modifying self.pos are // `next()` and `prev()` and they do bounds checking - unsafe { *self.fixed.get_unchecked(self.pos) == 1 } + unsafe { *self.fixed.get_unchecked(self.pos) } } #[inline(always)] - fn get_field_at_pos(&self, pos: usize) -> usize { + fn get_field_at_pos(&self, pos: usize) -> u8 { // safety: // TODO // would need to mathematically explain that the calculations in @@ -328,27 +351,27 @@ impl SField { } #[inline(always)] - fn set(&mut self, nr: usize) { + fn set(&mut self, nr: u8) { self.field[self.pos] = nr; self.pos_last_placed = self.pos; } #[inline(always)] - fn get_row(&self, row: &mut [usize; 9]) { + fn get_row(&self, row: &mut [u8; SIZE]) { for (idx, row_elem) in row.iter_mut().enumerate() { *row_elem = self.get_field_at_pos((self.pos / self.size) * self.size + idx); } } #[inline(always)] - fn get_col(&self, col: &mut [usize; 9]) { + fn get_col(&self, col: &mut [u8; SIZE]) { for (idx, col_elem) in col.iter_mut().enumerate() { *col_elem = self.get_field_at_pos(idx * self.size + self.pos % self.size); } } #[inline(always)] - fn get_block(&self, block: &mut [usize; 9]) { + fn get_block(&self, block: &mut [u8; SIZE]) { let block_start_row = self.pos / self.size / self.block_size * self.block_size; let block_start_col = self.pos % self.size / self.block_size * self.block_size; @@ -361,22 +384,22 @@ impl SField { } #[inline(always)] - fn block_ok(&self, nr: usize) -> bool { - let mut block = [0; 9]; + fn block_ok(&self, nr: u8) -> bool { + let mut block = [0; SIZE]; self.get_block(&mut block); !block.contains(&nr) } #[inline(always)] - fn row_ok(&self, nr: usize) -> bool { - let mut row = [0; 9]; + fn row_ok(&self, nr: u8) -> bool { + let mut row = [0; SIZE]; self.get_row(&mut row); !row.contains(&nr) } #[inline(always)] - fn col_ok(&self, nr: usize) -> bool { - let mut col = [0; 9]; + fn col_ok(&self, nr: u8) -> bool { + let mut col = [0; SIZE]; self.get_col(&mut col); !col.contains(&nr) } @@ -407,12 +430,12 @@ impl SField { if !self.solve_backtracking() { println!("there is no solution."); } - println!("took {:.3}s", now.elapsed().as_secs_f64()); + eprintln!("took {:.3}s", now.elapsed().as_secs_f64()); } } fn run() -> Result<(), String> { - let mut field = SField::new(3); + let mut field = SField::new(); field.build_possible_values_db();