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