130 lines
3.3 KiB
Rust
130 lines
3.3 KiB
Rust
use rayon::prelude::*;
|
|
|
|
pub fn day6() {
|
|
let (world, start) = World::from_string(&crate::input(6));
|
|
|
|
let (mut path, _) = traverse(start, &world);
|
|
|
|
path.sort_by_key(|t| (t.x, t.y));
|
|
path.dedup_by_key(|t| (t.x, t.y));
|
|
let total_traveled = path.len();
|
|
|
|
let loops = path
|
|
.par_iter()
|
|
.filter(|p| {
|
|
let mut new_world = world.clone();
|
|
new_world.map[p.y][p.x] = Terrain::Blocked;
|
|
let (_, is_loop) = traverse(start, &new_world);
|
|
is_loop
|
|
})
|
|
.count();
|
|
|
|
println!("Total Travelled: {}", total_traveled);
|
|
println!("Total Loopable Positions: {}", loops);
|
|
}
|
|
|
|
fn traverse(start: Transform, world: &World) -> (Vec<Transform>, bool) {
|
|
let mut positions = vec![start];
|
|
let mut current = start;
|
|
|
|
while let Some(next) = current.travel(world) {
|
|
if positions.contains(&next) {
|
|
return (positions, true);
|
|
}
|
|
positions.push(next);
|
|
current = next;
|
|
}
|
|
(positions, false)
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
|
struct Transform {
|
|
x: usize,
|
|
y: usize,
|
|
direction: Direction,
|
|
}
|
|
|
|
impl Transform {
|
|
fn new(x: usize, y: usize, direction: Direction) -> Self {
|
|
Self { x, y, direction }
|
|
}
|
|
|
|
fn turn(&self) -> Self {
|
|
let direction = match self.direction {
|
|
Direction::North => Direction::East,
|
|
Direction::East => Direction::South,
|
|
Direction::South => Direction::West,
|
|
Direction::West => Direction::North,
|
|
};
|
|
Self::new(self.x, self.y, direction)
|
|
}
|
|
|
|
fn travel(&self, world: &World) -> Option<Self> {
|
|
let (dx, dy) = match self.direction {
|
|
Direction::North => (0, -1),
|
|
Direction::South => (0, 1),
|
|
Direction::East => (1, 0),
|
|
Direction::West => (-1, 0),
|
|
};
|
|
|
|
let new_x = (self.x as i32 + dx).try_into().ok()?;
|
|
let new_y = (self.y as i32 + dy).try_into().ok()?;
|
|
let next = Self::new(new_x, new_y, self.direction);
|
|
|
|
match world.terrain_at(&next) {
|
|
Some(Terrain::Blocked) => Some(self.turn()),
|
|
Some(Terrain::Free) => Some(next),
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct World {
|
|
map: Vec<Vec<Terrain>>,
|
|
}
|
|
|
|
impl World {
|
|
fn from_string(input: &str) -> (Self, Transform) {
|
|
let mut start = Transform::new(0, 0, Direction::North);
|
|
let map = input
|
|
.lines()
|
|
.enumerate()
|
|
.map(|(y, line)| {
|
|
if let Some(x) = line.find('^') {
|
|
start = Transform::new(x, y, Direction::North);
|
|
}
|
|
line.chars()
|
|
.map(|c| {
|
|
if c == '#' {
|
|
Terrain::Blocked
|
|
} else {
|
|
Terrain::Free
|
|
}
|
|
})
|
|
.collect()
|
|
})
|
|
.collect();
|
|
|
|
(Self { map }, start)
|
|
}
|
|
|
|
fn terrain_at(&self, at: &Transform) -> Option<Terrain> {
|
|
self.map.get(at.y)?.get(at.x).copied()
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
|
enum Terrain {
|
|
Free,
|
|
Blocked,
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
|
enum Direction {
|
|
North,
|
|
South,
|
|
East,
|
|
West,
|
|
}
|