Day 6 (refactor)

This commit is contained in:
sepia 2024-12-06 12:53:49 -06:00
parent 7a0c38afb0
commit 8036ad4d58
3 changed files with 127 additions and 127 deletions

52
Cargo.lock generated
View File

@ -22,6 +22,7 @@ name = "advent"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dotenvy", "dotenvy",
"rayon",
"regex", "regex",
"reqwest", "reqwest",
] ]
@ -117,6 +118,31 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.5" version = "0.2.5"
@ -134,6 +160,12 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.35" version = "0.8.35"
@ -744,6 +776,26 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.1"

View File

@ -5,5 +5,6 @@ edition = "2021"
[dependencies] [dependencies]
dotenvy = "0.15.7" dotenvy = "0.15.7"
rayon = "1.10.0"
regex = "1.11.1" regex = "1.11.1"
reqwest = { version = "0.12.9", features = ["blocking"] } reqwest = { version = "0.12.9", features = ["blocking"] }

View File

@ -1,33 +1,21 @@
use rayon::prelude::*;
pub fn day6() { 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 (mut path, _) = traverse(start, &world);
let start = Transform::new(
cursor_location.0,
cursor_location.1,
Direction::North,
&world,
);
let mut path = traverse(start).unwrap(); path.sort_by_key(|t| (t.x, t.y));
path.dedup_by_key(|t| (t.x, t.y));
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);
let total_traveled = path.len(); let total_traveled = path.len();
let loops = path let loops = path
.iter() .par_iter()
.filter(|p| { .filter(|p| {
let mut new_world = world.clone(); let mut new_world = world.clone();
new_world.map[p.y][p.x] = Terrain::Blocked; new_world.map[p.y][p.x] = Terrain::Blocked;
let start = Transform::new(start.x, start.y, start.direction, &new_world); let (_, is_loop) = traverse(start, &new_world);
traverse(start).is_none() is_loop
}) })
.count(); .count();
@ -35,69 +23,58 @@ pub fn day6() {
println!("Total Loopable Positions: {}", loops); println!("Total Loopable Positions: {}", loops);
} }
#[derive(Copy, Clone)] fn traverse(start: Transform, world: &World) -> (Vec<Transform>, bool) {
struct Transform<'a> { 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, x: usize,
y: usize, y: usize,
direction: Direction, direction: Direction,
world: &'a World,
} }
impl<'a> Transform<'a> { impl Transform {
fn new(x: usize, y: usize, direction: Direction, world: &'a World) -> Self { fn new(x: usize, y: usize, direction: Direction) -> Self {
Self { Self { x, y, direction }
x,
y,
direction,
world,
}
} }
fn turn(&self) -> Self { fn turn(&self) -> Self {
Self { let direction = match self.direction {
x: self.x, Direction::North => Direction::East,
y: self.y, Direction::East => Direction::South,
direction: self.direction.turn(), Direction::South => Direction::West,
world: self.world, Direction::West => Direction::North,
} };
Self::new(self.x, self.y, direction)
} }
fn travel(&self) -> Option<Self> { fn travel(&self, world: &World) -> Option<Self> {
let t = match self.direction { let (dx, dy) = match self.direction {
Direction::North => Some(Self { Direction::North => (0, -1),
x: self.x, Direction::South => (0, 1),
y: self.y.checked_sub(1)?, Direction::East => (1, 0),
direction: self.direction, Direction::West => (-1, 0),
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;
}; };
if self.world.terrain_at(&t) == Terrain::Blocked { let new_x = (self.x as i32 + dx).try_into().ok()?;
Some(self.turn()) let new_y = (self.y as i32 + dy).try_into().ok()?;
} else { let next = Self::new(new_x, new_y, self.direction);
Some(t)
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 { impl World {
fn from_string(input: &str) -> (Self, (usize, usize)) { fn from_string(input: &str) -> (Self, Transform) {
let mut map: Vec<Vec<Terrain>> = Vec::new(); let mut start = Transform::new(0, 0, Direction::North);
let mut cursor: (usize, usize) = (0, 0); let map = input
for (y, line) in input.lines().enumerate() { .lines()
let row: Vec<Terrain> = line .enumerate()
.chars() .map(|(y, line)| {
.map(|c| match c { if let Some(x) = line.find('^') {
'#' => Terrain::Blocked, start = Transform::new(x, y, Direction::North);
_ => Terrain::Free, }
}) line.chars()
.collect(); .map(|c| {
map.push(row); if c == '#' {
if let Some(x) = line.find('^') { Terrain::Blocked
cursor = (x, y); } else {
}; Terrain::Free
} }
(Self { map }, cursor) })
.collect()
})
.collect();
(Self { map }, start)
} }
fn terrain_at(&self, at: &Transform) -> Terrain { fn terrain_at(&self, at: &Transform) -> Option<Terrain> {
self.map[at.y][at.x] 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)] #[derive(PartialEq, Eq, Copy, Clone)]
enum Terrain { enum Terrain {
Free, Free,
Blocked, Blocked,
} }
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone)]
enum Direction { enum Direction {
North, North,
South, South,
East, East,
West, 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> {}