189 lines
4.2 KiB
Rust
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);
|
|
}
|
|
}
|