|
@@ -0,0 +1,148 @@
|
|
|
+#![feature(let_chains)]
|
|
|
+use std::{collections::{HashSet, HashMap}, thread::current};
|
|
|
+
|
|
|
+#[derive(Clone, Hash, Eq, PartialEq)]
|
|
|
+pub struct Point {
|
|
|
+ pub x: usize,
|
|
|
+ pub y: usize
|
|
|
+}
|
|
|
+
|
|
|
+pub struct HeightMap {
|
|
|
+ map: Vec<Vec<char>>,
|
|
|
+ best_steps: HashMap<Point, usize>,
|
|
|
+ start: Point,
|
|
|
+ end: Point
|
|
|
+}
|
|
|
+
|
|
|
+impl HeightMap {
|
|
|
+ pub fn get_neighbors(& self, point: &Point) -> Vec<Point> {
|
|
|
+ let mut neighbors = Vec::new();
|
|
|
+ if point.x > 0 {
|
|
|
+ neighbors.push(Point { x: point.x - 1, y: point.y});
|
|
|
+ }
|
|
|
+ if point.x + 1< self.map[0].len() {
|
|
|
+ neighbors.push(Point { x: point.x + 1, y: point.y})
|
|
|
+ }
|
|
|
+ if point.y > 0 {
|
|
|
+ neighbors.push(Point { y: point.y - 1, x: point.x})
|
|
|
+ }
|
|
|
+ if point.y + 1 < self.map.len() {
|
|
|
+ neighbors.push(Point { y: point.y + 1, x: point.x})
|
|
|
+ }
|
|
|
+
|
|
|
+ return neighbors
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn acceptable_next(curr: &char) -> char {
|
|
|
+ if *curr == 'S' {
|
|
|
+ return 'a'
|
|
|
+ }
|
|
|
+ if *curr == 'z' {
|
|
|
+ return 'E'
|
|
|
+ }
|
|
|
+
|
|
|
+ return ((*curr as u8) + 1).try_into().unwrap()
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn best_possible_dist(&self, curr: &Point) -> usize {
|
|
|
+ self.end.x.abs_diff(curr.x) + self.end.y.abs_diff(curr.y)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn search_map(map: &mut HeightMap, mut breadcrumbs: HashSet<Point>, current_pos: Point, best: usize) -> usize {
|
|
|
+ if breadcrumbs.len() + map.best_possible_dist(¤t_pos) >= best {
|
|
|
+ return usize::MAX;
|
|
|
+ }
|
|
|
+ breadcrumbs.insert(current_pos.clone());
|
|
|
+ if current_pos == map.end {
|
|
|
+ println!("We've arrived!");
|
|
|
+ return breadcrumbs.len();
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut min = best;
|
|
|
+
|
|
|
+ for neighbor in map.get_neighbors(¤t_pos) {
|
|
|
+ if breadcrumbs.contains(&neighbor) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let neighbor_val = map.map[neighbor.y][neighbor.x];
|
|
|
+ let curr_val = map.map[current_pos.y][current_pos.x];
|
|
|
+ if !(neighbor_val == HeightMap::acceptable_next(&curr_val) || (neighbor_val <= curr_val) && (neighbor_val != 'E' && neighbor_val != 'S')) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if map.best_steps.contains_key(&neighbor) && map.best_steps[&neighbor] <= breadcrumbs.len() + map.best_possible_dist(¤t_pos) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let new = search_map(map, breadcrumbs.clone(), neighbor, min);
|
|
|
+ if new < min {
|
|
|
+ min = new;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if !map.best_steps.contains_key(¤t_pos) {
|
|
|
+ map.best_steps.insert(current_pos, min);
|
|
|
+ } else {
|
|
|
+ if min < map.best_steps[¤t_pos] {
|
|
|
+ map.best_steps.insert(current_pos, min);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ min
|
|
|
+}
|
|
|
+
|
|
|
+pub fn part_one(input: &str) -> Option<usize> {
|
|
|
+ let _map: Vec<Vec<char>> = input.lines().map(|line| line.chars().collect()).collect();
|
|
|
+ let mut end_coord = Point { x: 0, y: 0 };
|
|
|
+ let mut start_coord = Point { x: 0, y: 0 };
|
|
|
+
|
|
|
+
|
|
|
+ for (r_idx, row) in _map.iter().enumerate() {
|
|
|
+ for (col_idx, c) in row.iter().enumerate() {
|
|
|
+ if *c == 'E' {
|
|
|
+ end_coord = Point {y: r_idx, x: col_idx};
|
|
|
+ } else if *c == 'S' {
|
|
|
+ start_coord = Point {y: r_idx, x: col_idx};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut map = HeightMap {
|
|
|
+ best_steps: HashMap::new(),
|
|
|
+ map: _map,
|
|
|
+ start: start_coord.clone(),
|
|
|
+ end: end_coord.clone()
|
|
|
+ };
|
|
|
+
|
|
|
+ let best = map.map.len() * map.map[0].len();
|
|
|
+ Some(search_map(&mut map, HashSet::from_iter([start_coord.clone()].into_iter()), start_coord, best) - 1)
|
|
|
+}
|
|
|
+
|
|
|
+pub fn part_two(input: &str) -> Option<u32> {
|
|
|
+ None
|
|
|
+}
|
|
|
+
|
|
|
+fn main() {
|
|
|
+ let input = &advent_of_code::read_file("inputs", 12);
|
|
|
+ advent_of_code::solve!(1, part_one, input);
|
|
|
+ advent_of_code::solve!(2, part_two, input);
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_part_one() {
|
|
|
+ let input = advent_of_code::read_file("examples", 12);
|
|
|
+ assert_eq!(part_one(&input), Some(31));
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_part_two() {
|
|
|
+ let input = advent_of_code::read_file("examples", 12);
|
|
|
+ assert_eq!(part_two(&input), None);
|
|
|
+ }
|
|
|
+}
|