Day 6
This commit is contained in:
parent
1e0bcb0c39
commit
40b926347b
1 changed files with 196 additions and 0 deletions
196
src/bin/day6.rs
Normal file
196
src/bin/day6.rs
Normal 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(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue