Solve 06
This commit is contained in:
parent
84dba789f8
commit
d21a6a6d97
1 changed files with 181 additions and 0 deletions
181
src/bin/06.rs
Normal file
181
src/bin/06.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use aoc::*;
|
||||
use glam::IVec2;
|
||||
use std::collections::HashMap;
|
||||
use std::iter;
|
||||
|
||||
const INPUT: &str = include_str!("../../input/06");
|
||||
|
||||
fn main() {
|
||||
assert_example!(part1, "06-test", 41);
|
||||
println!("Part 1: {}", part1(INPUT));
|
||||
assert_example!(part2, "06-test", 6);
|
||||
println!("Part 2: {}", part2(INPUT));
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> usize {
|
||||
Lab::parse(input).patrol().count_visited()
|
||||
}
|
||||
|
||||
fn part2(input: &str) -> usize {
|
||||
let lab = Lab::parse(input);
|
||||
|
||||
lab.clone()
|
||||
.patrol()
|
||||
.tiles
|
||||
.into_iter()
|
||||
.filter_map(|(pos, tile)| match tile {
|
||||
Tile::Visited => Some(pos),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|&modification| modification != lab.start)
|
||||
.filter(|&modification| {
|
||||
let mut lab = lab.clone();
|
||||
lab.place_wall(modification);
|
||||
lab.loops()
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
enum Tile {
|
||||
Start,
|
||||
Empty,
|
||||
Wall,
|
||||
Visited,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
fn parse(input: char) -> Self {
|
||||
match input {
|
||||
'^' => Tile::Start,
|
||||
'.' => Tile::Empty,
|
||||
'#' => Tile::Wall,
|
||||
other => panic!("unexpected tile '{other}'"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Lab {
|
||||
start: IVec2,
|
||||
tiles: HashMap<IVec2, Tile>,
|
||||
size: IVec2,
|
||||
}
|
||||
|
||||
impl Lab {
|
||||
fn patrol(mut self) -> Self {
|
||||
let mut pos = self.start;
|
||||
let mut dir = Direction::Up;
|
||||
|
||||
while self.contains(pos) {
|
||||
self.mark_visited(pos);
|
||||
(pos, dir) = self.advance(pos, dir);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// <https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare>
|
||||
fn loops(&self) -> bool {
|
||||
let (mut tortoise_pos, mut tortoise_dir) = (self.start, Direction::Up);
|
||||
let (mut hare_pos, mut hare_dir) = (self.start, Direction::Up);
|
||||
|
||||
loop {
|
||||
(tortoise_pos, tortoise_dir) = self.advance(tortoise_pos, tortoise_dir);
|
||||
(hare_pos, hare_dir) = self.advance(hare_pos, hare_dir);
|
||||
(hare_pos, hare_dir) = self.advance(hare_pos, hare_dir);
|
||||
|
||||
if !self.contains(hare_pos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if hare_pos == tortoise_pos && hare_dir == tortoise_dir {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&self, pos: IVec2, mut dir: Direction) -> (IVec2, Direction) {
|
||||
while self.looking_at_wall(pos, dir) {
|
||||
dir = dir.rotate();
|
||||
}
|
||||
(pos + dir.vec(), dir)
|
||||
}
|
||||
|
||||
fn place_wall(&mut self, pos: IVec2) {
|
||||
self.tiles.insert(pos, Tile::Wall);
|
||||
}
|
||||
|
||||
fn mark_visited(&mut self, pos: IVec2) {
|
||||
self.tiles.insert(pos, Tile::Visited);
|
||||
}
|
||||
|
||||
fn contains(&self, position: IVec2) -> bool {
|
||||
position.x <= self.size.x && position.y <= self.size.y && position.x >= 0 && position.y >= 0
|
||||
}
|
||||
|
||||
fn looking_at_wall(&self, position: IVec2, d: Direction) -> bool {
|
||||
let check = position + d.vec();
|
||||
self.tiles.get(&check) == Some(&Tile::Wall)
|
||||
}
|
||||
|
||||
fn count_visited(&self) -> usize {
|
||||
self.tiles
|
||||
.values()
|
||||
.filter(|x| matches!(x, Tile::Visited))
|
||||
.count()
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> Self {
|
||||
let tiles: HashMap<_, _> = input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| line.chars().enumerate().zip(iter::repeat(y)))
|
||||
.map(|((x, c), y)| {
|
||||
let pos = IVec2::new(x as i32, y as i32);
|
||||
let tile = Tile::parse(c);
|
||||
(pos, tile)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let start = *tiles
|
||||
.iter()
|
||||
.find(|(_, c)| matches!(c, Tile::Start))
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
let size = tiles.keys().fold(IVec2::new(0, 0), |a, b| {
|
||||
IVec2::new(a.x.max(b.x), a.y.max(b.y))
|
||||
});
|
||||
|
||||
Self { tiles, start, size }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
Left,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn rotate(self) -> Direction {
|
||||
match self {
|
||||
Direction::Up => Direction::Right,
|
||||
Direction::Right => Direction::Down,
|
||||
Direction::Down => Direction::Left,
|
||||
Direction::Left => Direction::Up,
|
||||
}
|
||||
}
|
||||
|
||||
fn vec(self) -> IVec2 {
|
||||
match self {
|
||||
Direction::Up => IVec2::new(0, -1),
|
||||
Direction::Right => IVec2::new(1, 0),
|
||||
Direction::Down => IVec2::new(0, 1),
|
||||
Direction::Left => IVec2::new(-1, 0),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue