Day 6 (refactor)
This commit is contained in:
parent
7a0c38afb0
commit
8036ad4d58
3 changed files with 127 additions and 127 deletions
201
src/day6.rs
201
src/day6.rs
|
|
@ -1,33 +1,21 @@
|
|||
use rayon::prelude::*;
|
||||
|
||||
pub fn day6() {
|
||||
let input = crate::input(6);
|
||||
let (world, start) = World::from_string(&crate::input(6));
|
||||
|
||||
let (world, cursor_location) = World::from_string(&input);
|
||||
let start = Transform::new(
|
||||
cursor_location.0,
|
||||
cursor_location.1,
|
||||
Direction::North,
|
||||
&world,
|
||||
);
|
||||
let (mut path, _) = traverse(start, &world);
|
||||
|
||||
let mut path = traverse(start).unwrap();
|
||||
|
||||
path.sort_by(|a, b| {
|
||||
if a.x == b.x {
|
||||
a.y.cmp(&b.y)
|
||||
} else {
|
||||
a.x.cmp(&b.x)
|
||||
}
|
||||
});
|
||||
path.dedup_by(|a, b| a.x == b.x && a.y == b.y);
|
||||
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
|
||||
.iter()
|
||||
.par_iter()
|
||||
.filter(|p| {
|
||||
let mut new_world = world.clone();
|
||||
new_world.map[p.y][p.x] = Terrain::Blocked;
|
||||
let start = Transform::new(start.x, start.y, start.direction, &new_world);
|
||||
traverse(start).is_none()
|
||||
let (_, is_loop) = traverse(start, &new_world);
|
||||
is_loop
|
||||
})
|
||||
.count();
|
||||
|
||||
|
|
@ -35,69 +23,58 @@ pub fn day6() {
|
|||
println!("Total Loopable Positions: {}", loops);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Transform<'a> {
|
||||
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,
|
||||
world: &'a World,
|
||||
}
|
||||
|
||||
impl<'a> Transform<'a> {
|
||||
fn new(x: usize, y: usize, direction: Direction, world: &'a World) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
direction,
|
||||
world,
|
||||
}
|
||||
impl Transform {
|
||||
fn new(x: usize, y: usize, direction: Direction) -> Self {
|
||||
Self { x, y, direction }
|
||||
}
|
||||
|
||||
fn turn(&self) -> Self {
|
||||
Self {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
direction: self.direction.turn(),
|
||||
world: self.world,
|
||||
}
|
||||
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) -> Option<Self> {
|
||||
let t = match self.direction {
|
||||
Direction::North => Some(Self {
|
||||
x: self.x,
|
||||
y: self.y.checked_sub(1)?,
|
||||
direction: self.direction,
|
||||
world: self.world,
|
||||
}),
|
||||
Direction::South => Some(Self {
|
||||
x: self.x,
|
||||
y: self.y.checked_add(1)?,
|
||||
direction: self.direction,
|
||||
world: self.world,
|
||||
}),
|
||||
Direction::East => Some(Self {
|
||||
x: self.x.checked_add(1)?,
|
||||
y: self.y,
|
||||
direction: self.direction,
|
||||
world: self.world,
|
||||
}),
|
||||
Direction::West => Some(Self {
|
||||
x: self.x.checked_sub(1)?,
|
||||
y: self.y,
|
||||
direction: self.direction,
|
||||
world: self.world,
|
||||
}),
|
||||
}?;
|
||||
|
||||
if t.y >= t.world.map.len() || t.x >= t.world.map[0].len() {
|
||||
return None;
|
||||
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),
|
||||
};
|
||||
|
||||
if self.world.terrain_at(&t) == Terrain::Blocked {
|
||||
Some(self.turn())
|
||||
} else {
|
||||
Some(t)
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -108,75 +85,45 @@ struct World {
|
|||
}
|
||||
|
||||
impl World {
|
||||
fn from_string(input: &str) -> (Self, (usize, usize)) {
|
||||
let mut map: Vec<Vec<Terrain>> = Vec::new();
|
||||
let mut cursor: (usize, usize) = (0, 0);
|
||||
for (y, line) in input.lines().enumerate() {
|
||||
let row: Vec<Terrain> = line
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
'#' => Terrain::Blocked,
|
||||
_ => Terrain::Free,
|
||||
})
|
||||
.collect();
|
||||
map.push(row);
|
||||
if let Some(x) = line.find('^') {
|
||||
cursor = (x, y);
|
||||
};
|
||||
}
|
||||
(Self { map }, cursor)
|
||||
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) -> Terrain {
|
||||
self.map[at.y][at.x]
|
||||
fn terrain_at(&self, at: &Transform) -> Option<Terrain> {
|
||||
self.map.get(at.y)?.get(at.x).copied()
|
||||
}
|
||||
}
|
||||
|
||||
fn traverse<'a>(start: Transform<'a>) -> Option<Vec<Transform<'a>>> {
|
||||
let mut cursor = start;
|
||||
let mut traversed: Vec<Transform> = vec![cursor];
|
||||
while let Some(new_cursor) = cursor.travel() {
|
||||
if traversed.contains(&new_cursor) {
|
||||
return None;
|
||||
}
|
||||
cursor = new_cursor;
|
||||
traversed.push(cursor);
|
||||
}
|
||||
Some(traversed)
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
enum Terrain {
|
||||
Free,
|
||||
Blocked,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
enum Direction {
|
||||
North,
|
||||
South,
|
||||
East,
|
||||
West,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn turn(&self) -> Self {
|
||||
match self {
|
||||
Self::North => Self::East,
|
||||
Self::East => Self::South,
|
||||
Self::South => Self::West,
|
||||
Self::West => Self::North,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for Transform<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.x == other.x
|
||||
&& self.y == other.y
|
||||
&& self.direction == other.direction
|
||||
&& std::ptr::eq(self.world, other.world)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Eq for Transform<'a> {}
|
||||
|
|
|
|||
Reference in a new issue