From 00fe517860fea67404cb84e5351ce8a3fbf1f3b0 Mon Sep 17 00:00:00 2001 From: red Date: Thu, 21 Mar 2024 19:30:09 +0100 Subject: [PATCH] Improvement: Year 2016: Day 13: Now using a common BFS algorithm everywhere --- CHANGELOG.md | 4 +++ Cargo.toml | 2 +- NOTES_2016.md | 2 ++ src/bfs.rs | 4 +-- src/year_2016/day_13.rs | 70 ++++++++++------------------------------- src/year_2016/day_17.rs | 12 +++---- 6 files changed, 29 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b23d6c..6c6477c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ Of note: - The changelog 2015.5.2 has been rewritten from each commit content. - This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs) +## [2016.17.2] +### Changed +- Exercise for 2016, day 13, now uses the [BreadthFirstSearch](src/bfs.rs) lib. + ## [2016.17.1] ### Added - Solved [exercice for 2016, day 17](src/year_2016/17.rs). diff --git a/Cargo.toml b/Cargo.toml index a52ae5f..aaad67a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "advent-rs" -version = "2016.17.1" +version = "2016.17.2" edition = "2021" authors = ["Arnaud 'red' Rouyer"] readme = "README.md" diff --git a/NOTES_2016.md b/NOTES_2016.md index 9db4282..5dc4432 100644 --- a/NOTES_2016.md +++ b/NOTES_2016.md @@ -80,3 +80,5 @@ Another thing of note is that if you know how to optimize your loop-down, you ca ## Day 17: Two Steps Forward A big part of this day was bout building a [BreadthFirstSearch library](src/bfs.rs) that would be flexible enough to reuse in other exercises, since there are SO MANY in this year. + +There was some confusion between all the generics, abstractions, and types, especially since this day got some strange rules (storing together position+path) but in the end, I think I got it, and implementing it on day 13 (which only got a position) was really fast, and execution time got faster too. diff --git a/src/bfs.rs b/src/bfs.rs index 018f383..e5f666c 100644 --- a/src/bfs.rs +++ b/src/bfs.rs @@ -10,7 +10,7 @@ where starting: POS, pub ending: Option, queue: VecDeque<(POS, usize)>, - visited: HashSet, + pub visited: HashSet, pub depth: usize, next_moves: NM, } @@ -59,7 +59,7 @@ where } pub fn traverse_until_depth(&mut self, target_depth: usize) -> &mut Self { - self.traverse(|_position, depth| depth == target_depth, true) + self.traverse(|_position, depth| depth > target_depth, true) } pub fn traverse_until_position(&mut self, target_position: POS) -> &mut Self { diff --git a/src/year_2016/day_13.rs b/src/year_2016/day_13.rs index dff8372..34cf8fb 100644 --- a/src/year_2016/day_13.rs +++ b/src/year_2016/day_13.rs @@ -1,26 +1,4 @@ -use std::collections::HashSet; -use std::collections::VecDeque; - -enum PositionOrCount { - Position((usize, usize)), - Count(u8), -} - -impl PositionOrCount { - fn reached_position(&self, reach: (usize, usize)) -> bool { - match self { - PositionOrCount::Position(target) => *target == reach, - PositionOrCount::Count(_) => false, - } - } - - fn reached_count(&self, steps_count: u8) -> bool { - match self { - PositionOrCount::Position(_) => false, - PositionOrCount::Count(count) => steps_count > *count, - } - } -} +use crate::bfs::BreadthFirstSearch; fn cell_is_wall(favorite: usize, position: (usize, usize)) -> bool { let x = position.0; @@ -29,42 +7,22 @@ fn cell_is_wall(favorite: usize, position: (usize, usize)) -> bool { return value.count_ones() % 2 == 1; } -fn next_moves(position: (usize, usize)) -> Vec<(usize, usize)> { +fn next_moves(favorite: usize, position: (usize, usize)) -> Vec<(usize, usize)> { let mut new_moves: Vec<(usize, usize)> = vec![]; - if position.0 > 0 { + if position.0 > 0 && !cell_is_wall(favorite, (position.0 - 1, position.1)) { new_moves.push((position.0 - 1, position.1)); } - new_moves.push((position.0 + 1, position.1)); - if position.1 > 0 { + if !cell_is_wall(favorite, (position.0 + 1, position.1)) { + new_moves.push((position.0 + 1, position.1)); + } + if position.1 > 0 && !cell_is_wall(favorite, (position.0, position.1 - 1)) { new_moves.push((position.0, position.1 - 1)); } - new_moves.push((position.0, position.1 + 1)); - - new_moves -} - -fn traverse_until(favorite: usize, reach_condition: PositionOrCount) -> u8 { - let mut visited: HashSet<(usize, usize)> = HashSet::from([(1, 1)]); - let mut queue: VecDeque<((usize, usize), u8)> = VecDeque::from([((1, 1), 0)]); - - while let Some((position, depth)) = queue.pop_front() { - if reach_condition.reached_position(position) { - return depth; - } - if reach_condition.reached_count(depth) { - return visited.len() as u8; - } - - visited.insert(position); - for next_move in next_moves(position) { - if visited.contains(&next_move) || cell_is_wall(favorite, next_move) { - continue; - } - queue.push_back((next_move, depth + 1)); - } + if !cell_is_wall(favorite, (position.0, position.1 + 1)) { + new_moves.push((position.0, position.1 + 1)); } - 0 + new_moves } pub fn day_13_v1(input: impl Into) -> u8 { @@ -73,16 +31,20 @@ pub fn day_13_v1(input: impl Into) -> u8 { let favorite = input_parts[0].parse::().unwrap(); let reach_x = input_parts[1].parse::().unwrap(); let reach_y = input_parts[2].parse::().unwrap(); + let mut bfs = BreadthFirstSearch::new((1, 1), |position| next_moves(favorite, position)); + bfs.traverse_until_position((reach_x, reach_y)); - traverse_until(favorite, PositionOrCount::Position((reach_x, reach_y))) + bfs.depth as u8 } pub fn day_13_v2(input: impl Into) -> u8 { let binding = input.into(); let input_parts: Vec<_> = binding.split(",").collect(); let favorite = input_parts[0].parse::().unwrap(); + let mut bfs = BreadthFirstSearch::new((1, 1), |position| next_moves(favorite, position)); + bfs.traverse_until_depth(50); - traverse_until(favorite, PositionOrCount::Count(50)) + bfs.visited.len() as u8 } solvable!(day_13, day_13_v1, day_13_v2, u8); diff --git a/src/year_2016/day_17.rs b/src/year_2016/day_17.rs index 81a7aeb..5893ac5 100644 --- a/src/year_2016/day_17.rs +++ b/src/year_2016/day_17.rs @@ -16,7 +16,7 @@ impl Day17Position { } } - pub fn next_moves_for_hasher(&self, mut hasher: CoreWrapper) -> Vec { + pub fn next_moves(&self, mut hasher: CoreWrapper) -> Vec { hasher.update(self.path.clone()); let positions: &[u8] = &hasher.finalize()[0..=1]; let mut next_moves = vec![]; @@ -101,9 +101,7 @@ pub fn day_17_v1(input: impl Into) -> String { let mut hasher = Md5::new(); hasher.update(input.into().trim_end()); let starter = Day17Position::new((0, 0)); - let mut bfs = BreadthFirstSearch::new(starter, |curpos| { - curpos.next_moves_for_hasher(hasher.clone()) - }); + let mut bfs = BreadthFirstSearch::new(starter, |curpos| curpos.next_moves(hasher.clone())); bfs.traverse_until(|position| position.position == (3, 3)); bfs.ending.unwrap().path @@ -113,9 +111,7 @@ pub fn day_17_v2(input: impl Into) -> String { let mut hasher = Md5::new(); hasher.update(input.into().trim_end()); let starter = Day17Position::new((0, 0)); - let mut bfs = BreadthFirstSearch::new(starter, |curpos| { - curpos.next_moves_for_hasher(hasher.clone()) - }); + let mut bfs = BreadthFirstSearch::new(starter, |curpos| curpos.next_moves(hasher.clone())); bfs.longest_path_until(|position| position.position == (3, 3)); bfs.depth.to_string() @@ -135,12 +131,12 @@ mod tests { ("ulqzkmiv", "DRURDRUDDLLDLUURRDULRLDUUDDDRR"), ]; for (sample, result) in sample_one { - println!("TEST => {}", sample); assert_eq!(day_17_v1(sample), result); } } #[test] + #[ignore = "Too slow for CI"] fn works_with_samples_v2() { let sample_two: [(&str, &str); 3] = [ ("ihgpwlah", "370"),