2024-12-06 16:54:34 +00:00
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
|
|
|
use aoc_runner_derive::{aoc, aoc_generator};
|
|
|
|
|
2024-12-06 18:00:58 +00:00
|
|
|
// type Input = ((usize, usize), Vec<Vec<u8>>);
|
|
|
|
type Input = ((usize, usize), Vec<Vec<u8>>, Vec<Vec<[u8; 5]>>);
|
2024-12-06 06:46:04 +00:00
|
|
|
|
2024-12-06 16:54:34 +00:00
|
|
|
const UP: u8 = 0;
|
|
|
|
const DOWN: u8 = 1;
|
|
|
|
const LEFT: u8 = 2;
|
|
|
|
const RIGHT: u8 = 4;
|
|
|
|
const EMPTY: u8 = 8;
|
|
|
|
const WALL: u8 = 16;
|
|
|
|
const PASSED: u8 = 32;
|
|
|
|
|
|
|
|
const DIR: [(isize, isize); 5] = [(0, -1), (0, 1), (-1, 0), (0, 0), (1, 0)];
|
|
|
|
const NEXT_DIR: [usize; 5] = [RIGHT as usize, LEFT as usize, UP as usize, 0, DOWN as usize];
|
2024-12-06 14:46:53 +00:00
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
#[aoc_generator(day6)]
|
|
|
|
fn parse(input: &str) -> Input {
|
|
|
|
let lines = input.split("\n");
|
|
|
|
let mut pos = (0, 0);
|
2024-12-06 16:54:34 +00:00
|
|
|
let mut stones = VecDeque::new();
|
|
|
|
|
|
|
|
let map: Vec<Vec<u8>> = lines
|
2024-12-06 06:46:04 +00:00
|
|
|
.enumerate()
|
|
|
|
.map(|(y, line)| {
|
|
|
|
line.chars()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(x, c)| match c {
|
2024-12-06 16:54:34 +00:00
|
|
|
'.' => EMPTY,
|
|
|
|
'#' => {
|
|
|
|
stones.push_back((x, y));
|
|
|
|
WALL
|
|
|
|
}
|
2024-12-06 06:46:04 +00:00
|
|
|
'^' => {
|
|
|
|
pos = (x, y);
|
2024-12-06 16:54:34 +00:00
|
|
|
EMPTY
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
|
|
|
_ => todo!(),
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
})
|
|
|
|
.collect();
|
2024-12-06 16:54:34 +00:00
|
|
|
|
2024-12-06 18:00:58 +00:00
|
|
|
let mut jumps = vec![vec![[u8::MAX; 5]; map[0].len()]; map.len()];
|
|
|
|
|
|
|
|
while let Some((x, y)) = stones.pop_front() {
|
|
|
|
for dir in NEXT_DIR {
|
|
|
|
let mut d = 1;
|
|
|
|
loop {
|
|
|
|
let pos = (x as isize - d * DIR[dir].0, y as isize - d * DIR[dir].1);
|
|
|
|
if pos.0 < 0
|
|
|
|
|| pos.0 >= map[0].len() as isize
|
|
|
|
|| pos.1 < 0
|
|
|
|
|| pos.1 >= map.len() as isize
|
|
|
|
|| map[pos.1 as usize][pos.0 as usize] == WALL
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
jumps[pos.1 as usize][pos.0 as usize][dir] = (d - 1) as u8;
|
|
|
|
|
|
|
|
d += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(pos, map, jumps)
|
|
|
|
// (pos, map)
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[aoc(day6, part1)]
|
|
|
|
fn part1(input: &Input) -> u64 {
|
|
|
|
let h = input.1.len();
|
|
|
|
let w = input.1[0].len();
|
2024-12-06 16:54:34 +00:00
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
let mut map = input.1.clone();
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
let mut pos = ((input.0).0 as isize, (input.0).1 as isize);
|
|
|
|
let mut dir = UP as usize;
|
2024-12-06 06:46:04 +00:00
|
|
|
let mut count = 0;
|
2024-12-06 16:54:34 +00:00
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
loop {
|
2024-12-06 16:54:34 +00:00
|
|
|
if map[pos.1 as usize][pos.0 as usize] == EMPTY {
|
2024-12-06 06:46:04 +00:00
|
|
|
count += 1;
|
2024-12-06 16:54:34 +00:00
|
|
|
map[pos.1 as usize][pos.0 as usize] = PASSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if pos.0 + DIR[dir].0 < 0
|
|
|
|
|| pos.0 + DIR[dir].0 >= w as isize
|
|
|
|
|| pos.1 + DIR[dir].1 < 0
|
|
|
|
|| pos.1 + DIR[dir].1 >= h as isize
|
2024-12-06 06:46:04 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
|
|
|
|
dir = NEXT_DIR[dir];
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1);
|
|
|
|
pos = next_pos;
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
count
|
|
|
|
}
|
|
|
|
|
|
|
|
#[aoc(day6, part2)]
|
|
|
|
fn part2(input: &Input) -> u64 {
|
|
|
|
let h = input.1.len();
|
|
|
|
let w = input.1[0].len();
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
let initial = ((input.0).0 as isize, (input.0).1 as isize);
|
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
let mut map = input.1.clone();
|
2024-12-06 18:00:58 +00:00
|
|
|
let jumps = &input.2;
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
let mut pos = initial;
|
|
|
|
let mut dir = UP as usize;
|
|
|
|
let mut count = 0;
|
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
loop {
|
2024-12-06 16:54:34 +00:00
|
|
|
if pos.0 + DIR[dir].0 < 0
|
|
|
|
|| pos.0 + DIR[dir].0 >= w as isize
|
|
|
|
|| pos.1 + DIR[dir].1 < 0
|
|
|
|
|| pos.1 + DIR[dir].1 >= h as isize
|
2024-12-06 06:46:04 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
|
|
|
|
dir = NEXT_DIR[dir];
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1);
|
|
|
|
|
|
|
|
if next_pos != initial && map[next_pos.1 as usize][next_pos.0 as usize] == EMPTY {
|
|
|
|
map[next_pos.1 as usize][next_pos.0 as usize] = WALL;
|
2024-12-06 18:00:58 +00:00
|
|
|
if check_loop(pos, next_pos, dir, &mut map, jumps) {
|
2024-12-06 16:54:34 +00:00
|
|
|
count += 1;
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
map[next_pos.1 as usize][next_pos.0 as usize] = PASSED;
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
pos = next_pos;
|
|
|
|
}
|
|
|
|
|
2024-12-06 16:54:34 +00:00
|
|
|
count
|
|
|
|
}
|
2024-12-06 06:46:04 +00:00
|
|
|
|
2024-12-06 18:00:58 +00:00
|
|
|
fn check_loop(
|
|
|
|
mut pos: (isize, isize),
|
|
|
|
stone: (isize, isize),
|
|
|
|
mut dir: usize,
|
|
|
|
map: &mut [Vec<u8>],
|
|
|
|
jumps: &[Vec<[u8; 5]>],
|
|
|
|
) -> bool {
|
2024-12-06 06:46:04 +00:00
|
|
|
let h = map.len();
|
|
|
|
let w = map[0].len();
|
|
|
|
|
2024-12-06 16:54:34 +00:00
|
|
|
let mut changed = Vec::with_capacity(w * h * 4);
|
2024-12-06 06:46:04 +00:00
|
|
|
|
|
|
|
loop {
|
2024-12-06 16:54:34 +00:00
|
|
|
if pos.0 + DIR[dir].0 < 0
|
|
|
|
|| pos.0 + DIR[dir].0 >= w as isize
|
|
|
|
|| pos.1 + DIR[dir].1 < 0
|
|
|
|
|| pos.1 + DIR[dir].1 >= h as isize
|
2024-12-06 06:46:04 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-12-06 16:54:34 +00:00
|
|
|
let mut turn = false;
|
|
|
|
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
|
|
|
|
dir = NEXT_DIR[dir];
|
|
|
|
turn = true;
|
2024-12-06 14:46:53 +00:00
|
|
|
}
|
2024-12-06 16:54:34 +00:00
|
|
|
|
2024-12-06 18:00:58 +00:00
|
|
|
let next_pos = if (pos.0 == stone.0) || (pos.1 == stone.1) {
|
|
|
|
(pos.0 + DIR[dir].0, pos.1 + DIR[dir].1)
|
|
|
|
} else {
|
|
|
|
let d = jumps[pos.1 as usize][pos.0 as usize][dir] as isize;
|
|
|
|
if d == u8::MAX as isize {
|
|
|
|
changed.into_iter().for_each(|pos: (isize, isize)| {
|
|
|
|
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
(pos.0 + d * DIR[dir].0, pos.1 + d * DIR[dir].1)
|
|
|
|
};
|
2024-12-06 16:54:34 +00:00
|
|
|
|
|
|
|
if turn {
|
|
|
|
if map[next_pos.1 as usize][next_pos.0 as usize] & dir as u8 != 0 {
|
|
|
|
changed.into_iter().for_each(|pos: (isize, isize)| {
|
2024-12-06 18:00:58 +00:00
|
|
|
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
|
2024-12-06 16:54:34 +00:00
|
|
|
});
|
|
|
|
|
2024-12-06 14:46:53 +00:00
|
|
|
return true;
|
2024-12-06 16:54:34 +00:00
|
|
|
} else {
|
|
|
|
map[next_pos.1 as usize][next_pos.0 as usize] |= dir as u8;
|
|
|
|
changed.push(next_pos);
|
2024-12-06 14:46:53 +00:00
|
|
|
}
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
|
|
|
|
2024-12-06 16:54:34 +00:00
|
|
|
pos = next_pos;
|
2024-12-06 14:46:53 +00:00
|
|
|
}
|
|
|
|
|
2024-12-06 16:54:34 +00:00
|
|
|
changed.into_iter().for_each(|pos: (isize, isize)| {
|
2024-12-06 18:00:58 +00:00
|
|
|
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
|
2024-12-06 16:54:34 +00:00
|
|
|
});
|
|
|
|
|
2024-12-06 06:46:04 +00:00
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part1_example() {
|
|
|
|
assert_eq!(part1(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 41);
|
2024-12-06 16:54:34 +00:00
|
|
|
assert_eq!(part1(&parse(include_str!("../input/2024/day6.txt"))), 4977);
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2_example() {
|
|
|
|
assert_eq!(part2(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 6);
|
2024-12-06 16:54:34 +00:00
|
|
|
assert_eq!(part2(&parse(include_str!("../input/2024/day6.txt"))), 1729);
|
2024-12-06 06:46:04 +00:00
|
|
|
}
|
|
|
|
}
|