advent-of-code

Perserverance, or the lack thereof

git clone git://git.shimmy1996.com/advent-of-code.git

day-03.rs (4739B)

    1 use std::fs::File;
    2 use std::io::prelude::*;
    3 use std::io::BufReader;
    4 
    5 fn main() {
    6     let input = BufReader::new(File::open("input.txt").unwrap())
    7         .lines()
    8         .map(|line| {
    9             line.unwrap()
   10                 .trim()
   11                 .split(",")
   12                 .map(|x| x.to_string())
   13                 .collect::<Vec<String>>()
   14         })
   15         .collect::<Vec<Vec<String>>>();
   16 
   17     println!("Rust:");
   18     println!("Part 1: {}", part_1(&input));
   19     println!("Part 2: {}", part_2(&input));
   20 }
   21 
   22 type Coord = (i32, i32);
   23 
   24 fn build_path(ops: &Vec<String>) -> Vec<Coord> {
   25     // Translates path to coordinates.
   26     let mut path = ops
   27         .iter()
   28         .scan((0, 0), |curr, op| {
   29             let steps = op[1..].parse::<i32>().unwrap();
   30             let next = match &op[0..=0] {
   31                 "R" => (steps, 0),
   32                 "L" => (-steps, 0),
   33                 "U" => (0, steps),
   34                 "D" => (0, -steps),
   35                 _ => unreachable!(),
   36             };
   37             *curr = (curr.0 + next.0, curr.1 + next.1);
   38             Some(curr.clone())
   39         })
   40         .collect::<Vec<Coord>>();
   41     // Remove first step at the origin.
   42     path.insert(0, (path[0].0.signum(), path[0].1.signum()));
   43 
   44     path
   45 }
   46 
   47 fn build_steps(ops: &Vec<String>) -> Vec<i32> {
   48     // Translates path to coordinates.
   49     let mut steps = ops
   50         .iter()
   51         .scan(0, |steps, op| {
   52             *steps += op[1..].parse::<i32>().unwrap();
   53             Some(steps.clone())
   54         })
   55         .collect::<Vec<i32>>();
   56     // We remove first step.
   57     steps.insert(0, 1);
   58     steps
   59 }
   60 
   61 fn get_min_intersection(
   62     mut p1a: Coord,
   63     mut p1b: Coord,
   64     mut p2a: Coord,
   65     mut p2b: Coord,
   66 ) -> Option<Coord> {
   67     // Returns distance of closest intersection if any.
   68     // Always rotate p1 to be horizontal for easy processing.
   69     let mut rotated = false;
   70     if p1a.0 != p1b.0 {
   71         p1a = (p1a.1, p1a.0);
   72         p1b = (p1b.1, p1b.0);
   73         p2a = (p2a.1, p2a.0);
   74         p2b = (p2b.1, p2b.0);
   75         rotated = true;
   76     }
   77 
   78     let res = if p2a.0 == p2b.0 {
   79         if p1a.0 == p2a.0 {
   80             // Both horizontal, check if intersecting.
   81             let cross_start = std::cmp::max(p1a.1, p2a.1);
   82             let cross_end = std::cmp::min(p1b.1, p2b.1);
   83             if cross_start <= cross_end {
   84                 if cross_start * cross_end <= 0 {
   85                     Some((p1a.0, 0))
   86                 } else {
   87                     if cross_start.abs() <= cross_end.abs() {
   88                         Some((p1a.0, cross_start))
   89                     } else {
   90                         Some((p1a.0, cross_end))
   91                     }
   92                 }
   93             } else {
   94                 None
   95             }
   96         } else {
   97             None
   98         }
   99     } else {
  100         // p2 is vertical, check for intersection.
  101         if (p1a.0 - p2a.0) * (p1a.0 - p2b.0) <= 0 && (p2a.1 - p1a.1) * (p2a.1 - p1b.1) <= 0 {
  102             Some((p1a.0, p2a.1))
  103         } else {
  104             None
  105         }
  106     };
  107 
  108     if rotated {
  109         if let Some((x, y)) = res {
  110             Some((y, x))
  111         } else {
  112             None
  113         }
  114     } else {
  115         res
  116     }
  117 }
  118 
  119 fn part_1(input: &Vec<Vec<String>>) -> i32 {
  120     // Build the line segments.
  121     let path_1 = build_path(&input[0]);
  122     let path_2 = build_path(&input[1]);
  123 
  124     // Check if each line segment intersect.
  125     let mut min_dist = i32::max_value();
  126     for i in 1..path_1.len() {
  127         for j in 1..path_2.len() {
  128             if let Some(min_cross) =
  129                 get_min_intersection(path_1[i - 1], path_1[i], path_2[j - 1], path_2[j])
  130             {
  131                 min_dist = min_dist.min(min_cross.0.abs() + min_cross.1.abs());
  132             };
  133         }
  134     }
  135 
  136     min_dist
  137 }
  138 
  139 fn part_2(input: &Vec<Vec<String>>) -> i32 {
  140     // Build the line segments.
  141     let path_1 = build_path(&input[0]);
  142     let path_2 = build_path(&input[1]);
  143     // Build the step counts.
  144     let steps_1 = build_steps(&input[0]);
  145     let steps_2 = build_steps(&input[1]);
  146 
  147     // Check if each line segment intersect.
  148     let mut min_steps = i32::max_value();
  149     for i in 1..path_1.len() {
  150         for j in 1..path_2.len() {
  151             if let Some(min_cross) =
  152                 get_min_intersection(path_1[i - 1], path_1[i], path_2[j - 1], path_2[j])
  153             {
  154                 let step_1 = steps_1[i - 1]
  155                     + (min_cross.0 - path_1[i - 1].0).abs()
  156                     + (min_cross.1 - path_1[i - 1].1).abs();
  157                 let step_2 = steps_2[j - 1]
  158                     + (min_cross.0 - path_2[j - 1].0).abs()
  159                     + (min_cross.1 - path_2[j - 1].1).abs();
  160                 min_steps = min_steps.min(step_1 + step_2);
  161             };
  162         }
  163     }
  164 
  165     min_steps
  166 }