This commit is contained in:
Quentin Boyer 2024-12-06 22:52:53 +01:00
parent 1e0bcb0c39
commit 40b926347b

196
src/bin/day6.rs Normal file
View file

@ -0,0 +1,196 @@
use std::{collections::HashSet, time::Instant};
use aoc_2024::{load, print_res};
use bstr::{BString, ByteSlice};
use color_eyre::eyre::ContextCompat;
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct Point {
line: isize,
col: isize,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Tile {
Blocked,
Free,
}
type Parsed = (Point, Vec<Vec<Tile>>);
#[inline(never)]
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
let mut guard = None;
let mut lines = Vec::new();
for (l, line) in input.lines().enumerate() {
lines.push(
line.iter()
.enumerate()
.map(|(col, &c)| match c {
b'#' => Ok(Tile::Blocked),
b'.' => Ok(Tile::Free),
b'^' => {
color_eyre::eyre::ensure!(guard.is_none(), "Found two guards");
guard = Some(Point {
line: l as isize,
col: col as isize,
});
Ok(Tile::Free)
}
_ => Err(color_eyre::eyre::eyre!("Invalid char: `{c}`")),
})
.collect::<Result<_, _>>()?,
);
}
Ok((guard.with_context(|| "No guard found")?, lines))
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
enum Direction {
Up,
Right,
Down,
Left,
}
impl Direction {
pub fn turn_right(self) -> Self {
match self {
Self::Up => Self::Right,
Self::Right => Self::Down,
Self::Down => Self::Left,
Self::Left => Self::Up,
}
}
}
impl Point {
fn walk(self, direction: Direction) -> Self {
let Self { line, col } = self;
match direction {
Direction::Up => Self {
line: line - 1,
col,
},
Direction::Right => Self { col: col + 1, line },
Direction::Down => Self {
line: line + 1,
col,
},
Direction::Left => Self { col: col - 1, line },
}
}
}
fn visited_tiles(mut guard: Point, grid: &[Vec<Tile>]) -> HashSet<Point> {
let mut direction = Direction::Up;
let mut visited = HashSet::new();
loop {
visited.insert(guard);
let next = guard.walk(direction);
if next.line < 0
|| next.line as usize >= grid.len()
|| next.col < 0
|| next.col as usize >= grid[0].len()
{
break;
}
let tile = grid[next.line as usize][next.col as usize];
match tile {
Tile::Blocked => {
direction = direction.turn_right();
}
Tile::Free => {
guard = next;
}
}
}
visited
}
#[inline(never)]
pub fn part1((guard, grid): Parsed) {
let visited = visited_tiles(guard, &grid);
print_res!("Positions visited: {}", visited.len());
}
#[inline(never)]
pub fn part2((guard, mut grid): Parsed) {
let mut possible_blocks = 0;
let visited_no_blocks = visited_tiles(guard, &grid);
for line in 0..grid.len() {
for col in 0..grid[0].len() {
if grid[line][col] == Tile::Blocked
|| !visited_no_blocks.contains(&Point {
line: line as isize,
col: col as isize,
})
{
continue;
}
grid[line][col] = Tile::Blocked;
let mut guard = guard;
let mut direction = Direction::Up;
let mut visited = HashSet::new();
loop {
if visited.contains(&(guard, direction)) {
possible_blocks += 1;
break;
}
visited.insert((guard, direction));
let next = guard.walk(direction);
if next.line < 0
|| next.line as usize >= grid.len()
|| next.col < 0
|| next.col as usize >= grid[0].len()
{
break;
}
let tile = grid[next.line as usize][next.col as usize];
match tile {
Tile::Blocked => {
direction = direction.turn_right();
}
Tile::Free => {
guard = next;
}
}
}
grid[line][col] = Tile::Free;
}
}
print_res!("Possible blocks to loop: {possible_blocks}");
}
pub fn main() -> color_eyre::Result<()> {
let context = load()?;
let start = Instant::now();
let parsed = parsing(&context.input)?;
let elapsed = humantime::format_duration(start.elapsed());
let start = Instant::now();
if context.part == 1 {
part1(parsed);
} else {
part2(parsed);
}
let elapsed_part = humantime::format_duration(start.elapsed());
println!(" Parsing: {elapsed}");
println!(" Solving: {elapsed_part}");
Ok(())
}