From 9d157745d9f3b4a3c0c1499b852d72adf91e806a Mon Sep 17 00:00:00 2001 From: ddidderr Date: Mon, 18 Jul 2022 10:29:40 +0200 Subject: [PATCH] faster --- Cargo.lock | 3 +- src/main.rs | 314 ++++++++++++++++++++-------------------------------- 2 files changed, 121 insertions(+), 196 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9852c78..5706e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "sudk" version = "0.1.0" - diff --git a/src/main.rs b/src/main.rs index adbabb7..e7fbe80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,50 +1,58 @@ -use std::time::Duration; - -type Pos = usize; - struct SField { field: Vec, fixed: Vec, - known: Vec, size: usize, block_size: usize, - pos: Pos, - pos_last_placed: Pos, + pos: usize, + pos_last_placed: usize, num_fields: usize, + possible_values: Vec>, } impl SField { pub fn new(block_size: usize) -> SField { let size = block_size * block_size; - //let field = vec![0; size * size]; - /* - 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,]; - */ + // 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, + 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, @@ -58,6 +66,7 @@ impl SField { // ]; // EASY + // #[rustfmt::skip] // let field = vec![ // 5,9,0,6,1,3,0,0,0, // 0,0,0,9,0,0,5,0,0, @@ -79,52 +88,60 @@ impl SField { } }); - let mut known = Vec::with_capacity(size * size); - field.iter().for_each(|e| { - if *e == 0 { - known.push(0); - } else { - known.push(1); - } - }); - SField { field, fixed, - known, size, block_size, pos: 0, pos_last_placed: 0, 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 { + 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 { + if self.ok(nr) { + good_ones.push(nr); + } + } + self.possible_values[idx] = good_ones; + } + self.pos = 0; + } + pub fn solve_backtracking(&mut self) -> bool { let mut found_solution = false; let mut num_solutions = 0; + //let mut loop_count = 0; loop { - //println!("loop start"); - //println!("=========================="); - //std::thread::sleep(Duration::from_millis(100)); + //std::thread::sleep_ms(30); //self.print_clear(); - //println!("pos:{}", self.pos); //self.print(); + //loop_count += 1; + //if loop_count % 1000000 == 0 { + //self.print_clear(); + //self.print(); + //} if self.is_end() { - //println!("is_end"); found_solution = true; num_solutions += 1; if num_solutions % 100 == 0 { self.print_clear(); println!("Solutions: {}", num_solutions); self.print(); - // std::thread::sleep(Duration::from_millis(1000)); } - //std::thread::sleep(Duration::from_secs(2)); if self.is_fixed() { - // println!("is end is fixed"); continue; } self.clear_current_field(); @@ -133,25 +150,22 @@ impl SField { } if self.is_fixed() { - // println!("is fixed -> next"); self.next(); continue; } if self.goto_next_free_field() { - // println!("goto_next_free_field"); if self.put_valid_nr() { - // println!("placed valid nr"); self.next(); continue; } else { - // println!("could not place valid nr...going back"); + //println!("put_valid_nr failed for pos {}", self.pos); + //std::thread::sleep_ms(300); self.clear_current_field(); if !self.prev() { println!("Number of solutions: {}", num_solutions); break; } - //println!("{} {}", self.pos.row, self.pos.col); if !self.goto_prev_free_field() { println!("Number of solutions: {}", num_solutions); break; @@ -203,7 +217,7 @@ impl SField { self.print_gray(); } - print!("{} ", self.get_field_at_pos(i)); + print!("{:2} ", self.get_field_at_pos(i)); if i == self.pos || i == self.pos_last_placed || self.get_field_at_pos(i) == 0 { self.print_neutral(); @@ -217,6 +231,7 @@ impl SField { self.set(0); } + #[inline(always)] fn goto_prev_free_field(&mut self) -> bool { while { if !self.is_fixed() { @@ -228,6 +243,7 @@ impl SField { false } + #[inline(always)] fn goto_next_free_field(&mut self) -> bool { while { if !self.is_fixed() { @@ -239,22 +255,34 @@ impl SField { false } - fn goto_next_free_field_not_known(&mut self) -> bool { - while { - if !self.is_fixed() && !self.is_known() { + #[inline(always)] + 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 { + if self.ok(nr) { + self.set(nr); return true; } - self.next() - } {} + } false } + #[inline(always)] 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 { - if self.ok(nr) { - self.set(nr); + + let possible_vals = unsafe { self.possible_values.get_unchecked(self.pos) }; + + for idx in 0..possible_vals.len() { + let nr = unsafe { possible_vals.get_unchecked(idx) }; + if *nr <= current_nr { + continue; + } + + if self.ok(*nr) { + self.set(*nr); return true; } } @@ -271,84 +299,75 @@ impl SField { self.block_ok(nr) && self.row_ok(nr) && self.col_ok(nr) } + #[inline(always)] + fn _pos_to_xy(&self) -> (usize, usize) { + let y = self.pos / self.size; + let x = self.pos % self.size; + (x + 1, y + 1) + } + #[inline(always)] fn is_fixed(&self) -> bool { unsafe { *self.fixed.get_unchecked(self.pos) == 1 } } #[inline(always)] - fn is_known(&self) -> bool { - unsafe { *self.known.get_unchecked(self.pos) == 1 } - } - - #[inline(always)] - fn get_field_at_pos(&self, pos: Pos) -> usize { + fn get_field_at_pos(&self, pos: usize) -> usize { unsafe { *self.field.get_unchecked(pos) } } #[inline(always)] fn set(&mut self, nr: usize) { - assert!(!self.is_fixed()); self.field[self.pos] = nr; self.pos_last_placed = self.pos; } #[inline(always)] - fn set_with_known(&mut self, nr: usize) { - assert!(!self.is_fixed()); - self.field[self.pos] = nr; - self.known[self.pos] = 1; - self.pos_last_placed = self.pos; - } - - #[inline(always)] - fn get_row(&self) -> Vec { - let mut row = Vec::with_capacity(self.size); - for val in 0..self.size { - row.push(self.get_field_at_pos((self.pos / self.size) * self.size + val)); + fn get_row(&self, row: &mut [usize; 9]) { + for (idx, row_elem) in row.iter_mut().enumerate() { + *row_elem = self.get_field_at_pos((self.pos / self.size) * self.size + idx); } - row } #[inline(always)] - fn get_col(&self) -> Vec { - let mut col = Vec::with_capacity(self.size); - for val in 0..self.size { - col.push(self.get_field_at_pos(val * self.size + self.pos % self.size)); + fn get_col(&self, col: &mut [usize; 9]) { + for (idx, col_elem) in col.iter_mut().enumerate() { + *col_elem = self.get_field_at_pos(idx * self.size + self.pos % self.size); } - col } #[inline(always)] - fn get_block(&self) -> Vec { - let mut block = Vec::with_capacity(self.size); - + fn get_block(&self, block: &mut [usize; 9]) { 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; for r in 0..self.block_size { for c in 0..self.block_size { - block.push( - self.get_field_at_pos(self.size * (block_start_row + r) + block_start_col + c), - ); + block[r * self.block_size + c] = + self.get_field_at_pos(self.size * (block_start_row + r) + block_start_col + c); } } - block } #[inline(always)] fn block_ok(&self, nr: usize) -> bool { - !self.get_block().contains(&nr) + let mut block = [0; 9]; + self.get_block(&mut block); + !block.contains(&nr) } #[inline(always)] fn row_ok(&self, nr: usize) -> bool { - !self.get_row().contains(&nr) + let mut row = [0; 9]; + self.get_row(&mut row); + !row.contains(&nr) } #[inline(always)] fn col_ok(&self, nr: usize) -> bool { - !self.get_col().contains(&nr) + let mut col = [0; 9]; + self.get_col(&mut col); + !col.contains(&nr) } #[inline(always)] @@ -371,115 +390,20 @@ impl SField { true } - fn get_block_nums(&mut self) -> u16 { - let mut result = 0u16; - - for num in self.get_block().into_iter() { - if num > 0 { - result |= 1 << (num - 1); - } - } - - // println!("blk: {} {:016b}", self.pos, result); - result - } - - fn get_col_nums(&mut self) -> u16 { - let mut result = 0u16; - - for num in self.get_col().into_iter() { - if num > 0 { - result |= 1 << (num - 1); - } - } - - // println!("col: {} {:016b}", self.pos, result); - result - } - - fn get_row_nums(&mut self) -> u16 { - let mut result = 0u16; - - for num in self.get_row().into_iter() { - if num > 0 { - result |= 1 << (num - 1); - } - } - - // println!("row: {} {:016b}", self.pos, result); - result - } - - fn place_nr_by_deduction_if_possible(&mut self) -> bool { - let combined = self.get_row_nums() | self.get_col_nums() | self.get_block_nums(); - - let ones = combined.count_ones(); - assert!(ones < 9); // should never have 9 ones because the field wouldn't be empty in the first place - - if ones == 8 { - let nr = combined.trailing_ones() + 1; - self.set_with_known(nr as usize); - // println!("Placing nr: {} at pos: {}", nr, self.pos); - return true; - } - - false - } - - fn solve_deduction(&mut self) -> bool { - return false; - - let mut was_able_to_place_nr = false; - - loop { - if self.goto_next_free_field_not_known() { - was_able_to_place_nr |= self.place_nr_by_deduction_if_possible(); - - if !self.next() { - if was_able_to_place_nr { - self.pos = 0; - was_able_to_place_nr = false; - } else { - self.pos = 0; - return self.is_end(); - } - } - } else { - if was_able_to_place_nr { - self.pos = 0; - was_able_to_place_nr = false; - } else { - self.pos = 0; - return self.is_end(); - } - } - } - } - - fn solve(&mut self) -> bool { - //loop { - //if self.solve_deduction() { - //break; - //} else { - self.pos = 0; - println!("Falling back to backtracking"); + #[inline(always)] + fn solve(&mut self) { if !self.solve_backtracking() { println!("there is no solution."); - return false; } - //} - //} - - self.print(); - true } } fn run() -> Result<(), String> { let mut field = SField::new(3); + field.build_possible_values_db(); + field.solve(); - //field.get_row_missing_numbers(&Pos{row:4, col:5}); Ok(()) }