From f41ddb1381f2b4fabc11fd809a1ece193157ab48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20S=C5=82adek?= Date: Tue, 24 Dec 2024 00:17:05 +0100 Subject: [PATCH] refactor: make is_end as function argument instead of struct property --- src/solutions/year2023/day17.rs | 4 ++-- src/solutions/year2024/day16.rs | 15 +++++++------- src/solutions/year2024/day21.rs | 36 +++++++++++++++------------------ src/utils/graphs/dijkstra.rs | 25 ++++++++--------------- 4 files changed, 34 insertions(+), 46 deletions(-) diff --git a/src/solutions/year2023/day17.rs b/src/solutions/year2023/day17.rs index 0024d63..683ffd2 100644 --- a/src/solutions/year2023/day17.rs +++ b/src/solutions/year2023/day17.rs @@ -68,14 +68,14 @@ impl Day17 { let cost = move |_, next: Node| { *grid_clone.get_for_point(&next.vector.position()).unwrap() as usize }; - let dijkstra: Dijkstra = Dijkstra::new(adjacency, Box::new(cost), Box::new(is_end)); + let dijkstra: Dijkstra = Dijkstra::new(adjacency, Box::new(cost)); let starts = vec![ Node::new(start_point, Direction::East), Node::new(start_point, Direction::South), ]; - dijkstra.cost(starts).unwrap().to_string() + dijkstra.cost(starts, &is_end).unwrap().to_string() } fn filter_out_outside_grid(vec: Vec, grid: &Grid) -> Vec { diff --git a/src/solutions/year2024/day16.rs b/src/solutions/year2024/day16.rs index 6ad8d55..886f94b 100644 --- a/src/solutions/year2024/day16.rs +++ b/src/solutions/year2024/day16.rs @@ -11,17 +11,19 @@ pub struct Day16; impl Solution for Day16 { fn part_one(&self, input: &str) -> String { let (grid, start, end) = Self::setup_grid(input); - let dijkstra = Self::create_dijkstra(grid, end); + let dijkstra = Self::create_dijkstra(grid); + let is_end = Self::is_end_closure(end); - dijkstra.cost(vec![start]).unwrap().to_string() + dijkstra.cost(vec![start], &is_end).unwrap().to_string() } fn part_two(&self, input: &str) -> String { let (grid, start, end) = Self::setup_grid(input); - let dijkstra = Self::create_dijkstra(grid, end); + let dijkstra = Self::create_dijkstra(grid); + let is_end = Self::is_end_closure(end); dijkstra - .all_paths(vec![start]) + .all_paths(vec![start], &is_end) .iter() .flat_map(|path| path.iter().map(|p| p.position())) .unique() @@ -40,12 +42,11 @@ impl Day16 { (grid, start, end) } - fn create_dijkstra(grid: Grid, end: Point) -> Dijkstra { + fn create_dijkstra(grid: Grid) -> Dijkstra { let adjacency = Self::adjacency_closure(grid); let cost = Self::cost_closure(); - let is_end = Self::is_end_closure(end); - Dijkstra::new(adjacency, cost, is_end) + Dijkstra::new(adjacency, cost) } fn adjacency_closure(grid: Grid) -> Box Vec> { diff --git a/src/solutions/year2024/day21.rs b/src/solutions/year2024/day21.rs index 9a78bbd..027e84a 100644 --- a/src/solutions/year2024/day21.rs +++ b/src/solutions/year2024/day21.rs @@ -1,7 +1,7 @@ use crate::solutions::year2024::day21::Key::{Activate, Dir}; use crate::solutions::Solution; use crate::utils::direction::Direction; -use crate::utils::graphs::a_star::AStarBuilder; +use crate::utils::graphs::dijkstra::Dijkstra; use crate::utils::grid::Grid; use crate::utils::point::Point; use itertools::Itertools; @@ -53,8 +53,6 @@ impl Day21 { let path = self.path_for_str(¤t, pad); current = path.iter().map(|key| key.to_string()).collect::(); - - // println!("{}", current); } current.chars().count() @@ -62,29 +60,31 @@ impl Day21 { fn path_for_str(&self, code: &str, pad: &Pad) -> Vec { let code = "A".to_owned() + code; + let adjacent = pad.adjacent.clone(); - let neighbours = |p: Point| pad.adjacent(&p); - let distance = |_, _| 1; + let neighbours = Box::new(move |p: Point| adjacent.get(&p).unwrap().to_vec()); + let distance = Box::new(|_, _| 1); - let a_star = AStarBuilder::init(&neighbours, &distance).build(); + let dijkstra = Dijkstra::new(neighbours, distance); code.chars() .tuple_windows() .flat_map(|(from, to)| { - let path = a_star - .path( - pad.position(from as u8).unwrap(), - pad.position(to as u8).unwrap(), - ) - .unwrap(); - let mut directions: Vec = path + let start = pad.position(from as u8).unwrap(); + let end = pad.position(to as u8).unwrap(); + + let is_end = |p: Point| p == end; + let path = dijkstra.all_paths(vec![start], &is_end); + + let min_path = path.iter().min_by_key(|p| p.len()).unwrap(); + let mut directions: Vec = min_path + .iter() + .collect_vec() .windows(2) - .map(|pair| Dir(pair[0].direction(&pair[1]).unwrap())) + .map(|pair| Dir(pair[0].direction(pair[1]).unwrap())) .collect(); directions.push(Activate); - // println!("{from} {to} -> {:?}", directions.iter().map(|d| d.to_string()).collect::()); - directions.into_iter() }) .collect() @@ -171,10 +171,6 @@ impl Pad { fn position(&self, element: u8) -> Option { self.positions.get(&element).copied() } - - fn adjacent(&self, position: &Point) -> Vec { - self.adjacent.get(position).unwrap().to_vec() - } } #[cfg(test)] diff --git a/src/utils/graphs/dijkstra.rs b/src/utils/graphs/dijkstra.rs index d0d050b..135841b 100644 --- a/src/utils/graphs/dijkstra.rs +++ b/src/utils/graphs/dijkstra.rs @@ -7,23 +7,14 @@ use std::hash::Hash; pub struct Dijkstra { neighbours: Box Vec>, cost: Box usize>, - is_end: Box bool>, } impl Dijkstra { - pub fn new( - neighbours: Box Vec>, - cost: Box usize>, - is_end: Box bool>, - ) -> Self { - Self { - neighbours, - cost, - is_end, - } + pub fn new(neighbours: Box Vec>, cost: Box usize>) -> Self { + Self { neighbours, cost } } - pub fn cost(&self, starts: Vec) -> Option + pub fn cost(&self, starts: Vec, is_end: &dyn Fn(T) -> bool) -> Option where T: Hash + Eq + PartialEq + Ord + Copy + Debug, { @@ -36,7 +27,7 @@ impl Dijkstra { } while let Some(State { cost, node }) = heap.pop() { - if (self.is_end)(node) { + if is_end(node) { return Some(cost); } @@ -56,7 +47,7 @@ impl Dijkstra { /// It returns every possible visited node /// Even if there is a many possible ways to reach end - pub fn all_paths(&self, starts: Vec) -> Vec> + pub fn all_paths(&self, starts: Vec, is_end: &dyn Fn(T) -> bool) -> Vec> where T: Hash + Eq + PartialEq + Ord + Debug + Copy, { @@ -74,7 +65,7 @@ impl Dijkstra { let mut end_nodes: Vec = Vec::new(); while let Some(State { cost, node }) = heap.pop() { - if (self.is_end)(node) { + if is_end(node) { lowest = Some(cost); end_nodes.push(node); @@ -171,10 +162,10 @@ mod test { _ => unreachable!("Invalid node"), }), Box::new(|_: char, _: char| 1), - Box::new(|node: char| node == '<'), ); + let is_end = |node: char| node == '<'; - let paths = dijkstra.all_paths(vec!['A']); + let paths = dijkstra.all_paths(vec!['A'], &is_end); assert_eq!(paths.len(), 2); assert!(paths.contains(&VecDeque::from(vec!['A', '^', 'v', '<'])));