use crate::config::SymmetryType; pub fn mirror_coords( row: usize, col: usize, width: usize, height: usize, symmetry: SymmetryType, ) -> Vec<(usize, usize)> { let mut coords = vec![(row, col)]; let mirror_row = |i: usize| height.checked_sub(1).and_then(|h| h.checked_sub(i)); let mirror_col = |i: usize| width.checked_sub(1).and_then(|w| w.checked_sub(i)); match symmetry { SymmetryType::Rotational180 => { if let (Some(mr), Some(mc)) = (mirror_row(row), mirror_col(col)) { coords.push((mr, mc)); } } SymmetryType::Rotational90 => { // each 90-degree image is kept only when it lands inside the grid, // so non-square grids no longer underflow or go out of bounds if let Some(mc) = mirror_col(row) { if col < height { coords.push((col, mc)); } } if let (Some(mr), Some(mc)) = (mirror_row(row), mirror_col(col)) { coords.push((mr, mc)); } if let Some(mr) = mirror_row(col) { if row < width { coords.push((mr, row)); } } } SymmetryType::Diagonal => { if width == height { coords.push((col, row)); } } SymmetryType::Mirror => { if let Some(mc) = mirror_col(col) { coords.push((row, mc)); } } SymmetryType::None => {} } coords.sort(); coords.dedup(); coords } #[cfg(test)] mod tests { use super::*; #[test] fn rotational_180() { let coords = mirror_coords(0, 0, 5, 5, SymmetryType::Rotational180); assert_eq!(coords, vec![(0, 0), (4, 4)]); } #[test] fn rotational_180_center() { let coords = mirror_coords(2, 2, 5, 5, SymmetryType::Rotational180); assert_eq!(coords, vec![(2, 2)]); } #[test] fn rotational_90() { let coords = mirror_coords(0, 1, 5, 5, SymmetryType::Rotational90); assert!(coords.contains(&(0, 1))); assert!(coords.contains(&(1, 4))); assert!(coords.contains(&(4, 3))); assert!(coords.contains(&(3, 0))); } #[test] fn mirror_lr() { let coords = mirror_coords(1, 0, 5, 5, SymmetryType::Mirror); assert_eq!(coords, vec![(1, 0), (1, 4)]); } #[test] fn diagonal() { let coords = mirror_coords(1, 3, 5, 5, SymmetryType::Diagonal); assert_eq!(coords, vec![(1, 3), (3, 1)]); } #[test] fn rotational_90_non_square_stays_in_bounds() { let coords = mirror_coords(0, 3, 5, 7, SymmetryType::Rotational90); for (r, c) in &coords { assert!(*r < 7 && *c < 5, "point ({}, {}) out of bounds", r, c); } assert!(coords.contains(&(0, 3))); let coords2 = mirror_coords(6, 4, 5, 7, SymmetryType::Rotational90); for (r, c) in &coords2 { assert!(*r < 7 && *c < 5, "point ({}, {}) out of bounds", r, c); } } #[test] fn no_symmetry() { let coords = mirror_coords(1, 2, 5, 5, SymmetryType::None); assert_eq!(coords, vec![(1, 2)]); } }