Day 6 (refactor)
This commit is contained in:
parent
7a0c38afb0
commit
8036ad4d58
|
@ -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"
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
201
src/day6.rs
201
src/day6.rs
|
@ -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> {}
|
|
||||||
|
|
Loading…
Reference in New Issue