diff --git a/src/main.rs b/src/main.rs index da300ba..0d57528 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,49 +1,109 @@ use std::time::Instant; -const MAX_BLOCK_SIZE: usize = 10; +#[derive(Copy, Clone, Debug)] +struct Bitset { + set: u16, +} + +impl Bitset { + pub fn new() -> Self { + Self { set: 0 } + } + + #[inline(always)] + fn get(&self, nr: usize) -> bool { + self.set & (1 << nr) != 0 + } + + #[inline(always)] + fn set(&mut self, nr: usize) { + self.set |= 1 << nr; + } + + #[inline(always)] + fn unset(&mut self, nr: usize) { + self.set &= !(1 << nr); + } +} + +const BLOCK_SIZE: usize = 4; +//const MAX_BLOCK_SIZE: usize = 10; struct SField { field: Vec, fixed: Vec, size: usize, - block_size: usize, pos: usize, pos_last_placed: usize, num_fields: usize, possible_values: Vec>, + + // For each row/col/block we create a bitset that represents the numbers + // that are already in that row, col or block. + // This way we can check very fast if a nr fits into a specific field or not. + rows: [Bitset; BLOCK_SIZE * BLOCK_SIZE], + cols: [Bitset; BLOCK_SIZE * BLOCK_SIZE], + blocks: [Bitset; BLOCK_SIZE * BLOCK_SIZE], +} + +#[inline(always)] +fn xy_to_pos(x: usize, y: usize, size: usize) -> usize { + y * size + x +} + +#[inline(always)] +fn pos_to_xy_blocknr(pos: usize, size: usize) -> (usize, usize, usize) { + let x = pos % size; + let y = pos / size; + + let block_x = x / BLOCK_SIZE; + let block_y = y / BLOCK_SIZE; + + let block_nr = block_y * BLOCK_SIZE + block_x; + + (x, y, block_nr) } impl SField { - pub fn new(block_size: usize) -> SField { - if block_size > MAX_BLOCK_SIZE { - panic!("block size cannot be larger than {}", MAX_BLOCK_SIZE); - } - - let size = block_size * block_size; + pub fn new() -> SField { + const SIZE: usize = BLOCK_SIZE * BLOCK_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, - ]; + let field = match BLOCK_SIZE { + 4 => vec![ + 0usize,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, + ], + 3 => 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, + ], + _ => panic!("Unsupported block size. Only 3 and 4 is ok for now.") + }; // MANY SOLUTIONS // #[rustfmt::skip] @@ -87,7 +147,7 @@ impl SField { // 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); @@ -96,15 +156,82 @@ impl SField { } }); + // create bitsets + // rows + let mut bit_rows = [Bitset::new(); SIZE]; + for row_nr in 0..SIZE { + let mut row = [0; SIZE]; + for count in 0..SIZE { + row[count] = field[xy_to_pos(count, row_nr, SIZE)]; + } + + row.into_iter() + .filter(|val| *val != 0) + .for_each(|val| bit_rows[row_nr].set(val - 1)); + } + + // cols + let mut bit_cols = [Bitset::new(); SIZE]; + for col_nr in 0..SIZE { + let mut col = [0; SIZE]; + for count in 0..SIZE { + col[count] = field[xy_to_pos(col_nr, count, SIZE)]; + } + + col.into_iter() + .filter(|val| *val != 0) + .for_each(|val| bit_cols[col_nr].set(val - 1)); + } + + // blocks + let mut bit_blocks = [Bitset::new(); SIZE]; + + for block_nr in 0..SIZE { + let block_start_x = block_nr * BLOCK_SIZE % SIZE; + let block_start_y = block_nr * BLOCK_SIZE / SIZE * BLOCK_SIZE; + + let mut block = [0; SIZE]; + for count in 0..SIZE { + let block_offset_x = count % BLOCK_SIZE; + let block_offset_y = count / BLOCK_SIZE; + + let field_x = block_start_x + block_offset_x; + let field_y = block_start_y + block_offset_y; + + //dbg!( + //block_nr, + //block_start_x, + //block_start_y, + //block_offset_x, + //block_offset_y, + //field_x, + //field_y, + //); + + block[count] = field[xy_to_pos(field_x, field_y, SIZE)]; + } + + block + .into_iter() + .filter(|x| *x != 0) + .for_each(|x| bit_blocks[block_nr].set(x - 1)); + } + + //dbg!(bit_rows); + //dbg!(bit_cols); + //dbg!(bit_blocks); + SField { field, fixed, - size, - block_size, + size: 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], + rows: bit_rows, + cols: bit_cols, + blocks: bit_blocks, } } @@ -148,11 +275,11 @@ impl SField { 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 num_solutions % 100 == 0 { + //self.print_clear(); + //println!("Solutions: {}", num_solutions); + //self.print(); + //} if self.is_fixed() { continue; @@ -296,8 +423,9 @@ impl SField { continue; } - if self.ok(*nr) { - self.set(*nr); + let nr = *nr; + if self.ok(nr) { + self.set(nr); return true; } } @@ -311,14 +439,11 @@ impl SField { #[inline(always)] fn ok(&self, nr: usize) -> bool { - self.block_ok(nr) && self.row_ok(nr) && self.col_ok(nr) - } + //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) + let (x, y, block_nr) = pos_to_xy_blocknr(self.pos, self.size); + + !self.rows[y].get(nr - 1) && !self.cols[x].get(nr - 1) && !self.blocks[block_nr].get(nr - 1) } #[inline(always)] @@ -340,10 +465,32 @@ impl SField { #[inline(always)] fn set(&mut self, nr: usize) { - self.field[self.pos] = nr; + let old = unsafe { self.field.get_unchecked_mut(self.pos) }; + + // if another number > 0 was in that cell, we need to remove that number from our bitsets + if *old > 0 { + let (x, y, block_nr) = pos_to_xy_blocknr(self.pos, self.size); + self.rows[y].unset(*old - 1); + self.cols[x].unset(*old - 1); + self.blocks[block_nr].unset(*old - 1); + } + + // add new nr to our bitsets if it is a meaningful (> 0) number + if nr > 0 { + let (x, y, block_nr) = pos_to_xy_blocknr(self.pos, self.size); + self.rows[y].set(nr - 1); + self.cols[x].set(nr - 1); + self.blocks[block_nr].set(nr - 1); + } + + // write the new number into the field + *old = nr; + + // remember the last placed position for debugging purposes self.pos_last_placed = self.pos; } + /* #[inline(always)] fn get_row(&self, row: &mut [usize]) { for (idx, row_elem) in row.iter_mut().enumerate() { @@ -391,6 +538,7 @@ impl SField { self.get_col(&mut col[0..self.size]); !col.contains(&nr) } + */ #[inline(always)] fn next(&mut self) -> bool { @@ -398,6 +546,7 @@ impl SField { return false; } + //println!("next {} -> {}", self.pos, self.pos + 1); self.pos += 1; true } @@ -423,7 +572,7 @@ impl SField { } fn run() -> Result<(), String> { - let mut field = SField::new(4); + let mut field = SField::new(); field.build_possible_values_db();