Day 24, part 1
This commit is contained in:
parent
98fcdfeaef
commit
a7d32abb93
13
src/bin/d24p1.rs
Normal file
13
src/bin/d24p1.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use aoc22::{
|
||||
day24::{self},
|
||||
util,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
let (map, start, target) = day24::parse_map(&util::parse_input());
|
||||
|
||||
day24::print_map(&map, &start);
|
||||
|
||||
let rounds = day24::find_path(&map, &start, &target);
|
||||
println!("Goal is reachable in {} min", rounds);
|
||||
}
|
177
src/day24.rs
Normal file
177
src/day24.rs
Normal file
@ -0,0 +1,177 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::util::{Coord, Dir};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Tile {
|
||||
Empty,
|
||||
Wall,
|
||||
Blizzards(Vec<Dir>),
|
||||
}
|
||||
|
||||
/// Returns (map, start, end)
|
||||
pub fn parse_map(input: &str) -> (Vec<Vec<Tile>>, Coord, Coord) {
|
||||
let mut map = Vec::new();
|
||||
|
||||
for line in input.lines() {
|
||||
let mut row = Vec::new();
|
||||
|
||||
for c in line.chars() {
|
||||
row.push(match c {
|
||||
'.' => Tile::Empty,
|
||||
'#' => Tile::Wall,
|
||||
_ => Tile::Blizzards(vec![Dir::from_char(c).unwrap()]),
|
||||
});
|
||||
}
|
||||
|
||||
map.push(row);
|
||||
}
|
||||
|
||||
assert!(map.len() >= 2);
|
||||
let last_row = map.len() - 1;
|
||||
|
||||
let start = map[0]
|
||||
.iter()
|
||||
.position(|t| matches!(t, Tile::Empty))
|
||||
.unwrap();
|
||||
let end = map[last_row]
|
||||
.iter()
|
||||
.position(|t| matches!(t, Tile::Empty))
|
||||
.unwrap();
|
||||
|
||||
(map, (0, start), (last_row, end))
|
||||
}
|
||||
|
||||
pub fn print_map(map: &Vec<Vec<Tile>>, expedition: &Coord) {
|
||||
for (y, row) in map.iter().enumerate() {
|
||||
for (x, tile) in row.iter().enumerate() {
|
||||
if *expedition == (y, x) {
|
||||
print!("E");
|
||||
} else {
|
||||
match tile {
|
||||
Tile::Empty => print!("."),
|
||||
Tile::Wall => print!("#"),
|
||||
Tile::Blizzards(blizzards) => {
|
||||
if blizzards.len() > 1 {
|
||||
print!("{}", blizzards.len());
|
||||
} else {
|
||||
print!("{}", blizzards[0].to_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print!("\n");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_map(map: &Vec<Vec<Tile>>) -> Vec<Vec<Tile>> {
|
||||
assert!(map.len() > 0);
|
||||
// Outermost rows/columns are walls
|
||||
let min_y = 1;
|
||||
let max_y = map.len() - 2;
|
||||
let min_x = 1;
|
||||
let max_x = map[0].len() - 2;
|
||||
|
||||
// Start with a map without blizzards
|
||||
let mut result: Vec<Vec<Tile>> = map
|
||||
.iter()
|
||||
.map(|r| {
|
||||
r.iter()
|
||||
.map(|t| {
|
||||
if matches!(t, Tile::Blizzards(_)) {
|
||||
Tile::Empty
|
||||
} else {
|
||||
t.clone()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
for y in 0..map.len() {
|
||||
for x in 0..map[0].len() {
|
||||
if let Tile::Blizzards(blizzards) = &map[y][x] {
|
||||
for blizzard_dir in blizzards {
|
||||
let new_tile = match blizzard_dir {
|
||||
Dir::Right => {
|
||||
if x == max_x {
|
||||
(y, min_x)
|
||||
} else {
|
||||
(y, x + 1)
|
||||
}
|
||||
}
|
||||
Dir::Down => {
|
||||
if y == max_y {
|
||||
(min_y, x)
|
||||
} else {
|
||||
(y + 1, x)
|
||||
}
|
||||
}
|
||||
Dir::Left => {
|
||||
if x == min_x {
|
||||
(y, max_x)
|
||||
} else {
|
||||
(y, x - 1)
|
||||
}
|
||||
}
|
||||
Dir::Up => {
|
||||
if y == min_y {
|
||||
(max_y, x)
|
||||
} else {
|
||||
(y - 1, x)
|
||||
}
|
||||
}
|
||||
};
|
||||
let new_tile = &mut result[new_tile.0][new_tile.1];
|
||||
if let Tile::Blizzards(blizzards) = new_tile {
|
||||
blizzards.push(*blizzard_dir);
|
||||
} else {
|
||||
assert!(matches!(new_tile, Tile::Empty));
|
||||
*new_tile = Tile::Blizzards(vec![*blizzard_dir]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns length of the shortest path
|
||||
pub fn find_path(initial: &Vec<Vec<Tile>>, start: &Coord, target: &Coord) -> usize {
|
||||
let mut map = initial.clone();
|
||||
let mut to_check = HashSet::new();
|
||||
to_check.insert(*start);
|
||||
let mut rounds = 0;
|
||||
|
||||
loop {
|
||||
map = next_map(&map);
|
||||
rounds += 1;
|
||||
let mut check_next = HashSet::new();
|
||||
|
||||
for pos in to_check {
|
||||
let mut reachable = vec![
|
||||
pos,
|
||||
(pos.0 + 1, pos.1),
|
||||
(pos.0, pos.1 + 1),
|
||||
(pos.0, pos.1 - 1),
|
||||
];
|
||||
// This would underflow in the first round
|
||||
if pos.0 != 0 {
|
||||
reachable.push((pos.0 - 1, pos.1));
|
||||
}
|
||||
|
||||
for p in reachable {
|
||||
if p == *target {
|
||||
return rounds;
|
||||
}
|
||||
if matches!(map[p.0][p.1], Tile::Empty) {
|
||||
check_next.insert(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
to_check = check_next;
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ pub mod day20;
|
||||
pub mod day21;
|
||||
pub mod day22;
|
||||
pub mod day23;
|
||||
pub mod day24;
|
||||
pub mod day3;
|
||||
pub mod day4;
|
||||
pub mod day5;
|
||||
|
19
src/util.rs
19
src/util.rs
@ -144,6 +144,25 @@ impl Dir {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_char(c: char) -> Option<Dir> {
|
||||
match c {
|
||||
'^' => Some(Dir::Up),
|
||||
'>' => Some(Dir::Right),
|
||||
'v' => Some(Dir::Down),
|
||||
'<' => Some(Dir::Left),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Dir::Right => ">",
|
||||
Dir::Down => "v",
|
||||
Dir::Left => "<",
|
||||
Dir::Up => "^",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cw(&self) -> Dir {
|
||||
let d = *self as usize;
|
||||
Self::from_usize((d + 1) % 4).unwrap()
|
||||
|
Loading…
x
Reference in New Issue
Block a user