use std::collections::{HashMap, HashSet}; use std::fs; use std::iter; #[allow(dead_code)] pub fn run() { let content = fs::read_to_string("input/day03.txt").unwrap(); let p1: Vec<&str> = content.lines().next().unwrap().split(",").collect(); let p2: Vec<&str> = content.lines().skip(1).next().unwrap().split(",").collect(); task1(&p1, &p2); task2(&p1, &p2); } fn task1(p1: &Vec<&str>, p2: &Vec<&str>) { let points1 = visited_points(&p1); let points2 = visited_points(&p2); let closest_distance = points1 .intersection(&points2) .min_by_key(|(a, b)| a.abs() + b.abs()) .unwrap(); println!( "Task 1: Closest point is at distance {}", closest_distance.0.abs() + closest_distance.1.abs() ); } /** * returns all visited points, the origin is left out, unless it is visited once again */ fn visited_points(p: &Vec<&str>) -> HashSet<(i32, i32)> { p.iter() .map(|step| { let direction = step.chars().next().unwrap(); let number = step[1..].parse::().unwrap(); (direction, number) }) .scan((0, 0), |(cx, cy), (direction, number)| { fn fi(i: i32) -> i32 { i + 1 } fn fd(i: i32) -> i32 { i - 1 } let fstep = match direction { 'U' | 'R' => fi, 'D' | 'L' => fd, _ => panic!("Unexpected input"), }; fn fx(point: (i32, i32), f: fn(i32) -> i32) -> (i32, i32) { let (x, y) = point; (f(x), y) } fn fy(point: (i32, i32), f: fn(i32) -> i32) -> (i32, i32) { let (x, y) = point; (x, f(y)) } let faxis = match direction { 'U' | 'D' => fy, 'R' | 'L' => fx, _ => panic!("Unexpected input"), }; let steps = (0..number) .scan((*cx, *cy), |(sx, sy), _| { let (newx, newy) = faxis((*sx, *sy), fstep); *sx = newx; *sy = newy; Some((newx, newy)) }) .collect::>(); let last = steps.iter().last().unwrap(); *cx = last.0; *cy = last.1; Some(steps) }) .flatten() .collect() } fn task2(p1: &Vec<&str>, p2: &Vec<&str>) { let points1 = get_points(&p1); let points2 = get_points(&p2); let set1 = points1 .keys() .map(|(x, y)| (*x, *y)) .collect::>(); let set2 = points2 .keys() .map(|(x, y)| (*x, *y)) .collect::>(); let p = set1 .intersection(&set2) .min_by_key(|p| points1[p] + points2[p]) .unwrap(); println!( "Task 2: shortest is at {:?} with distances {}/{} = {}", p, points1[p], points2[p], points1[p] + points2[p] ); // 20384 is too short } fn get_points(path: &Vec<&str>) -> HashMap<(i32, i32), usize> { let directions = vec![('U', (0, 1)), ('D', (0, -1)), ('L', (-1, 0)), ('R', (1, 0))] .into_iter() .collect::>(); path.iter() .map(|it| { let direction = it.chars().next().unwrap(); let number = it[1..].parse::().unwrap(); iter::repeat(direction).take(number) }) .flatten() .scan((0, 0), |(sx, sy), dir| { let (dx, dy) = directions[&dir]; *sx += dx; *sy += dy; Some((*sx, *sy)) }) .enumerate() .fold(HashMap::new(), |mut hm, (i, point)| { if !hm.contains_key(&point) { hm.insert(point, i + 1); } hm }) }