day20 refactored for part 2

This commit is contained in:
2022-10-02 14:05:24 +02:00
parent 9a34cca688
commit 4002297ede

View File

@@ -2,12 +2,13 @@ use std::collections::{HashMap, HashSet, VecDeque};
pub fn run() { pub fn run() {
let input = std::fs::read_to_string("input/day20.txt").unwrap(); let input = std::fs::read_to_string("input/day20.txt").unwrap();
let maze = Maze::from(&input); let maze = Maze::from(&input, PortalField::create);
let part1 = maze.shortest_path(); let part1 = maze.shortest_path();
println!("Part 1: {}", part1); println!("Part 1: {}", part1);
} }
type C = i32; type C = i32;
type FieldFactory = fn(&Point) -> Box<dyn Field>;
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)] #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
struct Point { struct Point {
@@ -30,48 +31,91 @@ impl Point {
} }
} }
struct Field { trait Field {
fn neighbors(&self) -> Vec<Point>;
fn set_label_partner(&mut self, point: Point);
fn set_neighbors(&mut self, neighbors: Vec<Point>);
fn create(point: &Point) -> Box<dyn Field> where Self: Sized;
}
struct PortalField {
neighbors: Vec<Point>, neighbors: Vec<Point>,
} }
impl Field { impl PortalField {
fn of() -> Self { fn new() -> Self {
Field { neighbors: vec![] } PortalField { neighbors: vec![] }
} }
} }
pub struct Maze { impl Field for PortalField {
map: HashMap<Point, Field>, fn neighbors(&self) -> Vec<Point> {
self.neighbors.clone()
}
fn set_label_partner(&mut self, point: Point) {
self.neighbors.push(point);
}
fn set_neighbors(&mut self, neighbors: Vec<Point>) {
self.neighbors = neighbors;
}
fn create(_: &Point) -> Box<dyn Field> where Self: Sized {
Box::new(PortalField::new())
}
}
//impl Field for PortalField {
// fn neighbors(self, level: usize) -> Vec<Point> {
// if let Some((direction, p)) = self.dimension_neighbor {
// if direction == DimensionUp && level < 0 {
// let mut v = self.neighbors.clone();
// v.push(p);
// v
// } else {
// self.neighbors.clone()
// }
// } else {
// self.neighbors.clone()
// }
// }
//}
type FieldMap = HashMap<Point, Box<dyn Field>>;
struct Maze {
map: FieldMap,
start: Point, start: Point,
finish: Point, finish: Point,
} }
impl Maze { impl Maze {
pub fn from(input: &String) -> Self { fn from(input: &String, field_factory: FieldFactory) -> Self {
let (moc, width, height) = Self::map_of_chars(input); let (moc, width, height) = Self::map_of_chars(input);
let mut map = Self::create_map_of_free_spots(&moc); let mut map = Self::create_map_of_free_spots(&moc, field_factory);
Self::add_physical_neighbors(&mut map); Self::add_physical_neighbors(&mut map);
let labels = Self::labels(&moc, width, height); let labels = Self::labels(&moc, width, height);
Self::add_portals(&mut map, &labels); Self::process_labels(&mut map, &labels);
let start = labels["AA"][0]; let start = labels["AA"][0];
let finish = labels["ZZ"][0]; let finish = labels["ZZ"][0];
Maze { map, start, finish } Maze { map, start, finish }
} }
pub fn shortest_path(&self) -> usize { fn shortest_path(&self) -> usize {
bfs(&self.map, self.start, self.finish) bfs(&self.map, self.start, self.finish)
} }
fn add_portals(map: &mut HashMap<Point, Field>, labels: &HashMap<String, Vec<Point>>) { fn process_labels(map: &mut FieldMap, labels: &HashMap<String, Vec<Point>>) {
for (label, points) in labels { for (label, points) in labels {
if label == "AA" || label == "ZZ" { if label == "AA" || label == "ZZ" {
continue; continue;
} }
map.get_mut(&points[0]).unwrap().neighbors.push(points[1]); map.get_mut(&points[0]).unwrap().set_label_partner(points[1]);
map.get_mut(&points[1]).unwrap().neighbors.push(points[0]); map.get_mut(&points[1]).unwrap().set_label_partner(points[0]);
} }
} }
@@ -135,7 +179,7 @@ impl Maze {
} }
} }
fn add_physical_neighbors(map: &mut HashMap<Point, Field>) { fn add_physical_neighbors(map: &mut FieldMap) {
let points: HashSet<Point> = map.keys().map(|p| *p).collect(); let points: HashSet<Point> = map.keys().map(|p| *p).collect();
map.iter_mut().for_each(|(p, f)| { map.iter_mut().for_each(|(p, f)| {
let neighbors = p let neighbors = p
@@ -143,14 +187,14 @@ impl Maze {
.into_iter() .into_iter()
.filter(|neighbor| points.contains(neighbor)) .filter(|neighbor| points.contains(neighbor))
.collect(); .collect();
f.neighbors = neighbors; f.set_neighbors(neighbors);
}) })
} }
fn create_map_of_free_spots(moc: &HashMap<Point, char>) -> HashMap<Point, Field> { fn create_map_of_free_spots(moc: &HashMap<Point, char>, field_factory: FieldFactory) -> FieldMap {
moc.keys() moc.keys()
.filter(|p| moc[p] == '.') .filter(|p| moc[p] == '.')
.map(|p| (*p, Field::of())) .map(|p| (*p, field_factory(p)))
.collect() .collect()
} }
@@ -179,7 +223,7 @@ impl Maze {
} }
} }
fn bfs(map: &HashMap<Point, Field>, start: Point, finish: Point) -> usize { fn bfs(map: &FieldMap, start: Point, finish: Point) -> usize {
let mut open: VecDeque<(Point, usize)> = VecDeque::new(); let mut open: VecDeque<(Point, usize)> = VecDeque::new();
let mut seen: HashSet<Point> = HashSet::new(); let mut seen: HashSet<Point> = HashSet::new();
@@ -190,11 +234,11 @@ fn bfs(map: &HashMap<Point, Field>, start: Point, finish: Point) -> usize {
return d; return d;
} }
for neighbor in &map[&p].neighbors { for neighbor in map[&p].neighbors() {
if !seen.contains(neighbor) { if !seen.contains(&neighbor) {
open.push_back((*neighbor, d + 1)); open.push_back((neighbor, d + 1));
} }
seen.insert(*neighbor); seen.insert(neighbor);
} }
} }
panic!("no path found") panic!("no path found")
@@ -202,19 +246,19 @@ fn bfs(map: &HashMap<Point, Field>, start: Point, finish: Point) -> usize {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::tasks::day20::Maze; use crate::tasks::day20::{Field, Maze, PortalField};
#[test] #[test]
fn example1() { fn example1() {
let input = std::fs::read_to_string("input/day20_example1.txt").unwrap(); let input = std::fs::read_to_string("input/day20_example1.txt").unwrap();
let maze = Maze::from(&input); let maze = Maze::from(&input, PortalField::create);
assert_eq!(maze.shortest_path(), 23); assert_eq!(maze.shortest_path(), 23);
} }
#[test] #[test]
fn result() { fn result() {
let input = std::fs::read_to_string("input/day20.txt").unwrap(); let input = std::fs::read_to_string("input/day20.txt").unwrap();
let maze = Maze::from(&input); let maze = Maze::from(&input, PortalField::create);
assert_eq!(maze.shortest_path(), 454); assert_eq!(maze.shortest_path(), 454);
} }
} }