day20 shortest path to come
This commit is contained in:
199
src/tasks/day20.rs
Normal file
199
src/tasks/day20.rs
Normal file
@@ -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<Point> {
|
||||
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<Point>,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
fn of(point: Point) -> Self {
|
||||
Field {
|
||||
point,
|
||||
neighbors: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Maze {
|
||||
map: HashMap<Point, Field>,
|
||||
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<Point, Field>, labels: &HashMap<String, Vec<Point>>) {
|
||||
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<Point, char>, width: C, height: C) -> HashMap<String, Vec<Point>> {
|
||||
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<String> {
|
||||
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<Point, Field>) {
|
||||
let points: HashSet<Point> = 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<Point, char>) -> HashMap<Point, Field> {
|
||||
moc.keys()
|
||||
.filter(|p| moc[p] == '.')
|
||||
.map(|p| (*p, Field::of(*p)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn map_of_chars(input: &String) -> (HashMap<Point, char>, C, C) {
|
||||
let mut map: HashMap<Point, char> = 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user