diff --git a/input/day20_example1.txt b/input/day20_example1.txt new file mode 100644 index 0000000..bf1d8d2 --- /dev/null +++ b/input/day20_example1.txt @@ -0,0 +1,19 @@ + A + A + #######.######### + #######.........# + #######.#######.# + #######.#######.# + #######.#######.# + ##### B ###.# +BC...## C ###.# + ##.## ###.# + ##...DE F ###.# + ##### G ###.# + #########.#####.# +DE..#######...###.# + #.#########.###.# +FG..#########.....# + ###########.##### + Z + Z \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 304d6c4..1f95c44 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ mod tasks; fn main() { - tasks::day19::run(); + tasks::day20::run(); } diff --git a/src/tasks/day20.rs b/src/tasks/day20.rs new file mode 100644 index 0000000..ca3d163 --- /dev/null +++ b/src/tasks/day20.rs @@ -0,0 +1,199 @@ +use std::collections::{HashMap, HashSet}; +use std::fmt::format; + +pub fn run() {} + +type C = i32; + +#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)] +struct Point { + x: C, + y: C, +} + +impl Point { + fn of(x: C, y: C) -> Self { + Point { x, y } + } + + fn neighbors(self) -> Vec { + vec![ + Point::of(self.x + 1, self.y), + Point::of(self.x, self.y + 1), + Point::of(self.x - 1, self.y), + Point::of(self.x, self.y - 1), + ] + } +} + +struct Field { + point: Point, + neighbors: Vec, +} + +impl Field { + fn of(point: Point) -> Self { + Field { + point, + neighbors: vec![], + } + } +} + +pub struct Maze { + map: HashMap, + width: C, + height: C, + start: Point, + finish: Point, +} + +impl Maze { + pub fn from(input: &String) -> Self { + let (moc, width, height) = Self::map_of_chars(input); + println!("moc size {}, w {width}, h {height}", moc.len()); + + let mut map = Self::create_map_of_free_spots(&moc); + Self::add_physical_neighbors(&mut map); + let labels = Self::labels(&moc, width, height); + Self::add_portals(&mut map, &labels); + let start = labels["AA"][0]; + let finish = labels["ZZ"][0]; + + Maze { map, width, height, start, finish } + } + + pub fn shortest_path(&self) -> usize { + todo!() + } + + fn add_portals(map: &mut HashMap, labels: &HashMap>) { + println!("{:?}", labels); + for (label, points) in labels { + if label == "AA" || label == "ZZ" { + continue; + } + + map.get_mut(&points[0]).unwrap().neighbors.push(points[1]); + map.get_mut(&points[1]).unwrap().neighbors.push(points[0]); + } + } + + fn labels(moc: &HashMap, width: C, height: C) -> HashMap> { + let horizontal: Vec<(String, Point)> = (0..width - 2) + .into_iter() + .flat_map(|x| { + (0..height).into_iter().flat_map(move |y| { + println!("{x} {y} h?"); + let mut triple = [ + moc[&Point::of(x, y)], + moc[&Point::of(x + 1, y)], + moc[&Point::of(x + 2, y)], + ]; + let left = Self::label(triple).map(|label| (label, Point::of(x, y))); + triple.reverse(); + let right = Self::label(triple).map(|label| (label, Point::of(x + 2, y))); + vec![left, right] + }) + }) + .flatten() + .collect(); + + let vertical: Vec<(String, Point)> = (0..width) + .into_iter() + .flat_map(|x| { + (0..height - 2).into_iter().flat_map(move |y| { + println!("{x} {y} v?"); + let mut triple = [ + moc[&Point::of(x, y)], + moc[&Point::of(x, y + 1)], + moc[&Point::of(x, y + 2)], + ]; + let left = Self::label(triple).map(|label| (label, Point::of(x, y))); + triple.reverse(); + let right = Self::label(triple).map(|label| (label, Point::of(x, y + 2))); + vec![left, right] + }) + }) + .flatten() + .collect(); + + let mut result = HashMap::new(); + for (s, p) in horizontal.into_iter().chain(vertical.into_iter()) { + if !result.contains_key(&s) { + result.insert(s.clone(), vec![]); + } + result.get_mut(&s).unwrap().push(p); + } + result + } + + fn label(chars: [char; 3]) -> Option { + println!("{}{}{}", chars[0], chars[1], chars[2]); + if chars[0] == '.' && chars[1].is_alphabetic() && chars[2].is_alphabetic() { + let label = if chars[1] < chars[2] { + format!("{}{}", chars[1], chars[2]) + } else { + format!("{}{}", chars[2], chars[1]) + }; + Some(label) + } else { + None + } + } + + fn add_physical_neighbors(map: &mut HashMap) { + let points: HashSet = map.keys().map(|p| *p).collect(); + map.iter_mut().for_each(|(p, f)| { + let neighbors = p + .neighbors() + .into_iter() + .filter(|neighbor| points.contains(neighbor)) + .collect(); + f.neighbors = neighbors; + }) + } + + fn create_map_of_free_spots(moc: &HashMap) -> HashMap { + moc.keys() + .filter(|p| moc[p] == '.') + .map(|p| (*p, Field::of(*p))) + .collect() + } + + fn map_of_chars(input: &String) -> (HashMap, C, C) { + let mut map: HashMap = input + .lines() + .enumerate() + .flat_map(|(y, line)| { + line.chars() + .enumerate() + .map(move |(x, c)| (Point::of(x as C, y as C), c)) + }) + .collect(); + + let width = map.keys().map(|p| p.x).max().unwrap() + 1; + let height = map.keys().map(|p| p.y).max().unwrap() + 1; + + for x in 0..width { + for y in 0..height { + if !map.contains_key(&Point::of(x, y)) { + map.insert(Point::of(x, y), ' '); + } + } + } + (map, width, height) + } +} + +#[cfg(test)] +mod test { + use crate::tasks::day20::Maze; + + #[test] + fn example1() { + let input = std::fs::read_to_string("input/day20_example1.txt").unwrap(); + let maze = Maze::from(&input); + assert_eq!(maze.shortest_path(), 23); + } +} \ No newline at end of file diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index c7ddecc..8c632be 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -16,6 +16,7 @@ pub mod day16; pub mod day17; pub mod day18; pub mod day19; +pub mod day20; pub mod day21; #[allow(dead_code)] pub mod day22;