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