day20 refactored for part 2
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user