day 6
This commit is contained in:
parent
32712845e4
commit
8ae0017ca0
7
day-06/Cargo.lock
generated
Normal file
7
day-06/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day-06"
|
||||||
|
version = "0.1.0"
|
6
day-06/Cargo.toml
Normal file
6
day-06/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "day-06"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
264
day-06/src/main.rs
Normal file
264
day-06/src/main.rs
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
const INPUT: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/input.txt"));
|
||||||
|
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
enum Space {
|
||||||
|
Obstacle,
|
||||||
|
PlacedObstacle,
|
||||||
|
Empty,
|
||||||
|
Player,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
fn rotate(&mut self) {
|
||||||
|
*self = match *self {
|
||||||
|
Direction::Up => Direction::Right,
|
||||||
|
Direction::Right => Direction::Down,
|
||||||
|
Direction::Down => Direction::Left,
|
||||||
|
Direction::Left => Direction::Up,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default,Clone, Copy)]
|
||||||
|
struct VisitSet(u8);
|
||||||
|
|
||||||
|
impl VisitSet {
|
||||||
|
fn set(&mut self, dir: Direction) {
|
||||||
|
self.0 |= match dir {
|
||||||
|
Direction::Up => 0b00000001,
|
||||||
|
Direction::Down => 0b00000010,
|
||||||
|
Direction::Left => 0b00000100,
|
||||||
|
Direction::Right => 0b00001000,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_set(&self, dir: Direction) -> bool {
|
||||||
|
self.0 & (match dir {
|
||||||
|
Direction::Up => 0b00000001,
|
||||||
|
Direction::Down => 0b00000010,
|
||||||
|
Direction::Left => 0b00000100,
|
||||||
|
Direction::Right => 0b00001000,
|
||||||
|
}) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visited(&self) -> bool {
|
||||||
|
self.0 != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Grid {
|
||||||
|
data: Vec<(Space, VisitSet)>,
|
||||||
|
cols: usize,
|
||||||
|
curr: (usize, usize),
|
||||||
|
dir: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Index<usize> for Grid {
|
||||||
|
type Output = [(Space, VisitSet)];
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.data[self.cols * index..][..self.cols]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<usize> for Grid {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.data[self.cols * index..][..self.cols]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy,PartialEq, Eq, Debug)]
|
||||||
|
enum Walk {
|
||||||
|
Exit,
|
||||||
|
Continue,
|
||||||
|
Turn,
|
||||||
|
Loop,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grid {
|
||||||
|
fn rows(&self) -> usize {
|
||||||
|
self.data.len() / self.cols
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit(&mut self, dir: Direction) {
|
||||||
|
let row = self.curr.0;
|
||||||
|
let col = self.curr.1;
|
||||||
|
|
||||||
|
let (_, visited) = &mut self[row][col];
|
||||||
|
visited.set(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk(&mut self) -> Walk {
|
||||||
|
let (row, col) = self.curr;
|
||||||
|
|
||||||
|
assert_eq!(self[row][col].0, Space::Player);
|
||||||
|
|
||||||
|
// Use the current position and direction to find the new position
|
||||||
|
let new: Option<(usize, usize)> = match self.dir {
|
||||||
|
Direction::Up => if row != 0 {
|
||||||
|
Some((row - 1, col))
|
||||||
|
} else { None },
|
||||||
|
Direction::Down => if row != self.rows() - 1 {
|
||||||
|
Some((row + 1, col))
|
||||||
|
} else { None },
|
||||||
|
Direction::Left => if col != 0 {
|
||||||
|
Some((row, col - 1))
|
||||||
|
} else { None },
|
||||||
|
Direction::Right => if col != self.cols - 1 {
|
||||||
|
Some((row, col + 1))
|
||||||
|
} else { None },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mark the current location as visited
|
||||||
|
self.visit(self.dir);
|
||||||
|
|
||||||
|
match new {
|
||||||
|
Some((new_row, new_col)) =>
|
||||||
|
// New space would be over an obstacle
|
||||||
|
if matches!(self[new_row][new_col].0, Space::Obstacle | Space::PlacedObstacle) {
|
||||||
|
self.dir.rotate();
|
||||||
|
Walk::Turn
|
||||||
|
}
|
||||||
|
// New space is empty and ready to be moved to
|
||||||
|
else {
|
||||||
|
let new_space = self[new_row][new_col];
|
||||||
|
|
||||||
|
// Update the position
|
||||||
|
self.curr = (new_row, new_col);
|
||||||
|
// Set the guard to be on the new space
|
||||||
|
self[new_row][new_col].0 = Space::Player;
|
||||||
|
// And remove them from the old space
|
||||||
|
self[row][col].0 = Space::Empty;
|
||||||
|
|
||||||
|
// If the new space has already been visited in the same direction
|
||||||
|
// then we have detected a cycle
|
||||||
|
if new_space.1.is_set(self.dir) {
|
||||||
|
Walk::Loop
|
||||||
|
}
|
||||||
|
// Else we continue walking
|
||||||
|
else {
|
||||||
|
Walk::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
Walk::Exit
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
// For debugging purposes
|
||||||
|
// Designed to match the output from the instructions
|
||||||
|
fn display(&self) {
|
||||||
|
for i in 0..self.rows() {
|
||||||
|
for j in 0..self.cols {
|
||||||
|
let (space, visited) = self[i][j];
|
||||||
|
print!("{}", match space {
|
||||||
|
Space::Obstacle => '#',
|
||||||
|
Space::PlacedObstacle => 'O',
|
||||||
|
Space::Player => match self.dir {
|
||||||
|
Direction::Up => '^',
|
||||||
|
Direction::Down => 'v',
|
||||||
|
Direction::Right => '>',
|
||||||
|
Direction::Left => '<',
|
||||||
|
},
|
||||||
|
Space::Empty => if visited.visited() {
|
||||||
|
let up_down = visited.is_set(Direction::Up) || visited.is_set(Direction::Down);
|
||||||
|
let left_right = visited.is_set(Direction::Left) || visited.is_set(Direction::Right);
|
||||||
|
|
||||||
|
if up_down && left_right { '+' }
|
||||||
|
else if up_down { '|' }
|
||||||
|
else if left_right { '-' }
|
||||||
|
else { unreachable!() }
|
||||||
|
} else { '.' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let cols = INPUT.find('\n').unwrap();
|
||||||
|
|
||||||
|
let data = INPUT.lines()
|
||||||
|
.map(|line| {
|
||||||
|
line.chars().map(|char|
|
||||||
|
match char {
|
||||||
|
'.' => Space::Empty,
|
||||||
|
'#' => Space::Obstacle,
|
||||||
|
'^' => Space::Player,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
).map(|space| {
|
||||||
|
(space, VisitSet::default())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let start = data.iter().copied().position(|x| x.0 == Space::Player).unwrap();
|
||||||
|
|
||||||
|
let grid = Grid { data, cols, curr: (start / cols, start % cols), dir: Direction::Up };
|
||||||
|
|
||||||
|
// Possible candidates for blockages in part 2
|
||||||
|
// since we only need to consider spaces ever reached
|
||||||
|
// in part 1.
|
||||||
|
let mut candidates = BTreeSet::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut grid = grid.clone();
|
||||||
|
loop {
|
||||||
|
let walk = grid.walk();
|
||||||
|
|
||||||
|
candidates.insert(grid.curr);
|
||||||
|
|
||||||
|
if walk == Walk::Exit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let visited = grid.data.iter().copied().filter(|x| x.1.visited()).count();
|
||||||
|
|
||||||
|
println!("Part 1: {}", visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut total = 0;
|
||||||
|
|
||||||
|
for (i, j) in candidates {
|
||||||
|
if !grid[i][j].1.visited() && grid[i][j].0 == Space::Empty {
|
||||||
|
|
||||||
|
let mut grid = grid.clone();
|
||||||
|
|
||||||
|
grid[i][j].0 = Space::PlacedObstacle;
|
||||||
|
|
||||||
|
let looped = loop {
|
||||||
|
let walk = grid.walk();
|
||||||
|
|
||||||
|
if walk == Walk::Exit {
|
||||||
|
break false
|
||||||
|
} else if walk == Walk::Loop {
|
||||||
|
break true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
total += u32::from(looped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Part 2: {}", total);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user