Files
contests/advent_of_code/2024/6/src/main.rs

189 lines
4.2 KiB
Rust

use std::{collections::HashSet, time::Instant};
use nom::{
branch::alt,
character::complete::{char, multispace1},
multi::{many1, separated_list1},
IResult, Parser,
};
#[derive(Debug, PartialEq, Eq)]
enum Tile {
Start,
Empty,
Wall,
Passed,
}
fn parse(input: &str) -> IResult<&str, Vec<Vec<Tile>>> {
let (input, result) = separated_list1(
multispace1,
many1(alt((char('.'), char('#'), char('^'))).map(|c| match c {
'.' => Tile::Empty,
'#' => Tile::Wall,
'^' => Tile::Start,
_ => unreachable!(),
})),
)(input)?;
Ok((input, result))
}
fn proc(
height: usize,
width: usize,
dir: &(i32, i32),
pos: &(usize, usize),
) -> Option<(usize, usize)> {
let x = pos.0 as i32 + dir.0;
let y = pos.1 as i32 + dir.1;
if x < 0 || y < 0 || x >= width as i32 || y >= height as i32 {
None
} else {
Some((x as usize, y as usize))
}
}
fn turn(dir: (i32, i32)) -> (i32, i32) {
match dir {
(0, -1) => (1, 0),
(1, 0) => (0, 1),
(0, 1) => (-1, 0),
(-1, 0) => (0, -1),
_ => unreachable!(),
}
}
fn solve_1(input: &str) -> usize {
use Tile::*;
let (_, mut tiles) = parse(input).unwrap();
let mut pos = (0, 0);
for (y, row_of_tiles) in tiles.iter().enumerate() {
for (x, tile) in row_of_tiles.iter().enumerate() {
if let Start = tile {
pos = (x, y);
break;
}
}
}
let h = tiles.len();
let w = tiles[0].len();
let mut dir = (0, -1);
loop {
tiles[pos.1][pos.0] = Passed;
match proc(h, w, &dir, &pos) {
None => break,
Some(mut p) => {
while tiles[p.1][p.0] == Wall {
dir = turn(dir);
p = proc(h, w, &dir, &pos).unwrap();
}
pos = p;
}
}
}
tiles.into_iter().fold(0, |sum, row_of_tiles| {
sum + row_of_tiles
.into_iter()
.filter(|tile| *tile == Passed)
.count()
})
}
fn solve_2(input: &str) -> usize {
use Tile::*;
let (_, mut tiles) = parse(input).unwrap();
let mut pos = (0, 0);
for (y, row_of_tiles) in tiles.iter().enumerate() {
for (x, tile) in row_of_tiles.iter().enumerate() {
if let Start = tile {
pos = (x, y);
break;
}
}
}
let start = pos;
let h = tiles.len();
let w = tiles[0].len();
let mut result = 0;
for y in 0..h {
for x in 0..w {
match tiles[y][x] {
Wall => continue,
Start => continue,
_ => tiles[y][x] = Wall,
}
pos = start;
let mut visited = HashSet::new();
let mut dir = (0, -1);
loop {
if visited.contains(&(pos, dir)) {
result += 1;
break;
}
visited.insert((pos, dir));
match proc(h, w, &dir, &pos) {
None => break,
Some(mut p) => {
while tiles[p.1][p.0] == Wall {
dir = turn(dir);
p = proc(h, w, &dir, &pos).unwrap();
}
pos = p;
}
}
}
tiles[y][x] = Empty;
}
}
result
}
fn main() {
println!("Hello, this is Patrick!");
let now = Instant::now();
let input = include_str!("../input.txt");
let result_1 = solve_1(input);
println!("The number of walked tiles is {}", result_1);
let result_2 = solve_2(input);
println!("The number of possible obstructions is {}", result_2);
println!("Time passed: {:?}", Instant::now() - now);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1() {
let test_input = include_str!("../test.txt");
assert_eq!(solve_1(test_input), 41);
}
#[test]
fn test_2() {
let test_input = include_str!("../test.txt");
assert_eq!(solve_2(test_input), 6);
}
}