// use std::fmt; use strum::EnumIter; use strum::IntoEnumIterator; // use strum_macros::EnumIter; #[derive(Debug, EnumIter, PartialEq, Copy, Clone)] pub enum Groups { Row, Column, Cell, } #[derive(Debug, Clone)] pub struct AnyGroup { pub size: u8, pub width: u8, pub max_index: usize, pub groups: [Vec; 3], pub row_cell: Vec, pub col_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 b_width = board_size as usize * board_size as usize; let size = b_width * b_width; let mut g = AnyGroup { size: board_size, width: board_size * board_size, max_index: size, groups: [vec![0; size], vec![0; size], vec![0; size]], row_cell: vec![0; size], col_cell: vec![0; size], /* row: vec![0; b_width * b_width], column: vec![0; size], cell: vec![0; size], */ }; g.calculate(); g } fn calculate(&mut self) { let size = self.size; for y in 0..self.width { self.row_cell[y as usize] = y as usize / size as usize; self.col_cell[y as usize] = (y as usize/ size as usize) * size as usize; for x in 0..self.width { let index = self.pos(x,y); // x as usize + y as usize * self.width as usize; self.groups[Groups::Row as usize][index] = self.pos(x, y); self.groups[Groups::Column as usize][index] = self.pos(y, x); // self.row[index] = self.pos(x, y); // self.column[index] = self.pos(y, x); let x_off = x / size; let y_off = y / size; let x_mod = x % size; let y_mod = y % size; // x is correct: // x_mod + y_mod * self.size; // y is correct: // x_off + y_off * self.size self.groups[Groups::Cell as usize][index] = self.pos(x_mod + y_mod * size, x_off + y_off * size); } } } #[must_use] #[inline] /// Convert (X,Y) to index. pub fn pos(&self, x: u8, y: u8) -> usize { x as usize + y as usize * self.width as usize } #[must_use] #[inline] /// Convert index to (X,Y). pub fn xy(&self, index: usize) -> (u8, u8) { let width = self.width as usize; ((index % width) as u8, (index / width) as u8) } /// Indexes for that Group (Row,Column,Cell) pub fn group(&self, g: Groups, row: u8) -> &[usize] { let width = self.width as usize; let start = row as usize * width; &self.groups[g as usize][start..start + width] } /// Indexes for that row pub fn row(&self, row: u8) -> &[usize] { // return slice of row: let width = self.width as usize; let start = row as usize * width; &self.groups[Groups::Row as usize][start..start + width] } /// Indexes for that column pub fn column(&self, column: u8) -> &[usize] { // return slice of row: let width = self.width as usize; let start = column as usize * width; &self.groups[Groups::Column as usize][start..start + width] } // Indexes for that cell. pub fn cell(&self, cell: u8) -> &[usize] { // return slice of cell: let width = self.width as usize; let start = cell as usize * width; &self.groups[Groups::Cell as usize][start..start + width] } #[must_use] /// Which cell contains (x,y)? pub fn which_cell(&self, x: u8, y: u8) -> u8 { (x / self.size) + (y / self.size) * self.size } /* #[deprecated="Use xy() instead"] #[must_use] /// Convert index to x,y offsets. pub fn cell_offset(&self, idx:u8) -> (u8,u8) { (idx % self.size, idx / self.size) } */ #[must_use] /// Which index for given cell and (x,y)? pub fn which_cell_index(&self, cell_index:u8, x:u8, y:u8) -> usize { let (sx,sy) = self.cell_start(cell_index); self.pos(sx + x, sy+y) } #[must_use] /// Where does a given cell index start? pub fn cell_start(&self, cell_index:u8) -> (u8,u8) { ((cell_index % self.size) * self.size, (cell_index/self.size) * self.size) } #[must_use] /// Return index of cell (x,y) /// - This uses the groups to locate the index of the group. pub fn cell_index(&self, index: u8, x: u8, y: u8) -> usize { debug_assert!( x < self.size); debug_assert!( y < self.size); let result: usize = self.groups[Groups::Cell as usize][index as usize * self.width as usize]; result + (y * self.width + x) as usize } // Possibly update this so it shows +1 on x and y // to match actual positions (rather then starting at 0). /// display Group 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!(""); }; for g in Groups::iter() { println!("{:?}:", g); for i in 0..self.width { let r = self.group(g, 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] /// Verify that each index (0..max_index) is defined in each group. /// - Verify that it is used, and only once. fn check_dynamic() { for size in 3..=5 { let g = AnyGroup::new(size); // g.display(); let mut all = vec![0 as u8; g.max_index]; // Verify the X,Y values are in the correct cell. for gr in Groups::iter() { all.fill(0); for idx in 0..g.width { let grp = g.group(gr, idx); // cell(idx); // g.display(); for indexes in grp { let (x, y) = g.xy(*indexes); assert!( all[*indexes] == 0, "{:?} Size {} Index [{}]({},{}) already seen", gr, size, *indexes, x + 1, y + 1 ); all[*indexes] = 1; if gr == Groups::Cell { assert_eq!( g.which_cell(x, y), idx, "Verify {:?} [{}]({},{}) in cell {}", gr, *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", gr, 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 ); } */ } } #[test] fn check_cells() { let size = 3; let g = AnyGroup::new(size); assert_eq!(g.cell_index(0, 0, 0), 0); } }