diff --git a/src/day06.rs b/src/day06.rs index 841f5ca..5a3190c 100644 --- a/src/day06.rs +++ b/src/day06.rs @@ -3,6 +3,8 @@ use std::{ fs::read_to_string, }; +use crate::utils::grid::Grid; + pub fn day_main() { let input = read_to_string("input/day06.txt").unwrap(); let input = input.trim(); @@ -11,7 +13,7 @@ pub fn day_main() { } type RiddleResult = usize; -type Point = (i32, i32); +type Point = (i64, i64); fn part1(input: &str) -> RiddleResult { let (m, pos) = parse(input); @@ -21,13 +23,9 @@ fn part1(input: &str) -> RiddleResult { visited.len() } -fn get_visited( - m: &HashMap<(i32, i32), char>, - mut pos: (i32, i32), - mut dir: char, -) -> HashSet<(i32, i32)> { +fn get_visited(m: &Grid, mut pos: Point, mut dir: char) -> HashSet { let mut visited = HashSet::new(); - while m.contains_key(&pos) { + while m.contains_key(pos) { let (x, y) = pos; visited.insert(pos); let next = match dir { @@ -37,7 +35,7 @@ fn get_visited( '>' => (x + 1, y), _ => unreachable!(), }; - if let Some('#') = m.get(&next) { + if m.contains_key(next) && '#' == m[next] { dir = match dir { '^' => '>', 'v' => '<', @@ -62,13 +60,13 @@ fn part2(input: &str) -> RiddleResult { .count() } -fn parse(input: &str) -> (HashMap, Point) { +fn parse(input: &str) -> (Grid, Point) { let mut m = HashMap::new(); let mut pos = None; input.lines().enumerate().for_each(|(y, line)| { line.char_indices().for_each(|(x, c)| { - let x = x as i32; - let y = y as i32; + let x = x as i64; + let y = y as i64; if c == '^' { pos = Some((x, y)); m.insert((x, y), '.'); @@ -77,10 +75,10 @@ fn parse(input: &str) -> (HashMap, Point) { } }); }); - (m, pos.unwrap()) + (Grid::from(m), pos.unwrap()) } -fn is_loop(m: &HashMap, block: Point, mut pos: Point, mut dir: char) -> bool { +fn is_loop(m: &Grid, block: Point, mut pos: Point, mut dir: char) -> bool { let mut visited = HashSet::new(); loop { let (x, y) = pos; @@ -92,10 +90,9 @@ fn is_loop(m: &HashMap, block: Point, mut pos: Point, mut dir: char '>' => (x + 1, y), _ => unreachable!(), }; - let next_tile = m.get(&next); - if None == next_tile { + if !m.contains_key(next) { return false; - } else if next == block || Some(&'#') == next_tile { + } else if next == block || '#' == m[next] { // we only check for loops on a collision to speed things up if visited.contains(&(pos, dir)) { return true; diff --git a/src/lib.rs b/src/lib.rs index d945a75..7c6022d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,3 +4,4 @@ pub mod day03; pub mod day04; pub mod day05; pub mod day06; +pub mod utils; diff --git a/src/utils/grid.rs b/src/utils/grid.rs new file mode 100644 index 0000000..3e00910 --- /dev/null +++ b/src/utils/grid.rs @@ -0,0 +1,109 @@ +use std::{ + collections::HashMap, + ops::{Index, IndexMut}, +}; + +/// A grid structure, indexed by (x, y) tuples. The top-left coordinate is (0, 0). +pub struct Grid { + content_width: i64, + content_height: i64, + content: Vec, +} + +type Coord = (i64, i64); + +impl Grid { + pub fn from(mut source: HashMap) -> Grid { + let x_max = source.keys().max_by_key(|c| c.0).unwrap().0; + let y_max = source.keys().max_by_key(|c| c.1).unwrap().1; + let content_width = x_max + 1; + let content_height = y_max + 1; + let mut content = Vec::with_capacity((content_width * content_height) as usize); + for y in 0..content_height { + for x in 0..content_width { + let v = source + .remove(&(x, y)) + .unwrap_or_else(|| panic!("no entry for {x}, {y}")); + content.push(v); + } + } + Grid { + content_width, + content_height, + content, + } + } + + fn index_of(&self, c: Coord) -> usize { + (c.1 * self.content_width + c.0) as usize + } + + pub fn get(&self, c: Coord) -> &T { + &self.content[self.index_of(c)] + } + pub fn get_mut(&mut self, c: Coord) -> &mut T { + let index_of = self.index_of(c); + &mut self.content[index_of] + } + + pub fn set(&mut self, c: Coord, value: T) { + let index_of = self.index_of(c); + self.content[index_of] = value; + } + + pub fn contains_key(&self, c: Coord) -> bool { + 0 <= c.0 && c.0 < self.content_width && 0 <= c.1 && c.1 < self.content_height + } +} + +impl Index for Grid { + type Output = T; + + #[inline] + fn index(&self, index: Coord) -> &Self::Output { + self.get(index) + } +} + +impl IndexMut for Grid { + fn index_mut(&mut self, index: Coord) -> &mut Self::Output { + self.get_mut(index) + } +} + +#[cfg(test)] +mod test { + use std::collections::HashMap; + + use super::Grid; + + #[test] + fn init_and_read() { + let grid: Grid = Grid::from(HashMap::from_iter([ + ((0, 0), '.'), + ((0, 1), '.'), + ((1, 0), '.'), + ((1, 1), '#'), + ])); + assert_eq!(&'.', grid.get((0, 0))); + assert_eq!(&'.', grid.get((0, 1))); + assert_eq!(&'.', grid.get((1, 0))); + assert_eq!(&'#', grid.get((1, 1))); + } + #[test] + fn mutate_by_index() { + let mut grid = generate(); + grid[(0, 1)] = 'x'; + assert_eq!(&'x', grid.get((0, 1))); + } + + fn generate() -> Grid { + let grid: Grid = Grid::from(HashMap::from_iter([ + ((0, 0), '.'), + ((0, 1), '.'), + ((1, 0), '.'), + ((1, 1), '.'), + ])); + grid + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..f7a1b46 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod grid;