2019-12-07 00:29:42 +01:00
|
|
|
use std::cmp::{max, min};
|
2019-12-06 19:06:49 +01:00
|
|
|
use std::fmt::Write;
|
2019-12-03 09:52:39 +01:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
2020-12-14 19:24:43 +01:00
|
|
|
use anyhow::{bail, Context, Result};
|
2019-12-03 09:52:39 +01:00
|
|
|
|
|
|
|
const INPUT: &str = include_str!("../input/day03.txt");
|
|
|
|
|
2019-12-06 19:06:49 +01:00
|
|
|
pub fn run() -> Result<String> {
|
|
|
|
let mut res = String::with_capacity(128);
|
2019-12-03 10:22:56 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(INPUT)?;
|
2019-12-06 19:47:25 +01:00
|
|
|
|
|
|
|
writeln!(res, "part 1: {}", part1(&first, &second)?)?;
|
|
|
|
writeln!(res, "part 2: {}", part2(&first, &second)?)?;
|
2019-12-06 19:06:49 +01:00
|
|
|
|
|
|
|
Ok(res)
|
2019-12-03 09:52:39 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
fn manhattan_distance(a: &Point, b: &Point) -> u64 {
|
|
|
|
(a.x - b.x).abs() as u64 + (a.y - b.y).abs() as u64
|
2019-12-03 09:52:39 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
fn part1(first_wire: &Wire, second_wire: &Wire) -> Result<u64> {
|
|
|
|
first_wire
|
|
|
|
.0
|
|
|
|
.iter()
|
|
|
|
.flat_map(|first_seg| {
|
|
|
|
second_wire
|
|
|
|
.0
|
|
|
|
.iter()
|
|
|
|
.map(move |second_seg| (first_seg, second_seg))
|
|
|
|
})
|
|
|
|
.filter_map(|(f, s)| match f.intersection(s) {
|
|
|
|
Some(Point { x: 0, y: 0 }) | None => None,
|
|
|
|
Some(p) => Some(p),
|
|
|
|
})
|
|
|
|
.map(|inter| manhattan_distance(&inter, &Point { x: 0, y: 0 }))
|
|
|
|
.min()
|
2020-12-14 19:24:43 +01:00
|
|
|
.context("wire was empty")
|
2019-12-07 00:29:42 +01:00
|
|
|
}
|
2019-12-03 09:52:39 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
fn part2(first_wire: &Wire, second_wire: &Wire) -> Result<u64> {
|
|
|
|
let mut min_dist = None;
|
2019-12-03 09:52:39 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
let mut first_length = 0;
|
2019-12-03 09:52:39 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
for seg1 in first_wire.0.iter() {
|
|
|
|
let mut second_length = 0;
|
2019-12-03 09:52:39 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
for seg2 in second_wire.0.iter() {
|
|
|
|
if let Some(inter) = seg1.intersection(&seg2) {
|
|
|
|
if inter.x == 0 && inter.y == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let path_length = first_length
|
|
|
|
+ second_length
|
|
|
|
+ manhattan_distance(&inter, &seg1.begin)
|
|
|
|
+ manhattan_distance(&inter, &seg2.begin);
|
2019-12-03 10:45:37 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
min_dist = match min_dist {
|
|
|
|
Some(dist) => Some(min(dist, path_length)),
|
|
|
|
None => Some(path_length),
|
|
|
|
};
|
|
|
|
}
|
2019-12-03 10:45:37 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
second_length += manhattan_distance(&seg2.begin, &seg2.end);
|
2019-12-03 10:45:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
first_length += manhattan_distance(&seg1.begin, &seg1.end);
|
2019-12-03 10:45:37 +01:00
|
|
|
}
|
|
|
|
|
2020-12-14 19:24:43 +01:00
|
|
|
min_dist.context("wire was empty")
|
2019-12-03 10:45:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
fn parse_wires(input: &str) -> Result<(Wire, Wire)> {
|
|
|
|
let mut lines = input.lines();
|
2020-12-14 19:24:43 +01:00
|
|
|
let first = lines.next().context("input missing a line")?.parse()?;
|
|
|
|
let second = lines.next().context("input missing a line")?.parse()?;
|
2019-12-07 00:29:42 +01:00
|
|
|
|
|
|
|
Ok((first, second))
|
|
|
|
}
|
2019-12-03 10:45:37 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct Wire(Vec<Segment>);
|
|
|
|
|
|
|
|
impl FromStr for Wire {
|
2020-12-14 19:24:43 +01:00
|
|
|
type Err = anyhow::Error;
|
2019-12-07 00:29:42 +01:00
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Wire> {
|
|
|
|
let moves = s
|
|
|
|
.trim_end()
|
|
|
|
.split(',')
|
2020-12-14 19:24:43 +01:00
|
|
|
.map(|m| m.parse().context("failed to parse wire"))
|
|
|
|
.collect::<Result<Vec<Move>>>()?;
|
2019-12-07 00:29:42 +01:00
|
|
|
|
|
|
|
let mut pos = Point { x: 0, y: 0 };
|
|
|
|
|
|
|
|
let mut wire = Vec::with_capacity(moves.len());
|
|
|
|
for mv in moves {
|
|
|
|
let mut new_pos = pos;
|
|
|
|
match mv.direction {
|
|
|
|
Direction::Up => {
|
|
|
|
new_pos.y += mv.length;
|
|
|
|
}
|
|
|
|
Direction::Down => {
|
|
|
|
new_pos.y -= mv.length;
|
|
|
|
}
|
|
|
|
Direction::Left => {
|
|
|
|
new_pos.x -= mv.length;
|
|
|
|
}
|
|
|
|
Direction::Right => {
|
|
|
|
new_pos.x += mv.length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wire.push(Segment {
|
|
|
|
begin: pos,
|
|
|
|
end: new_pos,
|
|
|
|
min_x: min(pos.x, new_pos.x),
|
|
|
|
max_x: max(pos.x, new_pos.x),
|
|
|
|
min_y: min(pos.y, new_pos.y),
|
|
|
|
max_y: max(pos.y, new_pos.y),
|
|
|
|
});
|
2019-12-03 10:45:37 +01:00
|
|
|
|
|
|
|
pos = new_pos;
|
2019-12-07 00:29:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Wire(wire))
|
|
|
|
}
|
2019-12-03 10:45:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
struct Point {
|
|
|
|
x: i64,
|
|
|
|
y: i64,
|
|
|
|
}
|
2019-12-03 10:45:37 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct Segment {
|
|
|
|
begin: Point,
|
|
|
|
end: Point,
|
|
|
|
min_x: i64,
|
|
|
|
max_x: i64,
|
|
|
|
min_y: i64,
|
|
|
|
max_y: i64,
|
2019-12-06 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
impl Segment {
|
|
|
|
fn intersection(&self, other: &Segment) -> Option<Point> {
|
|
|
|
if self.min_x >= other.min_x
|
|
|
|
&& self.min_x <= other.max_x
|
|
|
|
&& other.min_y >= self.min_y
|
|
|
|
&& other.min_y <= self.max_y
|
|
|
|
{
|
|
|
|
Some(Point {
|
|
|
|
x: self.min_x,
|
|
|
|
y: other.min_y,
|
|
|
|
})
|
|
|
|
} else if other.min_x >= self.min_x
|
|
|
|
&& other.min_x <= self.max_x
|
|
|
|
&& self.min_y >= other.min_y
|
|
|
|
&& self.min_y <= other.max_y
|
|
|
|
{
|
|
|
|
Some(Point {
|
|
|
|
x: other.min_x,
|
|
|
|
y: self.min_y,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2019-12-06 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
enum Direction {
|
|
|
|
Up,
|
|
|
|
Down,
|
|
|
|
Left,
|
|
|
|
Right,
|
|
|
|
}
|
2019-12-06 19:47:25 +01:00
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
struct Move {
|
|
|
|
direction: Direction,
|
|
|
|
length: i64,
|
2019-12-06 19:47:25 +01:00
|
|
|
}
|
|
|
|
|
2019-12-07 00:29:42 +01:00
|
|
|
impl FromStr for Move {
|
2020-12-14 19:24:43 +01:00
|
|
|
type Err = anyhow::Error;
|
2019-12-07 00:29:42 +01:00
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self> {
|
|
|
|
let direction = s
|
|
|
|
.chars()
|
2020-12-14 19:45:51 +01:00
|
|
|
.next()
|
2020-12-14 19:24:43 +01:00
|
|
|
.context("couldn't get direction char in move")?;
|
2019-12-07 00:29:42 +01:00
|
|
|
|
2020-12-14 19:24:43 +01:00
|
|
|
let s = s.get(1..).context("move missing length")?;
|
2019-12-07 00:29:42 +01:00
|
|
|
|
|
|
|
let length = s.parse()?;
|
|
|
|
|
|
|
|
let direction = match direction {
|
|
|
|
'U' => Direction::Up,
|
|
|
|
'D' => Direction::Down,
|
|
|
|
'L' => Direction::Left,
|
|
|
|
'R' => Direction::Right,
|
2020-12-14 19:24:43 +01:00
|
|
|
_ => bail!("couldn't parse direction: {}", direction),
|
2019-12-07 00:29:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Move { direction, length })
|
|
|
|
}
|
2019-12-03 10:45:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-03 09:52:39 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
const PROVIDED1: &str = "R8,U5,L5,D3
|
|
|
|
U7,R6,D4,L4
|
|
|
|
";
|
|
|
|
|
|
|
|
const PROVIDED2: &str = "R75,D30,R83,U83,L12,D49,R71,U7,L72
|
|
|
|
U62,R66,U55,R34,D71,R55,D58,R83
|
|
|
|
";
|
|
|
|
|
|
|
|
const PROVIDED3: &str = "R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
|
|
|
|
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7
|
|
|
|
";
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part1_provided() {
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(PROVIDED1).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part1(&first, &second).unwrap(), 6);
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(PROVIDED2).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part1(&first, &second).unwrap(), 159);
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(PROVIDED3).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part1(&first, &second).unwrap(), 135);
|
2019-12-03 09:52:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part1_real() {
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(INPUT).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part1(&first, &second).unwrap(), 273);
|
2019-12-03 09:52:39 +01:00
|
|
|
}
|
2019-12-03 10:45:37 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2_provided() {
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(PROVIDED1).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part2(&first, &second).unwrap(), 30);
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(PROVIDED2).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part2(&first, &second).unwrap(), 610);
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(PROVIDED3).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part2(&first, &second).unwrap(), 410);
|
2019-12-03 10:45:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2_real() {
|
2019-12-07 00:29:42 +01:00
|
|
|
let (first, second) = parse_wires(INPUT).unwrap();
|
2019-12-06 19:47:25 +01:00
|
|
|
assert_eq!(part2(&first, &second).unwrap(), 15622);
|
2019-12-03 10:45:37 +01:00
|
|
|
}
|
2019-12-03 09:52:39 +01:00
|
|
|
}
|