advent-of-code-2019/day3/src/main.rs
2019-12-05 19:56:00 -05:00

201 lines
5.7 KiB
Rust

use std::collections::HashMap;
use std::error::Error;
use std::fs;
use std::result;
use std::str::FromStr;
const INPUT: &str = "input/input.txt";
type Result<T> = result::Result<T, Box<dyn Error>>;
#[derive(Debug, PartialEq)]
struct CrossedWires {
wires: Vec<Vec<Move>>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct Point {
x: i32,
y: i32,
}
#[derive(Debug, PartialEq)]
struct Move {
direction: Direction,
distance: i32,
}
#[derive(Debug, PartialEq)]
enum Direction {
Up,
Down,
Right,
Left,
}
impl CrossedWires {
fn find_intersections(&self) -> HashMap<Point, u32> {
let mut intersections: HashMap<Point, u32> = HashMap::new();
let mut occupied_points: HashMap<Point, u32> = HashMap::new();
for (wire_index, wire) in self.wires.iter().enumerate() {
let mut steps = 0;
let mut end_point = Point { x: 0, y: 0 };
for movement in wire.iter() {
let mut point = end_point.clone();
for _ in 0..movement.distance {
match movement.direction {
Direction::Up => point.y += 1,
Direction::Down => point.y -= 1,
Direction::Right => point.x += 1,
Direction::Left => point.x -= 1,
};
steps += 1;
if wire_index == 0 {
occupied_points.insert(point, steps);
} else {
if let Some(first_wire_steps) = occupied_points.get(&point) {
intersections.insert(point, first_wire_steps + steps);
}
}
}
end_point = point;
}
}
intersections
}
}
impl FromStr for CrossedWires {
type Err = Box<dyn Error>;
fn from_str(s: &str) -> Result<CrossedWires> {
let mut wires = s.split("\n");
let first_moves = wires.next().expect("First wire not found in input");
let second_moves = wires.next().expect("Second wire not found in input");
Ok(CrossedWires {
wires: vec![
get_moves_from_string(first_moves)?,
get_moves_from_string(second_moves)?,
],
})
}
}
impl From<char> for Direction {
fn from(c: char) -> Direction {
match c {
'U' => Direction::Up,
'D' => Direction::Down,
'R' => Direction::Right,
'L' => Direction::Left,
_ => panic!("Could not parse direction: {}", c),
}
}
}
fn get_moves_from_string(moves_string: &str) -> Result<Vec<Move>> {
let moves_strings = moves_string.split(",");
let mut moves = vec![];
for wire_move in moves_strings {
let mut wire_move = wire_move.chars();
let direction: Direction =
Direction::from(wire_move.next().expect("Invalid empty wire move"));
let distance: i32 = wire_move.collect::<String>().parse()?;
moves.push(Move {
direction,
distance,
});
}
Ok(moves)
}
fn read_wires(filename: &str) -> Result<CrossedWires> {
let wires = fs::read_to_string(filename)?;
Ok(wires.parse()?)
}
fn solve_part1() -> Result<i32> {
let wires = read_wires(INPUT)?;
let intersections = wires.find_intersections();
let intersect_points = intersections.keys();
let distances = intersect_points.map(|point| point.x.abs() + point.y.abs());
Ok(distances.min().expect("No intersections found"))
}
fn solve_part2() -> Result<i32> {
let wires = read_wires(INPUT)?;
let intersections = wires.find_intersections();
let min_intersection = intersections
.iter()
.min_by_key(|(_, steps)| steps.clone()).expect("No intersections found");
Ok(*min_intersection.1 as i32)
}
fn main() -> Result<()> {
println!("Part 1: {}", solve_part1()?);
println!("Part 2: {}", solve_part2()?);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_INPUT1: &str = "input/test1.txt";
// const TEST_INPUT2: &str = "input/test2.txt";
// const TEST_INPUT3: &str = "input/test3.txt";
#[test]
fn reads_wires() {
assert_eq!(
read_wires(TEST_INPUT1).unwrap(),
CrossedWires {
wires: vec![
vec![
Move {
direction: Direction::Right,
distance: 8
},
Move {
direction: Direction::Up,
distance: 5
},
Move {
direction: Direction::Left,
distance: 5
},
Move {
direction: Direction::Down,
distance: 3
},
],
vec![
Move {
direction: Direction::Up,
distance: 7
},
Move {
direction: Direction::Right,
distance: 6
},
Move {
direction: Direction::Down,
distance: 4
},
Move {
direction: Direction::Left,
distance: 4
},
],
],
}
);
}
}