1
0
Fork 0

day(24): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2023-07-03 10:57:50 +02:00
parent 113e927a65
commit f72e98c586
Signed by: mfocko
GPG key ID: 7C47D46246790496
2 changed files with 150 additions and 0 deletions

6
samples/day24.txt Normal file
View file

@ -0,0 +1,6 @@
#.######
#>>.<^<#
#.<..<<#
#>v.><>#
#<^v^^>#
######.#

144
src/bin/day24.rs Normal file
View 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);