ΠΠ°Π΄Π°ΡΠ° 6-Π³ΠΎ Π΄Π½Ρ Advent of Code β Guard Gallivant.
π Π§Π°ΡΡΡ I. ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π·Π°Π΄Π°ΡΠΈ
ΠΠ°ΠΌ ΡΡΠ΅Π±ΡΠ΅ΡΡΡ ΡΠΌΠΎΠ΄Π΅Π»ΠΈΡΠΎΠ²Π°ΡΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠ° ΠΏΠΎ Π»Π°Π±ΠΎΡΠ°ΡΠΎΡΠΈΠΈ, ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½Π½ΠΎΠΉ Π² Π²ΠΈΠ΄Π΅ ΡΠ΅ΡΠΊΠΈ.
- ΠΡ
ΡΠ°Π½Π½ΠΈΠΊ Π½Π°ΡΠΈΠ½Π°Π΅Ρ Π² ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ½Π½ΠΎΠΉ ΠΊΠ»Π΅ΡΠΊΠ΅, ΡΠΌΠΎΡΡΠΈΡ Π² ΠΎΠ΄Π½ΠΎΠΌ ΠΈΠ· ΡΠ΅ΡΡΡΡΡ
Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠΉ ΠΈ ΡΠ»Π΅Π΄ΡΠ΅Ρ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΏΡΠ°Π²ΠΈΠ»Π°ΠΌ:β
- ΠΡΠ»ΠΈ ΠΏΠ΅ΡΠ΅Π΄ Π½ΠΈΠΌ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠ΅ (
#
), ΠΎΠ½ ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π΅Ρ Π½Π°ΠΏΡΠ°Π²ΠΎ Π½Π° 90Β°
.
- ΠΠ½Π°ΡΠ΅ Π΄Π΅Π»Π°Π΅Ρ ΡΠ°Π³ Π²ΠΏΠ΅ΡΡΠ΄.β
ΠΠ°Π΄Π°ΡΠ° β ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΡΡ
ΠΊΠ»Π΅ΡΠΎΠΊ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊ ΠΏΠΎΡΠ΅ΡΠΈΡ, ΠΏΡΠ΅ΠΆΠ΄Π΅ ΡΠ΅ΠΌ ΠΏΠΎΠΊΠΈΠ½Π΅Ρ ΠΊΠ°ΡΡΡ.β
π‘ ΠΠ΄Π΅Ρ
ΠΠ΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΈΠΌΡΠ»ΠΈΡΠΎΠ²Π°ΡΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠ°, ΡΡΠΈΡΡΠ²Π°Ρ Π΅Π³ΠΎ ΡΠ΅ΠΊΡΡΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΈ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅. ΠΠ»Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡ ΠΏΠΎΡΠ΅ΡΡΠ½Π½ΡΡ
ΠΊΠ»Π΅ΡΠΎΠΊ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π΄Π²ΡΠΌΠ΅ΡΠ½ΡΠΉ ΠΌΠ°ΡΡΠΈΠ², Π³Π΄Π΅ ΠΊΠ°ΠΆΠ΄Π°Ρ ΠΊΠ»Π΅ΡΠΊΠ° Ρ
ΡΠ°Π½ΠΈΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡΡ
, Ρ ΠΊΠΎΡΠΎΡΡΡ
ΠΎΠ½Π° Π±ΡΠ»Π° ΠΏΠΎΡΠ΅ΡΠ΅Π½Π°. ΠΡΠ»ΠΈ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΡΡΡ Π² ΡΡ ΠΆΠ΅ ΠΊΠ»Π΅ΡΠΊΡ Ρ ΡΠ΅ΠΌ ΠΆΠ΅ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ, ΡΡΠΎ ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ Π½Π°ΡΠ°Π»ΠΎ ΡΠΈΠΊΠ»Π°.
π Π§Π°ΡΡΡ II. ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π·Π°Π΄Π°ΡΠΈ
ΠΠΎ Π²ΡΠΎΡΠΎΠΉ ΡΠ°ΡΡΠΈ Π·Π°Π΄Π°ΡΠΈ ΡΡΠ΅Π±ΡΠ΅ΡΡΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΠΎΠ·ΠΈΡΠΈΠΉ, Π³Π΄Π΅ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΡ ΠΏΡΠΈΠ²Π΅Π΄ΡΡ ΠΊ ΡΠΎΠΌΡ, ΡΡΠΎ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊ Π·Π°ΡΠΈΠΊΠ»ΠΈΡΡΡ ΠΈ Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ ΠΏΠΎΠΊΠΈΠ½Π΅Ρ ΠΊΠ°ΡΡΡ. ΠΡΠ΅Π²ΠΈΠ΄Π½ΠΎ, ΡΡΠΎ Π½Π΅Π»ΡΠ·Ρ ΡΠ°Π·ΠΌΠ΅ΡΠ°ΡΡ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠ΅ Π² Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠ°.β
π‘ ΠΠ΄Π΅Ρ
ΠΠ»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠ»Π΅ΡΠΊΠΈ, ΠΏΠΎΡΠ΅ΡΡΠ½Π½ΠΎΠΉ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠΎΠΌ Π² ΠΏΠ΅ΡΠ²ΠΎΠΉ ΡΠ°ΡΡΠΈ (ΠΊΡΠΎΠΌΠ΅ Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠΉ), Π²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠ΅ ΠΈ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎ ΡΠΈΠΌΡΠ»ΠΈΡΡΠ΅ΠΌ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠ°. ΠΡΠ»ΠΈ ΠΏΡΠΈ ΡΡΠΎΠΌ ΠΎΠ½ ΠΏΠΎΠΏΠ°Π΄Π°Π΅Ρ Π² ΡΠΈΠΊΠ», ΡΠ²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΡΡΡΡΡΠΈΠΊ. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ, ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΠΎΠ·ΠΈΡΠΈΠΉ, Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΡ Π² ΠΊΠΎΡΠΎΡΡΠ΅ Π²ΡΠ·ΡΠ²Π°Π΅Ρ Π·Π°ΡΠΈΠΊΠ»ΠΈΠ²Π°Π½ΠΈΠ΅.
π ΠΠ΅ΡΠ°Π»ΠΈ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ
- Π‘ΡΡΡΠΊΡΡΡΠ°
Direction
: ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»ΡΠ΅Ρ ΡΠ΅ΡΡΡΠ΅ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΡ ΠΈ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ ΠΌΠ΅ΡΠΎΠ΄Ρ Π΄Π»Ρ ΠΏΠΎΠ²ΠΎΡΠΎΡΠ° Π½Π°ΠΏΡΠ°Π²ΠΎ, ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΠΊΠ»Π΅ΡΠΊΠΈ ΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π±ΠΈΡΠΎΠ²ΠΎΠΉ ΠΌΠ°ΡΠΊΠΈ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ.β
- Π‘ΡΡΡΠΊΡΡΡΠ°
Guard
: Ρ
ΡΠ°Π½ΠΈΡ ΡΠ΅ΠΊΡΡΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΠΈ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠ°. ΠΠ΅ΡΠΎΠ΄ step ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅Ρ Π»ΠΎΠ³ΠΈΠΊΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΡ: Π΅ΡΠ»ΠΈ ΠΏΠ΅ΡΠ΅Π΄ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠΎΠΌ ΠΏΡΠ΅ΠΏΡΡΡΡΠ²ΠΈΠ΅ ΠΈΠ»ΠΈ Π³ΡΠ°Π½ΠΈΡΠ°, ΠΎΠ½ ΠΏΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π΅Ρ Π½Π°ΠΏΡΠ°Π²ΠΎ; ΠΈΠ½Π°ΡΠ΅ Π΄Π΅Π»Π°Π΅Ρ ΡΠ°Π³ Π²ΠΏΠ΅ΡΡΠ΄.β
- ΠΠ΅ΡΠΎΠ΄
simulate_guard
: ΡΠΈΠΌΡΠ»ΠΈΡΡΠ΅Ρ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠ° Π΄ΠΎ Π²ΡΡ
ΠΎΠ΄Π° Π·Π° Π³ΡΠ°Π½ΠΈΡΡ ΠΊΠ°ΡΡΡ ΠΈΠ»ΠΈ Π΄ΠΎ ΠΏΠΎΠΏΠ°Π΄Π°Π½ΠΈΡ Π² ΡΠΈΠΊΠ». ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠ»Π°Π³ Π²ΡΡ
ΠΎΠ΄Π° ΠΈ ΠΌΠ°ΡΡΠΈΠ² ΠΏΠΎΡΠ΅ΡΡΠ½Π½ΡΡ
Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠΉ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠ»Π΅ΡΠΊΠΈ.β
- ΠΠ΅ΡΠΎΠ΄Ρ
solve_simple
ΠΈ solve_hard
: ΡΠ΅Π°Π»ΠΈΠ·ΡΡΡ ΡΠ΅ΡΠ΅Π½ΠΈΡ Π΄Π»Ρ ΠΏΠ΅ΡΠ²ΠΎΠΉ ΠΈ Π²ΡΠΎΡΠΎΠΉ ΡΠ°ΡΡΠΈ Π·Π°Π΄Π°ΡΠΈ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎ, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ ΡΠΈΠΌΡΠ»ΡΡΠΈΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΡ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊΠ°, ΠΈ Π°Π½Π°Π»ΠΈΠ·ΠΈΡΡΡ Π΅Π³ΠΎ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΡ.β
π ΠΡΠΈΠΌΠΏΡΠΎΡΠΈΠΊΠ°
- Π§Π°ΡΡΡ I: Π² Ρ
ΡΠ΄ΡΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅ ΠΎΡ
ΡΠ°Π½Π½ΠΈΠΊ ΠΏΠΎΡΠ΅ΡΠ°Π΅Ρ Π²ΡΠ΅ ΠΊΠ»Π΅ΡΠΊΠΈ ΠΊΠ°ΡΡΡ, ΠΏΠΎΡΡΠΎΠΌΡ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡΡ ΡΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ
O(N)
, Π³Π΄Π΅ N
β ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΊΠ»Π΅ΡΠΎΠΊ.β
- Π§Π°ΡΡΡ II: Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΏΠΎΡΠ΅ΡΡΠ½Π½ΠΎΠΉ ΠΊΠ»Π΅ΡΠΊΠΈ (Π΄ΠΎ
N
), ΠΊΡΠΎΠΌΠ΅ Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠΉ, ΠΏΡΠΎΠ²ΠΎΠ΄ΠΈΡΡΡ Π½ΠΎΠ²Π°Ρ ΡΠΈΠΌΡΠ»ΡΡΠΈΡ, ΠΊΠ°ΠΆΠ΄Π°Ρ ΠΈΠ· ΠΊΠΎΡΠΎΡΡΡ
ΠΈΠΌΠ΅Π΅Ρ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡΡ O(N)
. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ, ΠΎΠ±ΡΠ°Ρ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡΡ β O(NΒ²)
.
- ΠΡΠΎΡΡΡΠ°Π½ΡΡΠ²Π΅Π½Π½Π°Ρ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡΡ: O(N) Π΄Π»Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡ ΠΏΠΎΡΠ΅ΡΡΠ½Π½ΡΡ
ΠΊΠ»Π΅ΡΠΎΠΊ.
π§Ύ ΠΡΡ
ΠΎΠ΄Π½ΡΠΉ ΠΊΠΎΠ΄
use super::TaskData;
/// Represents the four cardinal directions the guard can face.
#[derive(Debug, Clone, Copy)]
pub enum Direction {
Up,
Right,
Down,
Left,
}
/// Represents the type of each cell in the grid.
#[derive(Debug, Clone, Copy)]
pub enum CellType {
Free,
Obstacle,
}
/// Encapsulates the guard's position and facing direction.
#[derive(Debug, Clone, Copy)]
pub struct Guard {
pub row: usize,
pub col: usize,
pub dir: Direction,
}
/// Solution module containing methods to solve both parts of the problem.
pub struct Solution;
impl Solution {
/// Solves Part I: Calculates the number of distinct positions the guard visits
/// before leaving the mapped area.
pub fn solve_simple(data: &TaskData) -> i32 {
let (moved_out, seen_dirs) =
Self::simulate_guard(data.guard.clone(), &data.field, (data.rows, data.cols));
if !moved_out {
panic!("Guard is stuck in a loop.");
}
Self::count_seen_cells(&seen_dirs) as i32
}
/// Solves Part II: Determines the number of positions where placing a new obstacle
/// would cause the guard to get stuck in a loop.
pub fn solve_hard(data: &TaskData) -> i32 {
let mut field = data.field.clone();
let (_, seen_dirs) =
Self::simulate_guard(data.guard.clone(), &field, (data.rows, data.cols));
(0..data.rows)
.flat_map(|r| (0..data.cols).map(move |c| (r, c)))
.filter(|&(r, c)| (r, c) != (data.guard.row, data.guard.col) && seen_dirs[r][c] != 0)
.filter_map(|(r, c)| {
field[r][c] = CellType::Obstacle;
let (moved_out, _) =
Self::simulate_guard(data.guard.clone(), &field, (data.rows, data.cols));
field[r][c] = CellType::Free;
(!moved_out).then_some((r,c))
}).count() as i32
}
/// Simulates the guard's movement until they either leave the grid or enter a loop.
fn simulate_guard(
mut guard: Guard,
field: &[Vec<CellType>],
limits: (usize, usize),
) -> (bool, Vec<Vec<i32>>) {
let (rows, cols) = limits;
let mut seen_dirs = vec![vec![0; cols]; rows];
while seen_dirs[guard.row][guard.col] & guard.dir.bitmask() == 0 {
// Mark the current cell as visited from the current direction.
seen_dirs[guard.row][guard.col] |= guard.dir.bitmask();
if !Self::step_guard(&mut guard, field, limits) {
return (true, seen_dirs); // Guard has moved out of bounds.
}
}
(false, seen_dirs) // Guard is stuck in a loop.
}
/// Attempts to move the guard forward. If an obstacle is ahead, the guard turns right.
fn step_guard(guard: &mut Guard, field: &[Vec<CellType>], limits: (usize, usize)) -> bool {
match guard.dir.next_cell(guard.row, guard.col, limits) {
Some((nr, nc)) => {
if matches!(field[nr][nc], CellType::Obstacle) {
guard.dir = guard.dir.turn_right();
} else {
guard.row = nr;
guard.col = nc;
}
true
}
None => false, // Guard would move out of bounds
}
}
/// Counts the number of distinct cells that the guard has visited.
fn count_seen_cells(seen_dirs: &[Vec<i32>]) -> usize {
seen_dirs.iter()
.flat_map(|row| row.iter())
.filter(|&&v| v != 0)
.count()
}
}
impl Direction {
/// Returns the direction obtained by turning right from the current direction.
fn turn_right(self) -> Self {
match self {
Self::Up => Self::Right,
Self::Right => Self::Down,
Self::Down => Self::Left,
Self::Left => Self::Up,
}
}
/// Calculates the next cell position based on the current direction.
fn next_cell(self, row: usize, col: usize, limits: (usize, usize)) -> Option<(usize, usize)> {
match self {
Self::Up if row > 0 => Some((row - 1, col)),
Self::Right if col + 1 < limits.1 => Some((row, col + 1)),
Self::Down if row + 1 < limits.0 => Some((row + 1, col)),
Self::Left if col > 0 => Some((row, col - 1)),
_ => None,
}
}
/// Returns a bitmask corresponding to the current direction.
fn bitmask(self) -> i32 {
match self {
Self::Up => 1,
Self::Right => 2,
Self::Down => 4,
Self::Left => 8,
}
}
}
Tags: #rust #algorithms #adventofcode #simulation