aoc

My solutions for the Advent of Code
git clone https://git.tronto.net/aoc
Download | Log | Files | Refs | README

commit d78541c47c0b2b2a977a8a23114541bbbf7a0556
parent 6fb3f5aa5a2c076bd6263fca2b105d5837513c25
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Wed,  9 Jul 2025 17:18:36 +0200

Day 24 2022

Diffstat:
A2022/24/a.rs | 8++++++++
A2022/24/b.rs | 14++++++++++++++
A2022/24/common.rs | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2022/24/input | 37+++++++++++++++++++++++++++++++++++++
A2022/24/test | 6++++++
5 files changed, 269 insertions(+), 0 deletions(-)

diff --git a/2022/24/a.rs b/2022/24/a.rs @@ -0,0 +1,8 @@ +mod common; +use common::*; + +fn main() { + let map = read_map_from_stdin(); + let result = map.shortest_path(Position::Start, Position::Finish, 0); + println!("{result}"); +} diff --git a/2022/24/b.rs b/2022/24/b.rs @@ -0,0 +1,14 @@ +mod common; +use common::*; + +fn main() { + // The approach is greedy: once we reach the start / finish cell, + // we can wait there as long as we want, so it is always better + // to reach the partial destinations as soon as possible. + + let map = read_map_from_stdin(); + let a = map.shortest_path(Position::Start, Position::Finish, 0); + let b = map.shortest_path(Position::Finish, Position::Start, a); + let c = map.shortest_path(Position::Start, Position::Finish, b); + println!("{c}"); +} diff --git a/2022/24/common.rs b/2022/24/common.rs @@ -0,0 +1,204 @@ +// This code is more complicated than it should be. The reason is that +// for part 1 I decided to ignore the start and finish position, and not +// to treat them as other cells. This way I could get away with not +// considering wall cells '#' at all. +// But then for part 2 it was more convenient to include them, so I +// added an enum Position {} that treats start and finish as special +// cases. This is quite messy. + +use std::collections::{VecDeque, HashSet}; + +#[derive(PartialEq, Debug)] +pub enum Direction { Up, Down, Left, Right, None } + +impl Direction { + pub fn from_byte(b: u8) -> Direction { + match b { + b'^' => Direction::Up, + b'v' => Direction::Down, + b'>' => Direction::Right, + b'<' => Direction::Left, + b'.' => Direction::None, + x => panic!("unexpected char '{}'", x as char) + } + } +} + +#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)] +pub enum Position { + Start, + Finish, + Other { i: usize, j: usize } +} + +impl Position { + pub fn dist(&self, other: Position) -> usize { + match (*self, other) { + (Position::Start, Position::Start) | + (Position::Finish, Position::Finish) => + 0, + (Position::Start, Position::Finish) | + (Position::Finish, Position::Start) => + usize::MAX, // Not technically correct, but it works + (Position::Start | Position::Finish, Position::Other { i, j }) | + (Position::Other { i, j }, Position::Start | Position::Finish) => + i + j, + (Position::Other { i: i1, j: j1 }, + Position::Other { i: i2, j: j2 }) => { + let i = (i1 as i64 - i2 as i64).abs() as usize; + let j = (j1 as i64 - j2 as i64).abs() as usize; + i + j + } + } + } +} + +pub struct Map { + pub tiles: Vec<Vec<Direction>>, + pub imax: usize, + pub jmax: usize +} + +#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)] +struct Node { + p: Position, + t: usize +} + +impl Map { + fn submod(i: usize, t: usize, m: usize) -> usize { + let maybeneg = ((i as i64) - (t as i64)) % m as i64; + (maybeneg as usize + m) % m + } + + pub fn is_busy(&self, p: Position, t: usize) -> bool { + match p { + Position::Start | Position::Finish => false, + Position::Other { i, j } => { + let iup = Self::submod(i, t, self.imax); + let idown = (i + t) % self.imax; + let jleft = Map::submod(j, t, self.jmax); + let jright = (j + t) % self.jmax; + + self.tiles[iup][j] == Direction::Down || + self.tiles[idown][j] == Direction::Up || + self.tiles[i][jleft] == Direction::Right || + self.tiles[i][jright] == Direction::Left + } + } + } + + #[allow(dead_code)] + pub fn print(&self, t: usize) { + print!("#."); + for _ in 0..self.jmax { print!("#"); } + println!(); + + for i in 0..self.imax { + print!("#"); + for j in 0..self.jmax { + let p = Position::Other { i, j }; + print!("{}", if self.is_busy(p, t) { '*' } else { '.' }); + } + println!("#"); + } + + for _ in 0..self.jmax { print!("#"); } + print!(".#"); + println!(); + } + + fn mv(&self, p: Position, d: Direction) -> Option<Position> { + if d == Direction::None { return Some(p); } + match p { + Position::Start => if d == Direction::Down { + Some(Position::Other { i: 0, j: 0 }) + } else { None }, + Position::Finish => if d == Direction::Up { + Some(Position::Other { i: self.imax-1, j: self.jmax-1 }) + } else { None }, + Position::Other { i, j } => match d { + Direction::Up => if i > 0 { + Some(Position::Other { i: i-1, j }) + } else { + if i == 0 && j == 0 { + Some(Position::Start) + } else { None } + }, + Direction::Down => if i < self.imax-1 { + Some(Position::Other { i: i+1, j }) + } else { + if i == self.imax-1 && j == self.jmax-1 { + Some(Position::Finish) + } else { None } + }, + Direction::Left => if j > 0 { + Some(Position::Other { i, j: j-1 }) + } else { None }, + Direction::Right => if j < self.jmax-1 { + Some(Position::Other { i, j: j+1 }) + } else { None }, + Direction::None => panic!("stupid rust") + } + } + } + + fn maybe_push(&self, x: Node, d: Direction, v: &mut HashSet<Node>) { + if let Some(p) = self.mv(x.p, d) { + if !self.is_busy(p, x.t+1) { + v.insert(Node { p, t: x.t+1 }); + } + } + } + + pub fn shortest_path( + &self, + start: Position, + finish: Position, + t0: usize + ) -> usize { + let mut vnext = HashSet::<Node>::new(); + vnext.insert(Node { p: start, t: t0 }); + while !vnext.is_empty() { + let mut v = vnext.clone().into_iter().collect::<Vec<_>>(); + // Heuristic: explore first the positions closer to the target + v.sort_by(|a, b| a.p.dist(finish).cmp(&(b.p.dist(finish)))); + let mut v = VecDeque::<Node>::from(v); + vnext.clear(); + while !v.is_empty() { + let x = v.pop_front().unwrap(); + if x.p == finish { return x.t; } + + self.maybe_push(x, Direction::Up, &mut vnext); + self.maybe_push(x, Direction::Down, &mut vnext); + self.maybe_push(x, Direction::Left, &mut vnext); + self.maybe_push(x, Direction::Right, &mut vnext); + if !self.is_busy(x.p, x.t+1) { + self.maybe_push(x, Direction::None, &mut vnext); + } + } + } + panic!("queue is empty"); + } +} + +pub fn read_map_from_stdin() -> Map { + let mut tiles = vec![]; + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + let bytes = line.as_bytes(); + if bytes[2] == b'#' { + line.clear(); + continue; + } + let i = tiles.len(); + tiles.push(vec![]); + for j in 1..bytes.len()-2 { + tiles[i].push(Direction::from_byte(bytes[j])); + } + line.clear(); + } + let imax = tiles.len(); + let jmax = tiles[0].len(); + Map { tiles, imax, jmax } +} diff --git a/2022/24/input b/2022/24/input @@ -0,0 +1,37 @@ +#.#################################################################################################### +#<v>v>v><>^<>^.^vv<>><>>v.>v<.v>>v<<v<.^<>>>v<v^>.^<^>v>^<^v.^>v^>>><v<>v^<v<<^^.vv>.^>v>v<.^v<><v<>># +#>>v<^<>vvv.>^.v^^>^<<vv>>v.<^<>v^<v^>v^<>>.v.^<<v^.^v^v><<.^vv<^<^v>.vvv<v>.<>>^>.<<<<<<.<^^<v.<>.v<# +#<<<>^^>>>>.v^>v<>^>^^v>v^^v..v^.<<^>^>.>v^^vv.<>>^><<>v.<v<^^<<v^v<.>^>v<>^^<<>v<<^^><vv>vv<<^^...^># +#>>v><><<<^<.>v^<<v^>v^^<>>v<<>>.<>v><^<^<.^<><^^^vv>><v<v>vv>^>v^>>vv>><<v^<><>^v.vv>^<^<>v<vvvv^v><# +#<^<^>v^^<>>^<.^^>^vv><v<^^^.v^>><v^>v^.>>v^<..>>.v>v<v^^<.v<<^v<<>v.^>><>v<<>.^<<<<<>>>^<><v>><^>>v># +#>v>^^^v<v^.>^>^>v>>>v>v<v^v^<v>>><<>v..>>vvv<^><<>>^^.>v^<^>>.^v><v<^<.^>>vv<v^<^vv^<<<^><v.>v<^<.v># +#>v^>>v^<^<<^<v>..<.v^<.<><>v>v^<.><<<<^><<.<><><><.<.^.>^v^><<<vv.<>vvv<v^v.<^<>>>^>v<^v^<><><v>.^><# +#<><>>vv..<v^vv><^v^.^^vv.^>^>>v<>vv>^><<<><^vvv>>><>><>>^^<v<><^^v^^>.^>^<.<<^..v<v>v^>^v^v<<.v^<v<># +#>><^<>.v.><^.^^..^<v.v><v^<<v>^<><^>^..<vv>><^^>v^>.^><v>v^v>>>>vv<<>><v^<<^^v^vv<.>vv^><v^.^v>...v># +#>vv<v<><v>v>v>><^>>.<^<<^<^>^.^v>>vv^vv<^v><>>v^<^v.v^<v<vv^v><.^^>><.v^^><<^<><^>>vv^.><vv^<>>v.><<# +#<^<^<>v^v^^^<<v<>vv<<<.v<v^v^^><.>^.v.>vvv>^<<<v>^^>.^<<.v<v>^v..v<<<<<^^v^v^<>^>>^.>^<^v<.v..^v^>^># +#<<^^<>^^><v>v.<<><<<v<^<^>^<<^v<<>^^<.^^^^<>^>><^>.v^v>v.<.^<<vv^^>>>^v<v<<vv^><<.<>>><.v<^^>>^v>^^<# +#>v><>..v^>v>^<<^>vv>.^v^v>>v^^^>><><<>v^.vv^.<><v>^^v^>.<^><<^^>>v^.^^^>v.<>^^<^>>^<^<<<^>^<>>^<<v<># +#>vv<.vv><>^>>>>.<>.>v^>>v><<<v<.v^v>v^<v^<>>vv><.<><v>^>^<^^v^>v^<><>^^^>^<<^^.>..>.>^>^>>^>v>v<v<><# +#<>>v^>^<.v^^.^<vv^<>^>^^<^.^.>^<<^>><^vv><.v><^vv^^^^^v><><vv.v.>.^^v^v.>^v.<><v>>v>vv^.vv^^<>^>.><<# +#>>>>>>>.v<>^<^.>><v^^<v.<^<^>vv.^v>^>.>^>>^>>v^>v>.v.><<^^<^<vv.vv<vvvvv^^v><^^^>>><^>>v<v>>>v<^^.^># +#>^^^^<<v^^^...^<v>>^^v^^><<.<><>^v^v^>>>^^>.v<v.v<v^^v<vv>v>vv>^v^<>^vv^vv.^vv>v^v>v>>.<>>><^^<^^>^<# +#<^^^<v>^.<>^>^<^<v>vv<vv<^vv^^<><v^>v>^.><^^v<>><>vvvv^>^^<<^v><<v^v<><^<<>^.v<><vv<><>>^.>v>vv>><^<# +#>v^^^^><^<vv<^<v>v.<^.><.^>>^<>v.<<^<>.>vv<<.vv.v>v^.<^<^^>>^.><>v<v<v>v<v>>vv>^<><^<v>>><v>><<vvv^.# +#>><>><^^<^^>v<^.^>><<v<^v<>v>v^<<<<^^.v>v^.>v^<^><^v<v.>><v<^>^^>^^^^.<v>^>.v^<^^<<><>.<v^v>^^>v<^.<# +#><v>v^^<>.vvv>>.<..^^.<<^>>v^<>.<v<<vv><v<^v.^>>>v>.<^>.vvv<v<^<^^>^>v>v<>v<^v<vv^>v<<<<<>.^v^^.>><.# +#<>>.<>^^>>><>>^<><<^^v^><v><<<<v>>^<^vv<<.v^<<<^>v>v^<^.<<<<^<^v^>vv.<.^><v<.^^<^>^.>^v^v>^>^<^.v><.# +#<^<.<^^<>^v<^<^>^>^.>.<<^.v<>v^^>>..v^v<.>^>^.^^v>^v^>vv>>^<^>>>^>><<v><><<v^<>>v.vv><>.><^.v<^<v<<># +#<v^.v<>^^>v>.vv><>^v<^<^.<^>>v^^.><^<v<>v<<vv<>.<>v<v<>.>^^.v.^v><^^.^v<vv<<^<>>v.>^^.>^^><>>>>.^^.<# +#<>^<>^><.^^^v<^v^>v.>^<>^^v<vv^><v^^>^.><>^v<v>v<>.^><^^>^v<<.>>^^<.v<><vv>vv>>>v^v><v^<^<>v<^>>^<.<# +#>v^<v^>^>>^<^<^>v<^vvvv<<<vv<.><^<.^^>.>^.v<v<><<.<v>v^vv<>^<v<^<>>.^<>^v<vv<<><<.<^><v<<^><^<><<>^># +#<<><..v<^v<>^.v>v^.<v.><>>v>>vv>v^^^><vvv<.>^^<v<vv<<v<.^^^..^<<<^^v^>><>>^v>v><<>^^v..>^<^vv^<v><^<# +#><>vv<<<.vv>^^<v>^<^<vvv^v<<<<<^v<.>>^><^v>vv><v>>>^^^<v.><>><<v^^.vv.^v><<<<<<^>^v>>.>>v.>v^<<>^v^.# +#.^>>>vv>vv>^^v>>^.<v>^v.>^>..<v.><^<^>.^<v^<>^v^v^>vv<>v^^><<.^>>^..<^^>><.vv>>v^<v><<^v>>>>v><>.^v># +#>^^^>v<><><v>>>^^vv^^^v.v><v<>>^v^v^^>^v.>vv..^>>^v^<.^<<v<>^v<^<..^v>^<^^^v.v^>^^<>v><^<.^<v^.v.v>># +#>v>v>>>vvvv<>>v^><>v<v^.^>>v>.v^vv.^^v.^>>>^^^>v.v<^>>v<v<>^^v^^>^<v..v^<^>^v^vv<<v><vv.v^v.vv>v<^.># +#><^<>v^>>^><>vv>v^><<v<.>^.v^><<<^v>^>.v<<>^vvvv>.vv^v.><<^<v^.<.vv.vv^^>v^>v>>v^v><<v<^<v<><^v<>^^<# +#<vv^^>v>v<^>.<>..>>vv^^v^<vv>^.>^<^><v><^>>>><.^>v^<v^>v^<>.<.>.<<^<v>><v>>vvv<>.v^<>vvvv>v<.>^vv^v># +#>.<^<<^<>^v.^v^>v^<<>^^>^<.vvvv<^<.v^<>v>><v^^.v^v^<>><.^<v<<>.>v^>^><.>^>>.v^.^..^<v^^v^vv<^v.><<>># +#<>>>v<vv.^v>v^.v<<v<><v..v>vv^^v^<vv>><<v<v^>v<.>^.^v^<.>><><>^<>>vv>^>.>v><><^vv>>v>^^<<..><.>.>^.># +####################################################################################################.# diff --git a/2022/24/test b/2022/24/test @@ -0,0 +1,6 @@ +#.###### +#>>.<^<# +#.<..<<# +#>v.><># +#<^v^^># +######.#