226 lines
7.2 KiB
Rust
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,
|
|
}
|