diff --git a/src/day10.rs b/src/day10.rs index bf8f39c..50ac2fb 100644 --- a/src/day10.rs +++ b/src/day10.rs @@ -29,12 +29,12 @@ impl TrailMap { .collect_vec() } fn count_reachable_from(&self, pos: &(i64, i64), needle: u8, visited: &mut Grid) -> u64 { - if visited.get(pos.0, pos.1) == Some(true) { + if visited.get(pos) == Some(true) { return 0; } else { - visited.set(pos.0, pos.1, true); + visited.set(pos, true); } - let our_val = self.map.get(pos.0, pos.1).unwrap(); + let our_val = self.map.get(pos).unwrap(); if our_val == needle { return 1; } @@ -42,7 +42,7 @@ impl TrailMap { [(-1, 0), (1, 0), (0, -1), (0, 1)] // left, right, up, down .iter() .map(|(x_ofs, y_ofs)| (pos.0 + x_ofs, pos.1 + y_ofs)) // get target position - .map(|(x, y)| ((x, y), self.map.get(x, y))) // get value at that position + .map(|target_pos| (target_pos, self.map.get(&target_pos))) // get value at that position .filter(|(_, val)| *val == Some(our_val + 1)) // only interested if it's our value + 1 .map(|(pos, _)| pos) // discard the value .map(|pos| self.count_reachable_from(&pos, needle, visited)) @@ -50,14 +50,14 @@ impl TrailMap { } fn count_paths_to(&self, pos: &(i64, i64), needle: u8) -> u64 { - let our_val = self.map.get(pos.0, pos.1).unwrap(); + let our_val = self.map.get(pos).unwrap(); if our_val == needle { return 1; } [(-1, 0), (1, 0), (0, -1), (0, 1)] // left, right, up, down .iter() .map(|(x_ofs, y_ofs)| (pos.0 + x_ofs, pos.1 + y_ofs)) // get target position - .map(|(x, y)| ((x, y), self.map.get(x, y))) // get value at that position + .map(|target_pos| (target_pos, self.map.get(&target_pos))) // get value at that position .filter(|(_, val)| *val == Some(our_val + 1)) // only interested if it's our value + 1 .map(|(pos, _)| pos) // discard the value .map(|mov| self.count_paths_to(&mov, needle)) diff --git a/src/day6.rs b/src/day6.rs index e6c2cd5..39a60ea 100644 --- a/src/day6.rs +++ b/src/day6.rs @@ -1,5 +1,7 @@ use aoc_runner_derive::{aoc, aoc_generator}; use bitflags::bitflags; +use rayon::iter::ParallelIterator; +use rayon::slice::ParallelSlice; use std::fmt; use std::io::{BufRead, Lines}; use std::ops::BitAnd; @@ -14,10 +16,10 @@ pub fn get_input(input: &[u8]) -> Map { #[repr(u8)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] enum FacingDirection { - Up = b'^', - Down = b'v', - Left = b'<', - Right = b'>', + Up = 1, + Down = 2, + Left = 4, + Right = 8, } impl FacingDirection { @@ -45,6 +47,7 @@ enum StepOutcome { Continue, } +#[derive(Eq, PartialEq)] enum RunOutcome { LeftMap, LoopFound, @@ -98,7 +101,7 @@ impl From> for Map { let grid = Grid::from(input); let mut visited_from: Grid = Grid::new(grid.width() as i64); visited_from.data.resize(grid.data.len(), DirectionSet::empty()); - let guard_pos = grid.find(b'^').expect("Guard not found"); + let guard_pos = grid.find(&b'^').expect("Guard not found"); let guard_facing = FacingDirection::Up; Self { grid, @@ -112,31 +115,24 @@ impl From> for Map { impl Map { fn look(&self, dir: &FacingDirection) -> Option { - match dir { - FacingDirection::Up => self.grid.get(self.guard_pos.0, self.guard_pos.1 - 1), - FacingDirection::Down => self.grid.get(self.guard_pos.0, self.guard_pos.1 + 1), - FacingDirection::Left => self.grid.get(self.guard_pos.0 - 1, self.guard_pos.1), - FacingDirection::Right => self.grid.get(self.guard_pos.0 + 1, self.guard_pos.1), - } + self.grid.get(&dir.pos_ofs(self.guard_pos)) } /// Move one step in the facing direction, return if we are still inside the bounds fn step_guard(&mut self) -> StepOutcome { let new_pos = self.guard_facing.pos_ofs(self.guard_pos); if self .visited_from - .get(new_pos.0, new_pos.1) + .get(&new_pos) .is_some_and(|dirs| dirs.contains(self.guard_facing.into())) { - return StepOutcome::LoopFound; - } - if self.grid.set(new_pos.0, new_pos.1, b'X') { + StepOutcome::LoopFound + } else if self.grid.set(&new_pos, b'X').is_some() { if RECORD_PATH { self.path.push((new_pos, self.guard_facing)); } self.visited_from.set( - new_pos.0, - new_pos.1, - self.visited_from.get(new_pos.0, new_pos.1).unwrap() | self.guard_facing.into(), + &new_pos, + self.visited_from.get(&new_pos).unwrap() | self.guard_facing.into(), ); self.guard_pos = new_pos; StepOutcome::Continue @@ -168,7 +164,7 @@ pub fn part1(map: &Map) -> u64 { let mut map = map.clone(); map.run_guard::(); - (map.grid.count(b'X') + map.grid.count(b'-') + map.grid.count(b'|') + map.grid.count(b'^')) as u64 + map.grid.count(&b'X') as u64 + 1 // 'X' path positions + 1 starting position } // PROBLEM 2 solution @@ -178,28 +174,19 @@ pub fn part2(input_map: &Map) -> u64 { let mut path_map = input_map.clone(); path_map.run_guard::(); - let mut tested_position: grid::Grid = grid::Grid::new(path_map.grid.width() as i64); - tested_position.data.resize(path_map.grid.data.len(), false); - - let mut loop_count = 0u64; - let mut last_posdir = (input_map.guard_pos, input_map.guard_facing); - - for ((x, y), dir) in path_map.path.iter() { - if !tested_position.get(*x, *y).unwrap() { - tested_position.set(*x, *y, true); + path_map + .path + .par_windows(2) + .filter(|prev_cur| { + let last_posdir = prev_cur[0]; let mut test_map = input_map.clone(); - test_map.grid.set(*x, *y, b'#'); + test_map.grid.set(&prev_cur[1].0, b'#').unwrap(); test_map.guard_pos = last_posdir.0; test_map.guard_facing = last_posdir.1; - if let RunOutcome::LoopFound = test_map.run_guard::() { - loop_count += 1 - } - last_posdir = ((*x, *y), *dir); - } - } - - loop_count + test_map.run_guard::() == RunOutcome::LoopFound + }) + .count() as u64 } #[cfg(test)] diff --git a/src/day8.rs b/src/day8.rs index 3e2638c..73fe09d 100644 --- a/src/day8.rs +++ b/src/day8.rs @@ -43,7 +43,7 @@ impl AntennaMap { let offset = (a.0 - b.0, a.1 - b.1); for i in (start..).map_while(|i| if Some(i - start) != reps { Some(i as i64) } else { None }) { let node_pos = (a.0 + i * offset.0, a.1 + i * offset.1); - if !antinodes.set(node_pos.0, node_pos.1, true) { + if antinodes.set(&node_pos, true).is_none() { // left the grid break; } @@ -58,14 +58,14 @@ impl AntennaMap { #[aoc(day8, part1)] pub fn part1(map: &AntennaMap) -> u64 { let antinodes = map.find_antinodes(1, Some(1)); - antinodes.count(true) as u64 + antinodes.count(&true) as u64 } // PROBLEM 2 solution #[aoc(day8, part2)] pub fn part2(map: &AntennaMap) -> u64 { let antinodes = map.find_antinodes(0, None); - antinodes.count(true) as u64 + antinodes.count(&true) as u64 } #[cfg(test)] diff --git a/utils/grid/lib.rs b/utils/grid/lib.rs index 90255f1..91e174f 100644 --- a/utils/grid/lib.rs +++ b/utils/grid/lib.rs @@ -4,6 +4,42 @@ use std::{ iter::repeat, }; +#[derive(Clone)] +pub struct Coord2d { + pub x: i64, + pub y: i64, +} + +pub trait AsCoord2d { + fn to_coord(self) -> Coord2d; + fn x(&self) -> i64; + fn y(&self) -> i64; +} + +impl AsCoord2d for Coord2d { + fn to_coord(self) -> Coord2d { + self + } + fn x(&self) -> i64 { + self.x + } + fn y(&self) -> i64 { + self.y + } +} + +impl AsCoord2d for (i64, i64) { + fn to_coord(self) -> Coord2d { + Coord2d { x: self.0, y: self.1 } + } + fn x(&self) -> i64 { + self.0 + } + fn y(&self) -> i64 { + self.1 + } +} + #[derive(Clone)] pub struct Grid { pub data: Vec, @@ -29,8 +65,8 @@ impl Grid { pub fn height(&self) -> usize { return self.data.len() / self.width(); } - fn pos(&self, x: i64, y: i64) -> i64 { - y * self.width + x + fn pos(&self, c: &C) -> i64 { + c.y() * self.width + c.x() } pub fn coord(&self, pos: i64) -> Option<(i64, i64)> { if pos < 0 || pos >= self.data.len() as i64 { @@ -39,57 +75,58 @@ impl Grid { Some((pos % self.width, pos / self.width)) } } - fn valid_pos(&self, x: i64, y: i64) -> Option { - if x < 0 || x >= self.width { + fn valid_pos(&self, c: &C) -> Option { + if c.x() < 0 || c.x() >= self.width { return None; } - if y < 0 || y >= self.data.len() as i64 / self.width { + if c.y() < 0 || c.y() >= self.data.len() as i64 / self.width { return None; } - let pos = self.pos(x, y); + let pos = self.pos(c); if pos < 0 || pos as usize >= self.data.len() { return None; } - self.pos(x, y).try_into().ok() + self.pos(c).try_into().ok() } - pub fn get(&self, x: i64, y: i64) -> Option { - match self.valid_pos(x, y) { + pub fn get(&self, c: &C) -> Option { + match self.valid_pos(c) { Some(pos) => Some(self.data[pos]), None => None, } } - pub fn set(&mut self, x: i64, y: i64, val: T) -> bool { - match self.valid_pos(x, y) { + pub fn set(&mut self, c: &C, val: T) -> Option { + match self.valid_pos(c) { Some(pos) => { + let res = Some(self.data[pos]); self.data[pos] = val; - true + res } - None => false, + None => None, } } pub fn row(&self, y: i64) -> Option<&[T]> { if y < self.height() as i64 { - Some(&self.data[self.pos(0, y) as usize..self.pos(self.width, y) as usize]) + Some(&self.data[self.pos(&(0, y)) as usize..self.pos(&(self.width, y)) as usize]) } else { None } } - pub fn find(&self, haystack: T) -> Option<(i64, i64)> { + pub fn find(&self, haystack: &T) -> Option<(i64, i64)> { self.coord( self.data .iter() .enumerate() - .find_map(|(pos, val)| if *val == haystack { Some(pos as i64) } else { None }) + .find_map(|(pos, val)| if val == haystack { Some(pos as i64) } else { None }) .unwrap_or(-1), ) } - pub fn count(&self, haystack: T) -> usize { - self.data.iter().filter(|item| **item == haystack).count() + pub fn count(&self, haystack: &T) -> usize { + self.data.iter().filter(|item| *item == haystack).count() } - pub fn forward_slice(&self, x: i64, y: i64, len: i64) -> Option<&[T]> { - let pos = (self.valid_pos(x, y), self.valid_pos(x + len - 1, y)); + pub fn forward_slice(&self, start: &C, len: i64) -> Option<&[T]> { + let pos = (self.valid_pos(start), self.valid_pos(&(start.x() + len - 1, start.y()))); match pos { (Some(pos1), Some(pos2)) => Some(&self.data[pos1..pos2 + 1]), _ => None, @@ -151,7 +188,7 @@ impl Display for Grid { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { for y in 0..self.height() { for x in 0..self.width() { - f.write_fmt(format_args!("{}",self.get(x as i64, y as i64).unwrap() as char))?; + f.write_fmt(format_args!("{}", self.get(&(x as i64, y as i64)).unwrap() as char))?; } f.write_char('\n')?; } @@ -181,21 +218,21 @@ FBCG"; #[test] fn indexing() { let grid = unchecked_load(); - assert_eq!(grid.get(0, 0), Some(b'A')); - assert_eq!(grid.get(3, 3), Some(b'G')); - assert_eq!(grid.get(-1, 0), None); - assert_eq!(grid.get(0, -1), None); - assert_eq!(grid.get(5, 0), None); - assert_eq!(grid.get(0, 5), None); + assert_eq!(grid.get(&(0, 0)), Some(b'A')); + assert_eq!(grid.get(&(3, 3)), Some(b'G')); + assert_eq!(grid.get(&(-1, 0)), None); + assert_eq!(grid.get(&(0, -1)), None); + assert_eq!(grid.get(&(5, 0)), None); + assert_eq!(grid.get(&(0, 5)), None); } #[test] fn forward_slice() { let grid = unchecked_load(); - assert_eq!(grid.forward_slice(0, 0, 2), Some(b"AB".as_slice())); - assert_eq!(grid.forward_slice(2, 0, 2), Some(b"CD".as_slice())); - assert_eq!(grid.forward_slice(2, 0, 3), None); - assert_eq!(grid.forward_slice(0, 2, 4), Some(b"IJKL".as_slice())); + assert_eq!(grid.forward_slice(&(0, 0), 2), Some(b"AB".as_slice())); + assert_eq!(grid.forward_slice(&(2, 0), 2), Some(b"CD".as_slice())); + assert_eq!(grid.forward_slice(&(2, 0), 3), None); + assert_eq!(grid.forward_slice(&(0, 2), 4), Some(b"IJKL".as_slice())); } #[test]