Day 6: Guard Gallivant
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
You are viewing a single thread.
View all comments 3 points
*
Rust
Only part 1 because I’m meant to be leaving for a holiday in a few hours and haven’t packed yet. Part two looks simple enough to add:
part 2 plan
Change seen positions set to include direction, if pos+dir already seen then it’s a loop. Test all spaces.
Edit: I did the change on my phone (which was painful).
use std::{collections::HashSet, fs, str::FromStr};
use color_eyre::eyre::{Report, Result};
type GridPosition = usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Direction {
N,
E,
S,
W,
}
impl Direction {
fn clockwise(&self) -> Self {
match self {
Direction::N => Direction::E,
Direction::E => Direction::S,
Direction::S => Direction::W,
Direction::W => Direction::N,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Thing {
Guard(Direction),
Obstruction,
Space,
}
#[derive(Debug, PartialEq, Eq)]
struct LabMap {
grid: Vec<Thing>,
width: usize,
height: usize,
}
impl FromStr for LabMap {
type Err = Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let grid = s
.chars()
.filter_map(|ch| {
use Thing::*;
match ch {
'^' => Some(Guard(Direction::N)),
'>' => Some(Guard(Direction::E)),
'v' => Some(Guard(Direction::S)),
'<' => Some(Guard(Direction::W)),
'#' => Some(Obstruction),
'.' => Some(Space),
'\n' => None,
_ => unreachable!(),
}
})
.collect::<Vec<_>>();
let width = s
.chars()
.position(|ch| ch == '\n')
.ok_or_else(|| Report::msg("grid width cannot be zero, or one line"))?;
let height = grid.len() / width;
Ok(Self {
grid,
width,
height,
})
}
}
impl LabMap {
fn neighbour(&self, i: GridPosition, dir: Direction) -> Option<GridPosition> {
let width = self.width;
let length = self.grid.len();
use Direction::*;
match dir {
N if i >= width => Some(i - width),
E if i % width != width - 1 => Some(i + 1),
S if i + width < length => Some(i + width),
W if i % width != 0 => Some(i - 1),
_ => None,
}
}
fn guard_pos(&self) -> Option<(GridPosition, Direction)> {
self.grid
.iter()
.enumerate()
.filter_map(|(pos, &thing)| match thing {
Thing::Guard(dir) => Some((pos, dir)),
_ => None,
})
.next()
}
fn path_len(&self) -> usize {
let mut positions = HashSet::new();
let mut next = self.guard_pos();
while let Some((pos, dir)) = next {
positions.insert(pos);
next = self.neighbour(pos, dir).map(|npos| match self.grid[npos] {
Thing::Space | Thing::Guard(_) => (npos, dir),
Thing::Obstruction => (pos, dir.clockwise()),
});
}
positions.len()
}
fn num_loops(&self) -> usize {
(0..self.grid.len())
.filter(|&pos| matches!(self.grid[pos], Thing::Space))
.map(|pos| {
let mut grid = self.grid.clone();
grid[pos] = Thing::Obstruction;
LabMap {
grid,
width: self.width,
height: self.height,
}
})
.filter(LabMap::is_loop)
.count()
}
fn is_loop(&self) -> bool {
let mut positions = HashSet::new();
let mut next = self.guard_pos();
while let Some((pos, dir)) = next {
let is_new = positions.insert((pos, dir));
if !is_new {
return true;
}
next = self.neighbour(pos, dir).map(|npos| match self.grid[npos] {
Thing::Space | Thing::Guard(_) => (npos, dir),
Thing::Obstruction => (pos, dir.clockwise()),
});
}
false
}
}
fn part1(filepath: &str) -> Result<usize> {
let input = fs::read_to_string(filepath)?;
let map = LabMap::from_str(&input)?;
Ok(map.path_len())
}
fn part2(filepath: &str) -> Result<usize> {
let input = fs::read_to_string(filepath)?;
let map = LabMap::from_str(&input)?;
Ok(map.num_loops())
}
fn main() -> Result<()> {
color_eyre::install()?;
println!("Part 1: {}", part1("input.txt")?);
println!("Part 2: {}", part2("input.txt")?);
Ok(())
}