From 6beaf1606c5e9fc936e42c28505739fc11abe73d Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Mon, 12 Dec 2022 10:47:56 +0100 Subject: [PATCH] day(12): refactor Signed-off-by: Matej Focko --- src/bin/day12.rs | 110 ++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 58 deletions(-) diff --git a/src/bin/day12.rs b/src/bin/day12.rs index fee937b..e457b60 100644 --- a/src/bin/day12.rs +++ b/src/bin/day12.rs @@ -1,6 +1,4 @@ -use std::{ - collections::{BTreeSet, VecDeque}, -}; +use std::collections::{BTreeSet, VecDeque}; use aoc_2022::*; @@ -59,6 +57,41 @@ fn get_elevation(c: char) -> i32 { } } +fn bfs(graph: &Vec>, start: &Position, has_edge: F, is_target: G) -> Option +where + F: Fn(&[Vec], &Position, &Position) -> bool, + G: Fn(&[Vec], &Position) -> bool, +{ + let mut visited: BTreeSet = BTreeSet::new(); + let mut queue: VecDeque = VecDeque::new(); + visited.insert(*start); + queue.push_back(Vertex::new(*start, 0)); + + while let Some(v) = queue.pop_front() { + // debug!("Taking {:?} from queue at elevation {}", v, get_elevation(*index(input, &v.position))); + + for (dx, dy) in [(0, 1), (1, 0), (0, -1), (-1, 0)] { + let d = Position::new(dx, dy); + let neighbour = v.position + d; + + if in_range(graph, &neighbour) + && has_edge(graph, &v.position, &neighbour) + && !visited.contains(&neighbour) + { + if is_target(graph, &neighbour) { + // debug!("Found target at distance {}", v.distance + 1); + return Some(v.distance + 1); + } + + visited.insert(neighbour); + queue.push_back(Vertex::new(neighbour, v.distance + 1)); + } + } + } + + None +} + struct Day12; impl Solution for Day12 { fn parse_input>(pathname: P) -> Input { @@ -69,69 +102,30 @@ impl Solution for Day12 { } fn part_1(input: &Input) -> Output { - let start = find_start(input); - let target = find_target(input); + fn has_edge(graph: &[Vec], from: &Position, to: &Position) -> bool { + get_elevation(*index(graph, to)) - get_elevation(*index(graph, from)) <= 1 + } - let mut visited: BTreeSet = BTreeSet::new(); - let mut queue: VecDeque = VecDeque::new(); - visited.insert(start); - queue.push_back(Vertex::new(start, 0)); - - while let Some(v) = queue.pop_front() { - // debug!("Taking {:?} from queue", v); - - for (dx, dy) in [(0, 1), (1, 0), (0, -1), (-1, 0)] { - let d = Position::new(dx, dy); - let neighbour = v.position + d; - - let current = get_elevation(*index(input, &v.position)); - if in_range(input, &neighbour) - && (get_elevation(*index(input, &neighbour)) <= current + 1) - && !visited.contains(&neighbour) - { - if neighbour == target { - // debug!("Found target at distance {}", v.distance + 1); - return v.distance + 1; - } - - visited.insert(neighbour); - queue.push_back(Vertex::new(neighbour, v.distance + 1)); - } - } + let (start, target) = (find_start(input), find_target(input)); + if let Some(distance) = bfs(input, &start, has_edge, |_, &v| v == target) { + return distance; } panic!("haven't found path to target") } fn part_2(input: &Input) -> Output { + fn has_edge(graph: &[Vec], from: &Position, to: &Position) -> bool { + (get_elevation(*index(graph, from)) - get_elevation(*index(graph, to))) <= 1 + } + + fn is_target(graph: &[Vec], vertex: &Position) -> bool { + get_elevation(*index(graph, vertex)) == 0 + } + let start = find_target(input); - - let mut visited: BTreeSet = BTreeSet::new(); - let mut queue: VecDeque = VecDeque::new(); - visited.insert(start); - queue.push_back(Vertex::new(start, 0)); - - while let Some(v) = queue.pop_front() { - // debug!("Taking {:?} from queue at elevation {}", v, get_elevation(*index(input, &v.position))); - - for (dx, dy) in [(0, 1), (1, 0), (0, -1), (-1, 0)] { - let d = Position::new(dx, dy); - let neighbour = v.position + d; - - let current = get_elevation(*index(input, &v.position)); - if in_range(input, &neighbour) - && (current - get_elevation(*index(input, &neighbour))) <= 1 - && !visited.contains(&neighbour) - { - if get_elevation(*index(input, &neighbour)) == 0 { - // debug!("Found target at distance {}", v.distance + 1); - return v.distance + 1; - } - - visited.insert(neighbour); - queue.push_back(Vertex::new(neighbour, v.distance + 1)); - } - } + if let Some(distance) = bfs(input, &start, has_edge, is_target) { + return distance; } panic!("haven't found path to target")