day(24): add solution
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
113e927a65
commit
f72e98c586
2 changed files with 150 additions and 0 deletions
6
samples/day24.txt
Normal file
6
samples/day24.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#.######
|
||||||
|
#>>.<^<#
|
||||||
|
#.<..<<#
|
||||||
|
#>v.><>#
|
||||||
|
#<^v^^>#
|
||||||
|
######.#
|
144
src/bin/day24.rs
Normal file
144
src/bin/day24.rs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
use std::{collections::HashSet, ops::Index};
|
||||||
|
|
||||||
|
use aoc_2022::*;
|
||||||
|
|
||||||
|
type Input = Basin;
|
||||||
|
type Output = isize;
|
||||||
|
|
||||||
|
type Position = Vector3D<isize>;
|
||||||
|
|
||||||
|
struct Basin {
|
||||||
|
map: Vec<Vec<char>>,
|
||||||
|
rows: isize,
|
||||||
|
cols: isize,
|
||||||
|
entry: Position,
|
||||||
|
exit: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Position> for Basin {
|
||||||
|
type Output = char;
|
||||||
|
|
||||||
|
fn index(&self, index: Position) -> &Self::Output {
|
||||||
|
if index.y() == 0 && index.x() == 1 {
|
||||||
|
// entry
|
||||||
|
return &'.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if index.y() == self.rows - 1 && index.x() == self.cols - 2 {
|
||||||
|
// exit
|
||||||
|
return &'.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if index.y() <= 0
|
||||||
|
|| index.y() >= self.rows - 1
|
||||||
|
|| index.x() <= 0
|
||||||
|
|| index.x() >= self.cols - 1
|
||||||
|
{
|
||||||
|
// out of bounds
|
||||||
|
return &'#';
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to account for the loops of the blizzards
|
||||||
|
let x_mod = self.cols - 2;
|
||||||
|
let y_mod = self.rows - 2;
|
||||||
|
|
||||||
|
let x_w = ((index.x() - 1 + x_mod - (index.z() % x_mod)) % x_mod + 1) as usize;
|
||||||
|
let x_e = ((index.x() - 1 + x_mod + (index.z() % x_mod)) % x_mod + 1) as usize;
|
||||||
|
let y_n = ((index.y() - 1 + y_mod - (index.z() % y_mod)) % y_mod + 1) as usize;
|
||||||
|
let y_s = ((index.y() - 1 + y_mod + (index.z() % y_mod)) % y_mod + 1) as usize;
|
||||||
|
|
||||||
|
if self.map[index.y() as usize][x_w] == '>' {
|
||||||
|
return &self.map[index.y() as usize][x_w];
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.map[index.y() as usize][x_e] == '<' {
|
||||||
|
return &self.map[index.y() as usize][x_e];
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.map[y_n][index.x() as usize] == 'v' {
|
||||||
|
return &self.map[y_n][index.x() as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.map[y_s][index.x() as usize] == '^' {
|
||||||
|
return &self.map[y_s][index.x() as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
&'.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_exit(basin: &Input, entry: Position, exit: Position) -> Position {
|
||||||
|
let next_positions = |p| {
|
||||||
|
[(0, 0, 1), (0, -1, 1), (0, 1, 1), (-1, 0, 1), (1, 0, 1)]
|
||||||
|
.iter()
|
||||||
|
.filter_map(move |&(x, y, t)| {
|
||||||
|
let next_p = p + Vector3D::new(x, y, t);
|
||||||
|
|
||||||
|
if basin[next_p] == '.' {
|
||||||
|
Some(next_p)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let cost = |p: Position| p.z() as usize + exit.y().abs_diff(p.y()) + exit.x().abs_diff(p.x());
|
||||||
|
|
||||||
|
let mut seen: HashSet<Position> = HashSet::new();
|
||||||
|
let mut q: MinHeap<(usize, Position)> = MinHeap::new();
|
||||||
|
q.push((cost(entry), entry));
|
||||||
|
|
||||||
|
while let Some((_, pos)) = q.pop() {
|
||||||
|
// debug!("Dequeued: {:?}", pos);
|
||||||
|
if pos.y() == exit.y() && pos.x() == exit.x() {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
for next_pos in next_positions(pos) {
|
||||||
|
if !seen.contains(&next_pos) {
|
||||||
|
seen.insert(next_pos);
|
||||||
|
q.push((cost(next_pos), next_pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Day24;
|
||||||
|
impl Solution<Input, Output> for Day24 {
|
||||||
|
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
||||||
|
let map: Vec<Vec<char>> = file_to_string(pathname)
|
||||||
|
.lines()
|
||||||
|
.map(|l| l.chars().collect())
|
||||||
|
.collect();
|
||||||
|
let (rows, cols) = (map.len() as isize, map[0].len() as isize);
|
||||||
|
|
||||||
|
Basin {
|
||||||
|
map,
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
entry: Vector3D::new(1, 0, 0),
|
||||||
|
exit: Vector3D::new(cols - 2, rows - 1, isize::MAX),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_1(input: &Input) -> Output {
|
||||||
|
find_exit(input, input.entry, input.exit).z()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_2(input: &Input) -> Output {
|
||||||
|
let to_exit = find_exit(input, input.entry, input.exit);
|
||||||
|
let to_entry = find_exit(input, to_exit, input.entry);
|
||||||
|
let back_to_exit = find_exit(input, to_entry, input.exit);
|
||||||
|
|
||||||
|
back_to_exit.z()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// Day24::run("sample")
|
||||||
|
Day24::main()
|
||||||
|
}
|
||||||
|
|
||||||
|
test_sample!(day_24, Day24, 18, 54);
|
Loading…
Reference in a new issue