pub(super) fn inverts(mask: u8, x: usize, y: usize) -> bool {
match mask {
0 => (y + x).is_multiple_of(2),
1 => y.is_multiple_of(2),
2 => x.is_multiple_of(3),
3 => (y + x).is_multiple_of(3),
4 => (y / 2 + x / 3).is_multiple_of(2),
5 => ((y * x) % 2 + (y * x) % 3) == 0,
6 => ((y * x) % 2 + (y * x) % 3).is_multiple_of(2),
_ => ((y + x) % 2 + (y * x) % 3).is_multiple_of(2),
}
}
pub(super) fn penalty(dark: &[bool], side: usize) -> usize {
let at = |x: usize, y: usize| dark.get(y * side + x).copied().unwrap_or(false);
let lines: Vec<Vec<bool>> = (0..side)
.map(|y| (0..side).map(|x| at(x, y)).collect())
.chain((0..side).map(|x| (0..side).map(|y| at(x, y)).collect()))
.collect();
let one: usize = lines.iter().map(|line| runs(line)).sum();
let three: usize = lines.iter().map(|line| finders(line)).sum();
one + three + squares(dark, side) + balance(dark)
}
fn runs(line: &[bool]) -> usize {
let mut total = 0;
let mut run = 0_usize;
let mut colour = None;
for &module in line {
run = if colour == Some(module) { run + 1 } else { 1 };
colour = Some(module);
if run == 5 {
total += 3;
} else if run > 5 {
total += 1;
}
}
total
}
fn finders(line: &[bool]) -> usize {
const CORE: [bool; 7] = [true, false, true, true, true, false, true];
const QUIET: [bool; 4] = [false; 4];
line.windows(11)
.filter(|window| {
let is = |range: std::ops::Range<usize>, want: &[bool]| {
window.get(range).is_some_and(|part| part == want)
};
is(0..7, &CORE) && is(7..11, &QUIET) || is(4..11, &CORE) && is(0..4, &QUIET)
})
.count()
* 40
}
fn squares(dark: &[bool], side: usize) -> usize {
let at = |x: usize, y: usize| dark.get(y * side + x).copied();
let uniform = |x: usize, y: usize| {
let first = at(x, y);
first.is_some()
&& [at(x + 1, y), at(x, y + 1), at(x + 1, y + 1)]
.iter()
.all(|&other| other == first)
};
let last = side.saturating_sub(1);
(0..last)
.flat_map(|y| (0..last).map(move |x| (x, y)))
.filter(|&(x, y)| uniform(x, y))
.count()
* 3
}
fn balance(dark: &[bool]) -> usize {
let total = dark.len().max(1);
let deviation = (dark.iter().filter(|&&module| module).count() * 100).abs_diff(total * 50);
deviation / (5 * total) * 10
}