:)

factor out some of the generic spatial code

+77 -84
+4 -84
src/day4.rs
··· 1 - use itertools::Itertools; 1 + use crate::spatial::*; 2 2 3 3 pub fn day4_part1(input: &str) -> String { 4 4 let roll_locations = parse(input); 5 - removable_rolls(&roll_locations).len().to_string() 5 + removable_rolls(&roll_locations).count().to_string() 6 6 } 7 7 //fixed point algorithm go brrr 8 8 pub fn day4_part2(input: &str) -> String { ··· 19 19 rolls_removed.to_string() 20 20 } 21 21 22 - 23 - fn removable_rolls(roll_locations: &[Vec<bool>]) -> Vec<Point> { 22 + fn removable_rolls(roll_locations: &[Vec<bool>]) -> impl Iterator<Item = Point> { 24 23 all_coords(roll_locations[0].len(), roll_locations.len()) 25 24 .filter(|coords| roll_locations[coords.row][coords.col]) 26 25 .filter(|&coords| { ··· 30 29 .filter(|&&neighbour| neighbour) 31 30 .count() 32 31 < 4 33 - }).collect() 32 + }) 34 33 } 35 34 36 35 fn parse(input: &str) -> Vec<Vec<bool>> { ··· 39 38 .map(|line| line.chars().map(|c| c == '@').collect()) 40 39 .collect() 41 40 } 42 - 43 - fn unparse(grid: &[Vec<bool>]) -> String { 44 - grid.iter() 45 - .map(|row| { 46 - row.iter() 47 - .map(|&is_roll| if is_roll { '@' } else { '.' }) 48 - .collect::<String>() 49 - }) 50 - .join("\n") 51 - } 52 - 53 - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 54 - struct Point { 55 - row: usize, 56 - col: usize, 57 - } 58 - 59 - fn all_coords(width: usize, height: usize) -> impl Iterator<Item = Point> { 60 - (0..height) 61 - .cartesian_product(0..width) 62 - .map(|(row, col)| Point { row, col }) 63 - } 64 - 65 - /// North, East, South, West, Northeast, Southeast, Southwest, Northwest 66 - fn adjacent_including_diagonals<T: Copy + std::fmt::Debug>( 67 - grid: &[Vec<T>], 68 - coords: Point, 69 - ) -> [Option<T>; 8] { 70 - [ 71 - adjacent_cardinal(grid, coords), 72 - adjacent_ordinal(grid, coords), 73 - ] 74 - .concat() 75 - .try_into() 76 - .unwrap() 77 - } 78 - 79 - ///Never Eat Shredded Wheat 80 - fn adjacent_cardinal<T: Copy>(grid: &[Vec<T>], coords: Point) -> [Option<T>; 4] { 81 - let mut retval = [const { None }; 4]; 82 - let (row, col) = (coords.row, coords.col); 83 - 84 - if row > 0 { 85 - retval[0] = Some(grid[row - 1][col]) //N 86 - } 87 - if col < grid[0].len() - 1 { 88 - retval[1] = Some(grid[row][col + 1]) //E 89 - } 90 - if row < grid.len() - 1 { 91 - retval[2] = Some(grid[row + 1][col]) //S 92 - } 93 - if col > 0 { 94 - retval[3] = Some(grid[row][col - 1]) //N 95 - } 96 - 97 - retval 98 - } 99 - 100 - /// Never Eat Shredded Wheat, rotated 45 degrees clockwise 101 - /// AKA northeast, southeast, southwest, northwest 102 - fn adjacent_ordinal<T: Copy>(grid: &[Vec<T>], coords: Point) -> [Option<T>; 4] { 103 - let mut retval = [const { None }; 4]; 104 - let (row, col) = (coords.row, coords.col); 105 - 106 - if row > 0 && col < grid[0].len() - 1 { 107 - retval[0] = Some(grid[row - 1][col + 1]) //NE 108 - } 109 - if row < grid.len() - 1 && col < grid[0].len() - 1 { 110 - retval[1] = Some(grid[row + 1][col + 1]) //SE 111 - } 112 - if row < grid.len() - 1 && col > 0 { 113 - retval[2] = Some(grid[row + 1][col - 1]) //SW 114 - } 115 - if row > 0 && col > 0 { 116 - retval[3] = Some(grid[row - 1][col - 1]) //NW 117 - } 118 - 119 - retval 120 - }
+1
src/lib.rs
··· 117 117 118 118 pub mod day3; 119 119 pub mod day4; 120 + mod spatial; 120 121 121 122 #[cfg(test)] 122 123 mod tests {
+72
src/spatial.rs
··· 1 + use itertools::Itertools; 2 + 3 + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 4 + pub struct Point { 5 + pub row: usize, 6 + pub col: usize, 7 + } 8 + 9 + pub fn all_coords(width: usize, height: usize) -> impl Iterator<Item = Point> { 10 + (0..height) 11 + .cartesian_product(0..width) 12 + .map(|(row, col)| Point { row, col }) 13 + } 14 + 15 + /// North, East, South, West, Northeast, Southeast, Southwest, Northwest 16 + /// Depends on `coords` being within bounds 17 + pub fn adjacent_including_diagonals<T: Copy + std::fmt::Debug>( 18 + grid: &[Vec<T>], 19 + coords: Point, 20 + ) -> [Option<T>; 8] { 21 + [ 22 + adjacent_cardinal(grid, coords), 23 + adjacent_ordinal(grid, coords), 24 + ] 25 + .concat() 26 + .try_into() 27 + .unwrap() 28 + } 29 + 30 + //could make these use .get() for safe out-of-bounds reads, i suppose 31 + ///Never Eat Shredded Wheat 32 + fn adjacent_cardinal<T: Copy>(grid: &[Vec<T>], coords: Point) -> [Option<T>; 4] { 33 + let mut retval = [const { None }; 4]; 34 + let (row, col) = (coords.row, coords.col); 35 + 36 + if row > 0 { 37 + retval[0] = Some(grid[row - 1][col]) //N 38 + } 39 + if col < grid[0].len() - 1 { 40 + retval[1] = Some(grid[row][col + 1]) //E 41 + } 42 + if row < grid.len() - 1 { 43 + retval[2] = Some(grid[row + 1][col]) //S 44 + } 45 + if col > 0 { 46 + retval[3] = Some(grid[row][col - 1]) //N 47 + } 48 + 49 + retval 50 + } 51 + 52 + /// Never Eat Shredded Wheat, rotated 45 degrees clockwise 53 + /// AKA northeast, southeast, southwest, northwest 54 + fn adjacent_ordinal<T: Copy>(grid: &[Vec<T>], coords: Point) -> [Option<T>; 4] { 55 + let mut retval = [const { None }; 4]; 56 + let (row, col) = (coords.row, coords.col); 57 + 58 + if row > 0 && col < grid[0].len() - 1 { 59 + retval[0] = Some(grid[row - 1][col + 1]) //NE 60 + } 61 + if row < grid.len() - 1 && col < grid[0].len() - 1 { 62 + retval[1] = Some(grid[row + 1][col + 1]) //SE 63 + } 64 + if row < grid.len() - 1 && col > 0 { 65 + retval[2] = Some(grid[row + 1][col - 1]) //SW 66 + } 67 + if row > 0 && col > 0 { 68 + retval[3] = Some(grid[row - 1][col - 1]) //NW 69 + } 70 + 71 + retval 72 + }