-
Notifications
You must be signed in to change notification settings - Fork 10
/
day11.rs
101 lines (85 loc) · 2.96 KB
/
day11.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! # Chronal Charge
//!
//! Building a [summed-area table](https://en.wikipedia.org/wiki/Summed-area_table) allows us
//! to compute the power of any rectangle with only 4 array lookups.
//!
//! This makes the total complexity `O(n³)`, however the calculation for each size is independent
//! so we can parallelize over multiple threads.
use crate::util::parse::*;
use crate::util::thread::*;
use std::sync::Mutex;
pub struct Result {
x: usize,
y: usize,
size: usize,
power: i32,
}
struct Shared {
sat: Vec<i32>,
mutex: Mutex<Vec<Result>>,
}
pub fn parse(input: &str) -> Vec<Result> {
let grid_serial_number: i32 = input.signed();
// Build Summed-area table.
let mut sat = vec![0; 301 * 301];
for y in 1..301 {
for x in 1..301 {
let rack_id = x + 10;
let mut power_level = rack_id * y;
power_level += grid_serial_number;
power_level *= rack_id;
power_level = (power_level / 100) % 10;
power_level -= 5;
let index = (301 * y + x) as usize;
sat[index] = power_level + sat[index - 1] + sat[index - 301] - sat[index - 302];
}
}
// Use as many cores as possible to parallelize the search.
// Smaller sizes take more time so keep batches roughly the same effort so that some
// threads are not finishing too soon and waiting idle, while others are still busy.
// For example if there are 4 cores, then they will be assigned sizes:
// * 1, 5, 9, ..
// * 2, 6, 10, ..
// * 3, 7, 11, ..
// * 4, 8, 12, ..
let shared = Shared { sat, mutex: Mutex::new(Vec::new()) };
spawn_batches((1..301).collect(), |batch| worker(&shared, batch));
shared.mutex.into_inner().unwrap()
}
pub fn part1(input: &[Result]) -> String {
let Result { x, y, .. } = input.iter().find(|r| r.size == 3).unwrap();
format!("{x},{y}")
}
pub fn part2(input: &[Result]) -> String {
let Result { x, y, size, .. } = input.iter().max_by_key(|r| r.power).unwrap();
format!("{x},{y},{size}")
}
fn worker(shared: &Shared, batch: Vec<usize>) {
let result: Vec<_> = batch
.into_iter()
.map(|size| {
let (power, x, y) = square(&shared.sat, size);
Result { x, y, size, power }
})
.collect();
shared.mutex.lock().unwrap().extend(result);
}
/// Find the (x,y) coordinates and max power for a square of the specified size.
fn square(sat: &[i32], size: usize) -> (i32, usize, usize) {
let mut max_power = i32::MIN;
let mut max_x = 0;
let mut max_y = 0;
for y in size..301 {
for x in size..301 {
let index = 301 * y + x;
let power =
sat[index] - sat[index - size] - sat[index - 301 * size] + sat[index - 302 * size];
if power > max_power {
max_power = power;
max_x = x - size + 1;
max_y = y - size + 1;
}
}
}
(max_power, max_x, max_y)
}