parallelize day 6 solution
This commit is contained in:
parent
51dc523cf6
commit
c44da97678
4 changed files with 106 additions and 30 deletions
54
2024/day6/Cargo.lock
generated
54
2024/day6/Cargo.lock
generated
|
@ -2,6 +2,60 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[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 = "day6"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rayon = "1.10.0"
|
||||
|
|
|
@ -46,5 +46,7 @@ record all turns made somewhere, I just copy and save every turn step (position
|
|||
this step is already in the list of previous turns, I've actually entered a loop and can stop. This then leads to the correct number
|
||||
of possible obstacle postions.
|
||||
|
||||
One possible optimization would be to use the result map from task 1 to only check the positions, where the guard actually reaches,
|
||||
but the runtime of the algorithms is still in the low seconds on my laptop, so, again, not worth the effort...
|
||||
After my son challenged me to optimize the thing a bit, the program got it's pre-check to only ever simulate the guard walk for positions
|
||||
that are actually reachable on the original map and I took the chance to play with [rayon](https://github.com/rayon-rs/rayon). Initially
|
||||
the algorithm placing the potential obstacles was just two nested loops. Replacing the inner loop with a rayon parallel iterator and
|
||||
thereby distributing it to multiple CPU cores
|
|
@ -1,3 +1,5 @@
|
|||
use rayon::prelude::*;
|
||||
|
||||
type Map = Vec<Vec<char>>;
|
||||
|
||||
fn load_map() -> Map {
|
||||
|
@ -142,42 +144,59 @@ fn task1() {
|
|||
}
|
||||
|
||||
fn task2() {
|
||||
let map_orig = load_map();
|
||||
let mut map_orig = load_map();
|
||||
let mut possible_positions = 0;
|
||||
|
||||
// mark all fields touched by the guard
|
||||
let mut guard_opt = Some(get_guard_position(&map_orig));
|
||||
loop {
|
||||
if let Some(guard) = guard_opt {
|
||||
if map_orig[guard.y][guard.x] != '^' {
|
||||
map_orig[guard.y][guard.x] = 'X';
|
||||
}
|
||||
guard_opt = step_guard(&map_orig, guard);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// try out all possible positions and add some obstacle there
|
||||
for y in 0..map_orig.len() {
|
||||
for x in 0..map_orig[y].len() {
|
||||
let mut map = map_orig.clone();
|
||||
let mut guard_opt = Some(get_guard_position(&map));
|
||||
possible_positions += (0..map_orig[y].len())
|
||||
.into_par_iter()
|
||||
.fold(
|
||||
|| 0,
|
||||
|sum, x| {
|
||||
let mut map = map_orig.clone();
|
||||
let mut guard_opt = Some(get_guard_position(&map));
|
||||
|
||||
if map[x][y] == '#' || map[x][y] == '^' {
|
||||
continue; // no need to check here, there's already an obstacle or the guard
|
||||
}
|
||||
let current_element = map[x][y];
|
||||
map[x][y] = '#';
|
||||
let mut existing_turns = Vec::<GuardPosition>::new();
|
||||
loop {
|
||||
if let Some(guard) = guard_opt.as_ref() {
|
||||
let new_guard_pos = step_guard(&map, guard.clone());
|
||||
if let Some(np) = new_guard_pos.as_ref() {
|
||||
if np.direction != guard.direction {
|
||||
// we changed direction -> check whether we've changed the direction in the exact same way here before, which would mean we're in a loop
|
||||
if existing_turns.contains(np) {
|
||||
possible_positions += 1;
|
||||
break;
|
||||
} else {
|
||||
existing_turns.push(np.clone()); // remember the turn
|
||||
if map[x][y] != 'X' {
|
||||
return sum; // no need to check here, there's already an obstacle or the guard
|
||||
}
|
||||
map[x][y] = '#';
|
||||
let mut existing_turns = Vec::<GuardPosition>::new();
|
||||
loop {
|
||||
if let Some(guard) = guard_opt.as_ref() {
|
||||
let new_guard_pos = step_guard(&map, guard.clone());
|
||||
if let Some(np) = new_guard_pos.as_ref() {
|
||||
if np.direction != guard.direction {
|
||||
// we changed direction -> check whether we've changed the direction in the exact same way here before, which would mean we're in a loop
|
||||
if existing_turns.contains(np) {
|
||||
return sum + 1;
|
||||
} else {
|
||||
existing_turns.push(np.clone()); // remember the turn
|
||||
}
|
||||
}
|
||||
}
|
||||
guard_opt = new_guard_pos;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
guard_opt = new_guard_pos;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
map[x][y] = current_element;
|
||||
}
|
||||
sum
|
||||
},
|
||||
)
|
||||
.sum::<u32>();
|
||||
}
|
||||
|
||||
println!("Task 2: Possible positions: {possible_positions}");
|
||||
|
|
Loading…
Add table
Reference in a new issue