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 = 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, input: &str, position: Point) -> HashSet { // 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 = 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 = 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::; 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, 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) { 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, start: Point) -> Option<(Point, usize)> { let mut distances: HashMap = HashMap::with_capacity(map.len()); let mut open: VecDeque<(usize,Point)> = VecDeque::new(); let mut visited: HashSet = 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 { 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 { 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, }