/* New code - AnyGroup Handles any size puzzles. The old code below only handles 3x3 puzzles only! */ #[derive(Debug, Clone)] pub struct AnyGroup { pub size: u8, pub width: u8, pub max_index: usize, pub row: Vec, pub column: Vec, pub cell: Vec, } /// Find the number of digits needed to display given number. fn find_number_width(mut number: usize) -> usize { let mut size: usize = 1; number /= 10; while number > 0 { size += 1; number /= 10; } size } impl AnyGroup { pub fn new(board_size: u8) -> Self { let width = board_size as usize * board_size as usize; let mut g = AnyGroup { size: board_size, width: board_size * board_size, max_index: width * width, row: vec![0; width * width], column: vec![0; width * width], cell: vec![0; width * width], }; g.calculate(); g } pub fn pos(&self, x: u8, y: u8) -> usize { x as usize + y as usize * self.width as usize } pub fn xy(&self, index: usize) -> (u8, u8) { let width = self.width as usize; ((index % width) as u8, (index / width) as u8) } pub fn row(&self, row: u8) -> &[usize] { // return slice of row: let width = self.width as usize; let start = row as usize * width; &self.row[start..start + width] } pub fn column(&self, column: u8) -> &[usize] { // return slice of row: let width = self.width as usize; let start = column as usize * width; &self.column[start..start + width] } pub fn cell(&self, cell: u8) -> &[usize] { // return slice of cell: let width = self.width as usize; let start = cell as usize * width; &self.cell[start..start + width] } /// Which cell contains (x,y)? pub fn which_cell(&self, x: u8, y: u8) -> u8 { (x / self.size) + (y / self.size) * self.size } pub fn calculate(&mut self) { for y in 0..self.width { for x in 0..self.width { let index = x as usize + y as usize * self.width as usize; self.row[index] = self.pos(x, y); self.column[index] = self.pos(y, x); let x_off = x / self.size; let y_off = y / self.size; let x_mod = x % self.size; let y_mod = y % self.size; // x is correct: // x_mod + y_mod * self.size; // y is correct: // x_off + y_off * self.size self.cell[index] = self.pos( x_mod + y_mod * self.size, x_off + y_off * self.size, // x_mod + x_off * self.size, // y_off ); } } } // Possibly update this so it shows +1 on x and y // to match actual positions (rather then starting at 0). pub fn display(&self) { let max_index_size = find_number_width(self.width as usize * self.width as usize); let max_pos_size = find_number_width(self.width as usize); let printer = |row: u8, data: &[usize]| { print!("[{:2}: ", row); for j in data { let xy = self.xy(*j); print!( "{0:1$}({3:2$},{4:2$}) ", j, max_index_size, max_pos_size, xy.0 + 1, xy.1 + 1 ); } println!(""); }; println!("rows:"); // println!("{:?}", self.row); for i in 0..self.width { let r = self.row(i); /* print!("[{:2}]: ", i); for j in r { let xy = self.xy(*j); print!("{0:1$}({2:2},{3:2}) ", j, max_index_size, xy.0, xy.1); } println!(""); */ printer(i, r); } println!("columns:"); // println!("{:?}", self.column); for i in 0..self.width { let r = self.column(i); /* print!("[{:2}]: ", i); for j in r { let xy = self.xy(*j); print!("{0:1$}({2:2},{3:2}) ", j, max_index_size, xy.0, xy.1); } println!(""); */ printer(i, r); } println!("cells:"); // println!("{:?}", self.cell); for i in 0..self.width { let r = self.cell(i); /* print!("[{:2}]: ", i); for j in r { let xy = self.xy(*j); print!("{0:1$}({2:2},{3:2}) ", j, max_index_size, xy.0, xy.1); } println!(""); */ printer(i, r); } } } #[cfg(test)] mod tests { use crate::group::*; #[test] fn check_dynamic() { // Verify that each index (0..max_index) is defined in each group. // - Verify that it is used, and only once. for size in 3..=5 { let g = AnyGroup::new(size); let mut all = vec![0 as u8; g.max_index]; // Verify the X,Y values are in the correct cell. all.fill(0); for idx in 0..g.width { let grp = g.cell(idx); // g.display(); for indexes in grp { let (x, y) = g.xy(*indexes); assert!( all[*indexes] == 0, "Size {} Index [{}]({},{}) already seen", size, *indexes, x + 1, y + 1 ); all[*indexes] = 1; assert_eq!(g.which_cell(x, y), idx, "Verify [{}]({},{}) in cell {}", *indexes, x, y, idx); } } for indexes in 0..g.max_index { let (x, y) = g.xy(indexes); assert!( all[indexes] == 1, "Size {} Index [{}]({},{}) not seen", size, indexes, x + 1, y + 1 ); } all.fill(0); for idx in 0..g.width { let grp = g.row(idx); for indexes in grp { let (x, y) = g.xy(*indexes); assert_eq!(y, idx, "Expected row Y {} == {}", y, idx); assert!( all[*indexes] == 0, "Size {} row Index [{}]({},{}) already seen", size, *indexes, x + 1, y + 1 ); all[*indexes] = 1; } } for indexes in 0..g.max_index { let (x, y) = g.xy(indexes); assert!( all[indexes] == 1, "Size {} row Index [{}]({},{}) not seen", size, indexes, x + 1, y + 1 ); } all.fill(0); for idx in 0..g.width { let grp = g.column(idx); for indexes in grp { let (x, y) = g.xy(*indexes); assert_eq!(x, idx, "Expected column X {} == {}", x, idx); assert!( all[*indexes] == 0, "Size {} col Index [{}]({},{}) already seen", size, *indexes, x + 1, y + 1 ); all[*indexes] = 1; } } for indexes in 0..g.max_index { let (x, y) = g.xy(indexes); assert!( all[indexes] == 1, "Size {} col Index [{}]({},{}) not seen", size, indexes, x + 1, y + 1 ); } } } }