Files
aoc_2018/src/tasks/day20.rs
2018-12-21 10:32:11 +01:00

226 lines
7.2 KiB
Rust

use self::Tile::*;
#[allow(unused_imports)]
use crate::utils;
use std::collections::{HashMap, HashSet};
use std::ops::Add;
use std::collections::VecDeque;
pub fn task1() {
let input = utils::read_file("input/day20.txt");
// let input = "^ENNWSWW(NEWS|)SSSEEN(WNSE|)EE(SWEN|)NNN$".to_string();
let input = &input[1..];
let mut map: HashMap<Point, Tile> = HashMap::new();
add_default_neighbors_for_room(&mut map, Point(0, 0));
parse_input(&mut map, &input, Point(0, 0));
// print_map(&map);
println!("Most distant point: {:?}", longest_shortest_path(&map, Point(0,0)));
}
/// Returns the end points of the parsed input
fn parse_input(map: &mut HashMap<Point, Tile>, input: &str, position: Point) -> HashSet<Point> {
// println!("{}, {:?}", input, position);
if input == "$" {
println!("End of the road.");
return vec!(position).into_iter().collect();
}
let mut position = position;
let mut input = input;
let mut iterator = input.chars();
while let Some(c) = iterator.next() {
match c {
'(' => {
let (parts, rest) = split_parts(&input);
let mut middle_points: HashSet<Point> = HashSet::new();
for part in parts {
if part.len() > 0 {
for x in parse_input(map, part, position) {
middle_points.insert(x);
}
}
}
let mut end_points: HashSet<Point> = HashSet::new();
for point in middle_points {
if rest.len() > 0 {
for x in parse_input(map, rest, point) {
end_points.insert(x);
}
}
}
return end_points;
}
'N' => {
position = Point(position.0, position.1 - 2);
add_default_neighbors_for_room(map, position);
map.insert(Point(position.0, position.1 + 1), Door);
input = &input[1..];
}
'E' => {
position = Point(position.0 + 2, position.1);
add_default_neighbors_for_room(map, position);
map.insert(Point(position.0 - 1, position.1), Door);
input = &input[1..];
}
'S' => {
position = Point(position.0, position.1 + 2);
add_default_neighbors_for_room(map, position);
map.insert(Point(position.0, position.1 - 1), Door);
input = &input[1..];
}
'W' => {
position = Point(position.0 - 2, position.1);
add_default_neighbors_for_room(map, position);
map.insert(Point(position.0 + 1, position.1), Door);
input = &input[1..];
}
'$' => break,
c => panic!(
"Stumbled upon a '{}' when reading input where it shouldn't be",
c
),
}
}
vec!(position).into_iter().collect()
}
/// Splits the input by '|', but only by those that are not enclosed by further parenthesises.
/// The second return value is the rest after the closing parenthesis
fn split_parts(input: &str) -> (Vec<&str>, &str) {
// println!("Splitting '{}'", input);
let mut count = 0;
let mut closing_index = None::<usize>;
let mut last_separator = 0;
let mut parts = Vec::new();
for (i, c) in input.chars().enumerate() {
match c {
'(' => count += 1,
')' => {
count -= 1;
if count == 0 {
closing_index = Some(i);
parts.push(&input[last_separator + 1..i]);
break;
}
}
'|' => {
// println!("| at {} (depth {})", i, count);
if count == 1 {
parts.push(&input[last_separator + 1..i]);
last_separator = i;
}
}
_ => {}
}
}
let closing_index =
closing_index.expect(&format!("No matching closing parenthesis in {}", input));
(parts, &input[closing_index + 1..])
}
fn add_default_neighbors_for_room(map: &mut HashMap<Point, Tile>, position: Point) {
map.insert(position, Room);
for p in position.diagonals() {
map.insert(p, Wall);
}
for p in position.neighbors() {
if !map.contains_key(&p) {
map.insert(p, Unknown);
}
}
}
fn print_map(map: &HashMap<Point, Tile>) {
let xmin = map.keys().min_by_key(|it| it.0).unwrap().0;
let xmax = map.keys().max_by_key(|it| it.0).unwrap().0;
let ymin = map.keys().min_by_key(|it| it.1).unwrap().1;
let ymax = map.keys().max_by_key(|it| it.1).unwrap().1;
for y in ymin..=ymax {
for x in xmin..=xmax {
if x == 0 && y == 0 {
print!("X")
} else {
print!(
"{}",
match map.get(&Point(x, y)) {
None => " ",
Some(Wall) => "#",
Some(Room) => ".",
Some(Unknown) => "#",
Some(Door) => {
if y % 2 == 0 {
"|"
} else {
"-"
}
}
}
);
}
}
println!();
}
}
fn longest_shortest_path(map: &HashMap<Point, Tile>, start: Point) -> Option<(Point, usize)> {
let mut distances: HashMap<Point, usize> = HashMap::with_capacity(map.len());
let mut open: VecDeque<(usize,Point)> = VecDeque::new();
let mut visited: HashSet<Point> = HashSet::new();
open.push_back((0,start));
visited.insert(start);
while let Some((distance, predecessor)) = open.pop_front() {
distances.insert(predecessor, distance);
for p in predecessor.neighbors() {
if let Some(Door) = map.get(&p) {
let room = predecessor.next_room(&p);
if !visited.contains(&room) {
visited.insert(room);
open.push_back((distance + 1, room));
}
}
}
}
if let Some((point, distance)) = distances.iter().max_by_key(|it| it.1) {
Some((*point, *distance))
} else {
None
}
}
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
struct Point(i32, i32);
impl Point {
fn diagonals(&self) -> Vec<Point> {
vec![
Point(self.0 - 1, self.1 - 1),
Point(self.0 - 1, self.1 + 1),
Point(self.0 + 1, self.1 - 1),
Point(self.0 + 1, self.1 + 1),
]
}
fn neighbors(&self) -> Vec<Point> {
vec![
Point(self.0 - 1, self.1),
Point(self.0 + 1, self.1),
Point(self.0, self.1 - 1),
Point(self.0, self.1 + 1),
]
}
fn next_room(&self, other: &Self) -> Self {
Point(self.0 + 2*(other.0 - self.0), self.1 +2*(other.1-self.1))
}
}
enum Tile {
Unknown,
Room,
Wall,
Door,
}