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"
dependencies = [
"dotenvy",
"rayon",
"regex",
"reqwest",
]
@ -117,6 +118,31 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "displaydoc"
version = "0.2.5"
@ -134,6 +160,12 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "encoding_rs"
version = "0.8.35"
@ -744,6 +776,26 @@ dependencies = [
"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]]
name = "regex"
version = "1.11.1"

View File

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

View File

@ -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,
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();
map.push(row);
if let Some(x) = line.find('^') {
cursor = (x, y);
};
}
(Self { map }, cursor)
(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> {}