|
|
@ -2,7 +2,7 @@ use std::io::BufRead; |
|
|
|
use std::str::FromStr; |
|
|
|
use std::num::ParseIntError; |
|
|
|
use std::fmt::{self, Display}; |
|
|
|
use std::collections::HashSet; |
|
|
|
use std::collections::{HashSet, HashMap}; |
|
|
|
|
|
|
|
use Segment::*; |
|
|
|
|
|
|
@ -75,37 +75,60 @@ pub fn part1<F: BufRead> (mut input: F) { |
|
|
|
println!("{}", res); |
|
|
|
} |
|
|
|
|
|
|
|
pub fn part2<F: BufRead> (mut input: F) { |
|
|
|
let path1 = parse_input(&mut input); |
|
|
|
let path2 = parse_input(&mut input); |
|
|
|
let res = compute_first_intersection(path1, path2); |
|
|
|
println!("{}", res); |
|
|
|
} |
|
|
|
|
|
|
|
fn parse_input<F: BufRead> (input: &mut F) -> Vec<Segment> { |
|
|
|
let mut buf = String::new(); |
|
|
|
let _num_bytes = input.read_line(&mut buf).expect("Unable to read a line of input file"); |
|
|
|
buf.trim().split(",").filter_map(|segment| segment.parse().ok()).collect() |
|
|
|
} |
|
|
|
|
|
|
|
fn compute_first_intersection(path1: Vec<Segment>, path2: Vec<Segment>) -> i32 { |
|
|
|
let steps1 = build_full_path(path1, 0, (0, 0), HashMap::new(), 1); |
|
|
|
let steps2 = build_full_path(path2, 0, (0, 0), HashMap::new(), 1); |
|
|
|
|
|
|
|
let seen1: HashSet<_> = steps1.keys().collect(); |
|
|
|
let seen2: HashSet<_> = steps2.keys().collect(); |
|
|
|
|
|
|
|
seen1.intersection(&seen2).map(|coord| steps1[coord] + steps2[coord]).min().unwrap() |
|
|
|
} |
|
|
|
|
|
|
|
fn compute_closest_intersection(path1: Vec<Segment>, path2: Vec<Segment>) -> i32 { |
|
|
|
let seen1 = walk_on_path(path1, 0, (0, 0), HashSet::new()); |
|
|
|
let seen2 = walk_on_path(path2, 0, (0, 0), HashSet::new()); |
|
|
|
let steps1 = build_full_path(path1, 0, (0, 0), HashMap::new(), 1); |
|
|
|
let steps2 = build_full_path(path2, 0, (0, 0), HashMap::new(), 1); |
|
|
|
|
|
|
|
seen1.intersection(&seen2).map(|(x, y)| x.abs() + y.abs()).min().unwrap() |
|
|
|
let seen1: HashSet<_> = steps1.keys().collect(); |
|
|
|
let seen2: HashSet<_> = steps2.keys().collect(); |
|
|
|
|
|
|
|
seen1.intersection(&seen2).map(|(x, y)| x.abs() + y.abs()).min().unwrap() |
|
|
|
} |
|
|
|
|
|
|
|
fn walk_on_path( |
|
|
|
fn build_full_path( |
|
|
|
path: Vec<Segment>, |
|
|
|
index: usize, |
|
|
|
(x0, y0): (i32, i32), |
|
|
|
mut seen: HashSet<(i32, i32)> |
|
|
|
) -> HashSet<(i32, i32)> { |
|
|
|
mut steps: HashMap<(i32, i32), i32>, |
|
|
|
current_step: i32 |
|
|
|
) -> HashMap<(i32, i32), i32> { |
|
|
|
if index == path.len() { |
|
|
|
seen |
|
|
|
steps |
|
|
|
} else { |
|
|
|
let segment = &path[index]; |
|
|
|
let (dx, dy) = segment.direction(); |
|
|
|
let length = segment.length(); |
|
|
|
let mut current_step = current_step; |
|
|
|
|
|
|
|
for i in 1..(length + 1) { |
|
|
|
seen.insert((x0 + i * dx, y0 + i * dy)); |
|
|
|
steps.entry((x0 + i * dx, y0 + i * dy)).or_insert(current_step); |
|
|
|
current_step += 1; |
|
|
|
} |
|
|
|
|
|
|
|
walk_on_path(path, index + 1, (x0 + length * dx, y0 + length * dy), seen) |
|
|
|
build_full_path(path, index + 1, (x0 + length * dx, y0 + length * dy), steps, current_step) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -140,4 +163,22 @@ U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 |
|
|
|
assert_eq!(res, *expected) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn test_compute_first_intersection() { |
|
|
|
let mut input = r#"R8,U5,L5,D3 |
|
|
|
U7,R6,D4,L4 |
|
|
|
R75,D30,R83,U83,L12,D49,R71,U7,L72 |
|
|
|
U62,R66,U55,R34,D71,R55,D58,R83 |
|
|
|
R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 |
|
|
|
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 |
|
|
|
"#.as_bytes(); |
|
|
|
let test_results = [30, 610, 410]; |
|
|
|
for expected in &test_results { |
|
|
|
let path1 = parse_input(&mut input); |
|
|
|
let path2 = parse_input(&mut input); |
|
|
|
let res = compute_first_intersection(path1, path2); |
|
|
|
assert_eq!(res, *expected) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|