at main 161 lines 4.7 kB view raw
1use std::{num::ParseIntError, str::FromStr}; 2 3use itertools::Itertools; 4 5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 6pub struct Point { 7 pub row: usize, 8 pub col: usize, 9} 10 11impl FromStr for Point { 12 type Err = ParseIntError; 13 fn from_str(s: &str) -> Result<Self, Self::Err> { 14 let (row, col) = s.split_once(',').unwrap(); 15 Ok(Self { 16 row: row.parse()?, 17 col: col.parse()?, 18 }) 19 } 20} 21 22impl Point { 23 pub fn rectangular_area(&self, r: &Self) -> usize { 24 let height = self.row.abs_diff(r.row) + 1; 25 let width = self.col.abs_diff(r.col) + 1; 26 width * height 27 } 28} 29 30#[derive(Debug, Clone)] 31pub struct RightAngledShape { 32 corners: Vec<Point>, 33} 34 35impl RightAngledShape { 36 pub fn new(corners: &[Point]) -> Self { 37 Self { 38 corners: corners.to_vec(), 39 } 40 } 41 42 pub fn contains_rect(&self, rect: (Point, Point)) -> bool { 43 !(Self::rect_strict_contains_line( 44 rect, 45 (self.corners[0], self.corners[self.corners.len() - 1]), 46 ) || self 47 .corners 48 .windows(2) 49 .any(|window| Self::rect_strict_contains_line(rect, (window[0], window[1])))) 50 } 51 52 fn rect_strict_contains_line(rect: (Point, Point), line: (Point, Point)) -> bool { 53 let left_col = rect.0.col.min(rect.1.col); 54 let right_col = rect.0.col.max(rect.1.col); 55 let top_row = rect.0.row.min(rect.1.row); 56 let bottom_row = rect.0.row.max(rect.1.row); 57 58 if line.0.row == line.1.row { 59 //horizontal line 60 let row_cond = line.0.row >= bottom_row || line.0.row <= top_row; 61 let left_cond = line.0.col <= left_col && line.1.col <= left_col; 62 let right_cond = line.0.col >= right_col && line.1.col >= right_col; 63 64 !row_cond && !left_cond && !right_cond 65 } else { 66 //vertical line 67 let col_cond = line.0.col >= right_col || line.0.col <= left_col; 68 let above_cond = line.0.row <= top_row && line.1.row <= top_row; 69 let below_cond = line.0.row >= bottom_row && line.1.row >= bottom_row; 70 71 !col_cond && !above_cond && !below_cond 72 } 73 } 74} 75 76#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 77pub struct Point3D { 78 pub x: usize, 79 pub y: usize, 80 pub z: usize, 81} 82 83impl Point3D { 84 pub fn euclidean_distance(&self, other: &Self) -> f64 { 85 (((self.x as isize - other.x as isize).abs().pow(2) 86 + (self.y as isize - other.y as isize).abs().pow(2) 87 + (self.z as isize - other.z as isize).abs().pow(2)) as f64) 88 .sqrt() 89 } 90} 91 92pub fn all_coords(width: usize, height: usize) -> impl Iterator<Item = Point> { 93 (0..height) 94 .cartesian_product(0..width) 95 .map(|(row, col)| Point { row, col }) 96} 97 98/// North, East, South, West, Northeast, Southeast, Southwest, Northwest 99/// Depends on `coords` being within bounds 100pub fn adjacent_including_diagonals<T: Copy + std::fmt::Debug>( 101 grid: &[Vec<T>], 102 coords: Point, 103) -> [Option<T>; 8] { 104 [ 105 adjacent_cardinal(grid, coords), 106 adjacent_ordinal(grid, coords), 107 ] 108 .concat() 109 .try_into() 110 .unwrap() 111} 112 113//could make these use .get() for safe out-of-bounds reads, i suppose 114///Never Eat Shredded Wheat 115fn adjacent_cardinal<T: Copy>(grid: &[Vec<T>], coords: Point) -> [Option<T>; 4] { 116 let mut retval = [const { None }; 4]; 117 let (row, col) = (coords.row, coords.col); 118 119 if row > 0 { 120 retval[0] = Some(grid[row - 1][col]) //N 121 } 122 if col < grid[0].len() - 1 { 123 retval[1] = Some(grid[row][col + 1]) //E 124 } 125 if row < grid.len() - 1 { 126 retval[2] = Some(grid[row + 1][col]) //S 127 } 128 if col > 0 { 129 retval[3] = Some(grid[row][col - 1]) //N 130 } 131 132 retval 133} 134 135/// Never Eat Shredded Wheat, rotated 45 degrees clockwise 136/// AKA northeast, southeast, southwest, northwest 137fn adjacent_ordinal<T: Copy>(grid: &[Vec<T>], coords: Point) -> [Option<T>; 4] { 138 let mut retval = [const { None }; 4]; 139 let (row, col) = (coords.row, coords.col); 140 141 if row > 0 && col < grid[0].len() - 1 { 142 retval[0] = Some(grid[row - 1][col + 1]) //NE 143 } 144 if row < grid.len() - 1 && col < grid[0].len() - 1 { 145 retval[1] = Some(grid[row + 1][col + 1]) //SE 146 } 147 if row < grid.len() - 1 && col > 0 { 148 retval[2] = Some(grid[row + 1][col - 1]) //SW 149 } 150 if row > 0 && col > 0 { 151 retval[3] = Some(grid[row - 1][col - 1]) //NW 152 } 153 154 retval 155} 156 157pub fn transpose<S: AsRef<[T]>, T: Copy>(input: &[S]) -> Vec<Vec<T>> { 158 (0..input[0].as_ref().len()) 159 .map(|col| input.iter().map(|row| row.as_ref()[col]).collect()) 160 .collect() 161}