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