Advertisement
nairby

2022 Day 12

Dec 12th, 2022
2,525
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 3.63 KB | None | 0 0
  1. use std::env;
  2. use std::io::{self, prelude::*, BufReader};
  3. use std::fs::File;
  4. use std::collections::{HashMap,HashSet};
  5.  
  6. use std::collections::BinaryHeap;
  7. use std::cmp::Ordering;
  8.  
  9. use point2d::point2d::Point2D;
  10.  
  11. fn elevation(ch: char) -> i64 {
  12.     match ch {
  13.         'a'..='z' => { ch as i64 - 96 },
  14.         'S'=> { 1  },
  15.         'E'=> { 26 },
  16.         _ => panic!("Unknown character: {}", ch),
  17.     }
  18. }
  19.  
  20. #[derive(PartialEq,Eq)]
  21. struct Path {
  22.     pt: Point2D,
  23.     steps: i64,
  24. }
  25. impl Ord for Path {
  26.     fn cmp(&self, other: &Self) -> Ordering {
  27.         other.steps.cmp(&self.steps)
  28.     }
  29. }
  30. impl PartialOrd for Path {
  31.     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  32.         Some(other.steps.cmp(&self.steps))
  33.     }
  34. }
  35.  
  36. fn neighbor_addresses(pt: &Point2D) -> Vec<Point2D> {
  37.     vec![Point2D { x: pt.x  , y: pt.y-1 },  // up
  38.          Point2D { x: pt.x+1, y: pt.y   },  // right
  39.          Point2D { x: pt.x  , y: pt.y+1 },  // down
  40.          Point2D { x: pt.x-1, y: pt.y   }]  // left
  41. }
  42.  
  43. fn shortest_path(map: &HashMap<Point2D,i64>, start: Point2D, end: Point2D) -> i64 {
  44.  
  45.     // Explore
  46.     let mut visited: HashSet<Point2D> = HashSet::new();
  47.     let mut heap: BinaryHeap<Path> = BinaryHeap::new();
  48.     heap.push(Path{pt: start, steps: 0});
  49.     while heap.len() > 0 {
  50.  
  51.         let path = heap.pop().unwrap();
  52.         let (now,current_steps) = (path.pt, path.steps);
  53.         let current_elevation = map.get(&now).unwrap();
  54.  
  55.         'neighbor_loop: for neighbor in neighbor_addresses(&now) {
  56.  
  57.            // Determine if visitable
  58.            let new_elev = match map.get(&neighbor) {
  59.                Some(e) => e,
  60.                None => { continue 'neighbor_loop; }, // off the map
  61.             };
  62.             if new_elev > &(current_elevation + 1) { continue 'neighbor_loop; }
  63.  
  64.            // Check already visited
  65.            if visited.get(&neighbor).is_some() { continue 'neighbor_loop; }
  66.             visited.insert(neighbor);
  67.  
  68.             // New steps
  69.             let new_steps = current_steps + 1;
  70.  
  71.             // Found the end
  72.             if neighbor == end { return new_steps; }
  73.             heap.push(Path{pt: neighbor, steps: new_steps});
  74.         }
  75.     }
  76.     i64::MAX
  77. }
  78.  
  79. fn solve(input: &str) -> io::Result<()> {
  80.     let file = File::open(input).expect("Input file not found.");
  81.     let reader = BufReader::new(file);
  82.  
  83.     // Input
  84.     let input: Vec<String> = match reader.lines().collect() {
  85.         Err(err) => panic!("Unknown error reading input: {}", err),
  86.         Ok(result) => result,
  87.     };
  88.  
  89.     // Build map
  90.     let mut elev_map: HashMap<Point2D,i64> = HashMap::new();
  91.     let mut start = Point2D {x: 0, y: 0 };
  92.     let mut end = Point2D {x: 0, y: 0 };
  93.     let mut possible_starts: Vec<Point2D> = Vec::new();
  94.     for (y,line) in input.iter().enumerate() {
  95.         for (x,elev_ch) in line.chars().enumerate() {
  96.             let pt = Point2D { x: x as i64, y: y as i64 };
  97.             elev_map.insert(pt, elevation(elev_ch));
  98.             // Identify start and end points
  99.             match elev_ch {
  100.                 'a' => possible_starts.push(pt),
  101.                 'S' => start = pt,
  102.                 'E' => end   = pt,
  103.                 _ => {},
  104.             }
  105.         }
  106.     }
  107.  
  108.     // Part 1
  109.     let part1 = shortest_path(&elev_map.clone(), start, end);
  110.     println!("Part 1: {part1}"); // 380
  111.  
  112.     // Part 2
  113.     let best = possible_starts.iter().map(|s| shortest_path(&elev_map,*s,end)).min().unwrap();
  114.     println!("Part 2: {best}"); // 375
  115.  
  116.     Ok(())
  117. }
  118.  
  119. fn main() {
  120.     let args: Vec<String> = env::args().collect();
  121.     let filename = &args[1];
  122.     solve(&filename).unwrap();
  123. }
  124.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement