nairby

Day 15 Code

Dec 15th, 2021 (edited)
333
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 4.38 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. const MAP_MULTIPLIER: i64 = 5;
  12.  
  13. #[derive(PartialEq,Eq)]
  14. struct Path {
  15.     pt: Point2D,
  16.     cost: i64,
  17. }
  18. impl Ord for Path {
  19.     fn cmp(&self, other: &Self) -> Ordering {
  20.         other.cost.cmp(&self.cost)
  21.     }
  22. }
  23. impl PartialOrd for Path {
  24.     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  25.         Some(other.cost.cmp(&self.cost))
  26.     }
  27. }
  28.  
  29. fn neighbor_addresses(pt: &Point2D) -> Vec<Point2D> {
  30.     vec![Point2D { x: pt.x  , y: pt.y-1 },  // up
  31.          Point2D { x: pt.x+1, y: pt.y   },  // right
  32.          Point2D { x: pt.x  , y: pt.y+1 },  // down
  33.          Point2D { x: pt.x-1, y: pt.y   }] // left
  34. }
  35.  
  36. fn map_extents(map: &HashMap<Point2D,i64>) -> (i64,i64,i64,i64) {
  37.     let xmin = &map.keys().map(|&pt| pt.x).min().unwrap();
  38.     let ymin = &map.keys().map(|&pt| pt.y).min().unwrap();
  39.     let xmax = &map.keys().map(|&pt| pt.x).max().unwrap();
  40.     let ymax = &map.keys().map(|&pt| pt.y).max().unwrap();
  41.     (*xmin,*ymin,*xmax,*ymax)
  42. }
  43.  
  44. fn least_risky(map: &HashMap<Point2D,i64>) -> i64 {
  45.     // Prepare
  46.     let (xmin,ymin,xmax,ymax) = map_extents(&map);
  47.     let mut visited: HashSet<Point2D> = HashSet::new();
  48.  
  49.     // Explore
  50.     let mut heap: BinaryHeap<Path> = BinaryHeap::new();
  51.     let start = Point2D { x: xmin, y: ymin };
  52.     let end = Point2D { x: xmax, y: ymax };
  53.     heap.push(Path{pt: start, cost: 0});
  54.     while heap.len() > 0 {
  55.  
  56.         let path = heap.pop().unwrap();
  57.         let (now,current_risk) = (path.pt, path.cost);
  58.  
  59.         'n_loop: for neighbor in neighbor_addresses(&now) {
  60.  
  61.            // Check already visited
  62.            match visited.get(&neighbor) {
  63.                Some(_) => { continue 'n_loop; },
  64.                 None => {},
  65.             }
  66.             visited.insert(neighbor);
  67.  
  68.             // Determine risk level
  69.             let new_risk = match map.get(&neighbor) {
  70.                 Some(this_risk) => current_risk+this_risk,
  71.                 None => { continue 'n_loop; }, // off the map
  72.            };
  73.  
  74.            // Found the end
  75.            if neighbor == end {
  76.                return new_risk;
  77.            }
  78.            heap.push(Path{pt: neighbor, cost: new_risk});
  79.        }
  80.    }
  81.    i64::MAX
  82. }
  83.  
  84. fn solve(input: &str) -> io::Result<()> {
  85.    let file = File::open(input).expect("Input file not found.");
  86.    let reader = BufReader::new(file);
  87.  
  88.    // Input
  89.    let input: Vec<String> = match reader.lines().collect() {
  90.        Err(err) => panic!("Unknown error reading input: {}", err),
  91.        Ok(result) => result,
  92.    };
  93.  
  94.    // Build map (part 1)
  95.    let mut risk_map: HashMap<Point2D,i64> = HashMap::new();
  96.    for (y,line) in input.iter().enumerate() {
  97.        for (x,risk) in line.chars().enumerate() {
  98.            let pt = Point2D { x: x as i64, y: y as i64 };
  99.            risk_map.insert(pt, risk.to_digit(10).unwrap().try_into().unwrap());
  100.        }
  101.    }
  102.    
  103.    // Build map (part 2)
  104.    let mut risk_map2 = risk_map.clone();
  105.    let (xmin,ymin,xmax,ymax) = map_extents(&risk_map2);
  106.    for y in ymin..(ymax+1)*MAP_MULTIPLIER {
  107.        for x in xmin..(xmax+1)*MAP_MULTIPLIER {
  108.            let pt = &Point2D{x: x, y: y};
  109.            match risk_map2.get(pt) {
  110.                Some(_) => {}, // Already populated
  111.                None => {
  112.                    // New point
  113.                    let ref_pt = if y <= ymax {
  114.                        Point2D { x: x-xmax-1, y: y }  // Reference left
  115.                    } else {
  116.                        Point2D { x: x , y: y-ymax-1 } // Reference up
  117.                    };
  118.                    let new_value = match risk_map2.get(&ref_pt) {
  119.                        Some(z @ 1..=8) => z + 1,
  120.                        Some(9) => 1,
  121.                        Some(_) | None => unreachable!(),
  122.                    };
  123.                    risk_map2.insert(*pt,new_value);
  124.                },
  125.            }
  126.        }
  127.    }
  128.  
  129.    let part1 = least_risky(&risk_map.clone());
  130.    println!("Part 1: {}", part1); // 537
  131.  
  132.    let part2 = least_risky(&risk_map2.clone());
  133.    println!("Part 2: {}", part2); // 2881
  134.    Ok(())
  135. }
  136.  
  137. fn main() {
  138.    let args: Vec<String> = env::args().collect();
  139.    let filename = &args[1];
  140.    solve(&filename).unwrap();
  141. }
Add Comment
Please, Sign In to add comment