aoc2019/day3/src/main.rs

191 lines
5.0 KiB
Rust
Raw Normal View History

2019-12-07 10:20:57 +00:00
type Error = Box<dyn std::error::Error>;
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
pub enum Move {
Right(u32),
Left(u32),
Up(u32),
Down(u32),
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
pub enum Segment {
Horizontal {
row: i32,
col_start: i32,
col_end: i32,
steps_start: i32,
steps_dir: i32,
},
Vertical {
col: i32,
row_start: i32,
row_end: i32,
steps_start: i32,
steps_dir: i32,
},
}
impl Segment {
pub fn collision(&self, other: &Self) -> Option<Position> {
match (*self, *other) {
(Segment::Horizontal {..}, Segment::Horizontal {..}) => None,
(Segment::Vertical {..}, Segment::Vertical {..}) => None,
(
Segment::Horizontal { row, col_start, col_end, steps_start: col_steps_start, steps_dir: col_steps_dir },
Segment::Vertical { col, row_start, row_end, steps_start: row_steps_start, steps_dir: row_steps_dir },
)
|(
Segment::Vertical { col, row_start, row_end, steps_start: row_steps_start, steps_dir: row_steps_dir },
Segment::Horizontal { row, col_start, col_end, steps_start: col_steps_start, steps_dir: col_steps_dir },
)
=> {
if row_start <= row && row <= row_end && col_start <= col && col <= col_end {
let col_steps = col_steps_start + (col - col_start) * col_steps_dir;
let row_steps = row_steps_start + (row - row_start) * row_steps_dir;
Some(Position { row, col, steps: col_steps + row_steps })
} else {
None
}
}
}
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, Default)]
pub struct Position {
row: i32,
col: i32,
steps: i32,
}
impl Position {
pub fn manhatten_distance(&self) -> u32 {
self.row.abs() as u32 + self.col.abs() as u32
}
pub fn trace(&mut self, mov: Move) -> Segment {
let start = *self;
match mov {
Move::Right(off) => {
self.col += off as i32;
self.steps += off as i32;
Segment::Horizontal {
row: start.row,
col_start: start.col,
col_end: self.col,
steps_start: start.steps,
steps_dir: 1,
}
},
Move::Left(off) => {
self.col -= off as i32;
self.steps += off as i32;
Segment::Horizontal {
row: start.row,
col_start: self.col,
col_end: start.col,
steps_start: self.steps,
steps_dir: -1,
}
},
Move::Up(off) => {
self.row -= off as i32;
self.steps += off as i32;
Segment::Vertical {
col: start.col,
row_start: self.row,
row_end: start.row,
steps_start: self.steps,
steps_dir: -1,
}
},
Move::Down(off) => {
self.row += off as i32;
self.steps += off as i32;
Segment::Vertical {
col: start.col,
row_start: start.row,
row_end: self.row,
steps_start: start.steps,
steps_dir: 1,
}
},
}
}
}
impl std::str::FromStr for Move {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let e = move || Box::new(std::io::Error::new(std::io::ErrorKind::Other, "invalid move"));
let n = s[1..].parse::<u32>()?;
if s.is_empty() || !s.is_ascii() {
return Err(e());
}
Ok(match s.as_bytes()[0] {
b'R' => Move::Right(n),
b'L' => Move::Left(n),
b'U' => Move::Up(n),
b'D' => Move::Down(n),
_ => return Err(e()),
})
}
}
fn parse_moves<'a>(input: &'a str) -> impl Iterator<Item=Result<Move, Error>> + 'a {
input.split(',').map(str::parse)
}
fn trace_moves<'a>(input: &'a str) -> impl Iterator<Item=Result<Segment, Error>> + 'a {
let mut pos = Position::default();
parse_moves(input).map(move |m| m.map(|m| pos.trace(m)))
}
fn closest_intersection(lines: &[Vec<Segment>; 2]) -> u32 {
lines[0][1..].iter().map(|a| lines[1][1..].iter().filter_map(move |b| a.collision(b).map(|p| p.manhatten_distance()))).flatten().min().unwrap()
}
fn shortest_delay(lines: &[Vec<Segment>; 2]) -> i32 {
lines[0][1..].iter().map(|a| lines[1][1..].iter().filter_map(move |b| a.collision(b).map(|p| p.steps))).flatten().min().unwrap()
}
fn parse(input: &str) -> [Vec<Segment>; 2] {
let mut lines = input.split_whitespace().map(trace_moves).map(Iterator::collect::<Result<Vec<_>, _>>);
let result = [
lines.next().expect("require two lines").unwrap(),
lines.next().expect("require two lines").unwrap(),
];
assert!(lines.next().is_none(), "require only two lines");
result
}
fn main() {
let input: &str = include_str!("input.txt");
let lines = parse(input);
println!("Distance 1: {}", closest_intersection(&lines));
println!("Distance 2: {}", shortest_delay(&lines));
}
#[cfg(test)]
mod day3test {
use super::*;
#[test]
fn examples1() {
assert_eq!(closest_intersection(&parse("R75,D30,R83,U83,L12,D49,R71,U7,L72
U62,R66,U55,R34,D71,R55,D58,R83")), 159);
assert_eq!(closest_intersection(&parse("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7")), 135);
}
#[test]
fn examples2() {
assert_eq!(shortest_delay(&parse("R75,D30,R83,U83,L12,D49,R71,U7,L72
U62,R66,U55,R34,D71,R55,D58,R83")), 610);
assert_eq!(shortest_delay(&parse("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7")), 410);
}
}