From 867d6dce73e72a4cca5d4563950238a71c9bdb35 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Fri, 15 Jul 2022 23:31:17 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 6 + Cargo.toml | 13 ++ src/main.rs | 490 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 510 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9852c78 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "sudk" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dc0c9d9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sudk" +version = "0.1.0" +authors = ["ddidderr "] +edition = "2021" + +[dependencies] + +[profile.release] +lto = true +debug = false +codegen-units = 1 +panic = "abort" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..adbabb7 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,490 @@ +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, + num_fields: usize, +} + +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,]; + */ + + // MANY SOLUTIONS + 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 + // 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 + // 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); + field.iter().for_each(|e| { + if *e == 0 { + fixed.push(0); + } else { + fixed.push(1); + } + }); + + 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, + } + } + + pub fn solve_backtracking(&mut self) -> bool { + let mut found_solution = false; + let mut num_solutions = 0; + + loop { + //println!("loop start"); + //println!("=========================="); + //std::thread::sleep(Duration::from_millis(100)); + //self.print_clear(); + //println!("pos:{}", self.pos); + //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(); + self.prev(); + self.goto_prev_free_field(); + } + + 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"); + 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; + } + } + } + } + + found_solution + } + + #[inline(always)] + fn print_gray(&self) { + print!("\x1b\x5b\x31\x3b\x33\x30\x6d"); + } + + #[inline(always)] + fn print_green(&self) { + print!("\x1b\x5b\x31\x3b\x33\x32\x6d"); + } + + #[inline(always)] + fn print_red(&self) { + print!("\x1b\x5b\x31\x3b\x33\x31\x6d"); + } + + #[inline(always)] + fn print_neutral(&self) { + print!("\x1b\x5b\x31\x3b\x30\x6d"); + } + + #[inline(always)] + fn print_clear(&self) { + print!("\x1b\x5b\x48\x1b\x5b\x32\x4a"); + } + + #[inline(always)] + fn print(&self) { + for i in 0..self.size * self.size { + if i != 0 && i % self.size == 0 { + println!(); + } + + if i == self.pos_last_placed { + self.print_red(); + } else if i == self.pos { + self.print_green(); + } else if self.get_field_at_pos(i) == 0 { + self.print_gray(); + } + + print!("{} ", 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(); + } + } + println!(); + } + + #[inline(always)] + fn clear_current_field(&mut self) { + self.set(0); + } + + fn goto_prev_free_field(&mut self) -> bool { + while { + if !self.is_fixed() { + return true; + } + self.prev() + } {} + + false + } + + fn goto_next_free_field(&mut self) -> bool { + while { + if !self.is_fixed() { + return true; + } + self.next() + } {} + + false + } + + fn goto_next_free_field_not_known(&mut self) -> bool { + while { + if !self.is_fixed() && !self.is_known() { + return true; + } + self.next() + } {} + + false + } + + 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; + } + } + false + } + + #[inline(always)] + fn is_end(&self) -> bool { + !self.field.contains(&0) + } + + #[inline(always)] + fn ok(&self, nr: usize) -> bool { + self.block_ok(nr) && self.row_ok(nr) && self.col_ok(nr) + } + + #[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 { + 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)); + } + 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)); + } + col + } + + #[inline(always)] + fn get_block(&self) -> Vec { + let mut block = Vec::with_capacity(self.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; + + 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 + } + + #[inline(always)] + fn block_ok(&self, nr: usize) -> bool { + !self.get_block().contains(&nr) + } + + #[inline(always)] + fn row_ok(&self, nr: usize) -> bool { + !self.get_row().contains(&nr) + } + + #[inline(always)] + fn col_ok(&self, nr: usize) -> bool { + !self.get_col().contains(&nr) + } + + #[inline(always)] + fn next(&mut self) -> bool { + if self.pos == self.num_fields - 1 { + return false; + } + + self.pos += 1; + true + } + + #[inline(always)] + fn prev(&mut self) -> bool { + if self.pos == 0 { + return false; + } + + self.pos -= 1; + 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"); + 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.solve(); + //field.get_row_missing_numbers(&Pos{row:4, col:5}); + Ok(()) +} + +fn main() { + if let Err(e) = run() { + println!("{}", e) + } +}