use super::mask;
use super::version::{Plan, format_bits};
pub(super) struct Grid {
side: usize,
dark: Vec<bool>,
fixed: Vec<bool>,
}
impl Grid {
pub(super) fn furnished(plan: Plan) -> Self {
let side = plan.side();
let mut grid = Self {
side,
dark: vec![false; side * side],
fixed: vec![false; side * side],
};
for at in 0..side {
grid.furnish(6, at, at % 2 == 0);
grid.furnish(at, 6, at % 2 == 0);
}
for centre in [
(3, 3),
(side.saturating_sub(4), 3),
(3, side.saturating_sub(4)),
] {
grid.stamp(centre, 4, |ring| ring != 2 && ring != 4);
}
let centres = plan.alignment();
for (i, &cy) in centres.iter().enumerate() {
for (j, &cx) in centres.iter().enumerate() {
let corner = |a: usize, b: usize| a == 0 && b == 0;
let last = centres.len().saturating_sub(1);
if !(corner(i, j) || corner(i, last - j) || corner(last - i, j)) {
grid.stamp((cx, cy), 2, |ring| ring != 1);
}
}
}
grid.furnish(8, side.saturating_sub(8), true);
for at in format_slots(side).into_iter().chain(version_slots(plan)) {
grid.furnish(at.0, at.1, false);
}
grid
}
pub(super) fn inscribe(&mut self, bits: &[bool]) {
let mut taken = 0;
let mut right = self.side.saturating_sub(1);
while right >= 1 {
if right == 6 {
right = 5;
}
for step in 0..self.side {
for x in [right, right.saturating_sub(1)] {
let upward = (right + 1) & 2 == 0;
let y = if upward { self.side - 1 - step } else { step };
if !self.is_fixed(x, y) {
self.set(x, y, bits.get(taken).copied().unwrap_or(false));
taken += 1;
}
}
}
right = right.saturating_sub(2);
}
}
pub(super) fn resolve(self, plan: Plan) -> (u8, Vec<bool>) {
let scored = (0..8_u8)
.map(|number| (number, self.masked(number)))
.min_by_key(|(number, dark)| (mask::penalty(dark, self.side), *number));
let (number, mut dark) = scored.unwrap_or((0, self.dark.clone()));
self.stamp_bits(
&mut dark,
&format_slots(self.side),
format_bits(u32::from(number)),
15,
);
if let Some(bits) = plan.version_bits() {
self.stamp_bits(&mut dark, &version_slots(plan), bits, 18);
}
(number, dark)
}
fn masked(&self, number: u8) -> Vec<bool> {
let mut dark = self.dark.clone();
for (at, module) in dark.iter_mut().enumerate() {
let (x, y) = (at % self.side, at / self.side);
if self.fixed.get(at) != Some(&true) && mask::inverts(number, x, y) {
*module = !*module;
}
}
dark
}
fn stamp_bits(&self, dark: &mut [bool], slots: &[(usize, usize)], bits: u32, span: usize) {
for (index, &(x, y)) in slots.iter().enumerate() {
if let Some(module) = dark.get_mut(y * self.side + x) {
*module = bits >> (index % span) & 1 == 1;
}
}
}
fn stamp(&mut self, centre: (usize, usize), reach: i64, on: impl Fn(i64) -> bool) {
for dy in -reach..=reach {
for dx in -reach..=reach {
let x = i64::try_from(centre.0).unwrap_or(0) + dx;
let y = i64::try_from(centre.1).unwrap_or(0) + dy;
if let (Ok(x), Ok(y)) = (usize::try_from(x), usize::try_from(y)) {
self.furnish(x, y, on(dx.abs().max(dy.abs())));
}
}
}
}
fn offset(&self, x: usize, y: usize) -> Option<usize> {
(x < self.side).then(|| y * self.side + x)
}
fn furnish(&mut self, x: usize, y: usize, on: bool) {
self.set(x, y, on);
if let Some(slot) = self.offset(x, y).and_then(|at| self.fixed.get_mut(at)) {
*slot = true;
}
}
fn set(&mut self, x: usize, y: usize, on: bool) {
if let Some(slot) = self.offset(x, y).and_then(|at| self.dark.get_mut(at)) {
*slot = on;
}
}
fn is_fixed(&self, x: usize, y: usize) -> bool {
self.offset(x, y)
.and_then(|at| self.fixed.get(at))
.copied()
.unwrap_or(false)
}
}
fn version_slots(plan: Plan) -> Vec<(usize, usize)> {
if plan.version_bits().is_none() {
return Vec::new();
}
let far = |index: usize| plan.side() + index % 3 - 11;
let near = |index: usize| index / 3;
(0..18)
.map(|index| (far(index), near(index)))
.chain((0..18).map(|index| (near(index), far(index))))
.collect()
}
fn format_slots(side: usize) -> Vec<(usize, usize)> {
let end = side.saturating_sub(1);
let first = (0..6)
.map(|i| (8, i))
.chain([(8, 7), (8, 8), (7, 8)])
.chain((9..15).map(|i| (14 - i, 8)));
let second = (0..8)
.map(move |i| (end - i, 8))
.chain((8..15).map(move |i| (8, side + i - 15)));
first.chain(second).collect()
}