use super::matrix::Matrix;
pub fn mask_condition(mask: u8, row: usize, col: usize) -> bool {
let r = row as i32;
let c = col as i32;
match mask {
0 => (r + c) % 2 == 0,
1 => r % 2 == 0,
2 => c % 3 == 0,
3 => (r + c) % 3 == 0,
4 => (r / 2 + c / 3) % 2 == 0,
5 => (r * c) % 2 + (r * c) % 3 == 0,
6 => ((r * c) % 2 + (r * c) % 3) % 2 == 0,
7 => ((r + c) % 2 + (r * c) % 3) % 2 == 0,
_ => panic!("mask out of range: {}", mask),
}
}
pub fn apply_mask(matrix: &mut Matrix, mask: u8) {
let n = matrix.size;
for r in 0..n {
for c in 0..n {
if matrix.is_reserved(r, c) {
continue;
}
if mask_condition(mask, r, c) {
let v = matrix.get(r, c);
matrix.set_data(r, c, !v);
}
}
}
}
fn rule1(matrix: &Matrix) -> u32 {
let n = matrix.size;
let mut total = 0u32;
for r in 0..n {
total += run_score_along(|c| matrix.get(r, c), n);
}
for c in 0..n {
total += run_score_along(|r| matrix.get(r, c), n);
}
total
}
fn run_score_along<F: Fn(usize) -> bool>(get: F, n: usize) -> u32 {
let mut score = 0u32;
let mut run_color = get(0);
let mut run_len = 1u32;
for i in 1..n {
let v = get(i);
if v == run_color {
run_len += 1;
} else {
if run_len >= 5 {
score += 3 + (run_len - 5);
}
run_color = v;
run_len = 1;
}
}
if run_len >= 5 {
score += 3 + (run_len - 5);
}
score
}
fn rule2(matrix: &Matrix) -> u32 {
let n = matrix.size;
let mut total = 0u32;
for r in 0..n - 1 {
for c in 0..n - 1 {
let a = matrix.get(r, c);
if matrix.get(r, c + 1) == a && matrix.get(r + 1, c) == a && matrix.get(r + 1, c + 1) == a
{
total += 3;
}
}
}
total
}
fn rule3(matrix: &Matrix) -> u32 {
let n = matrix.size;
let mut total = 0u32;
let pattern_a: [bool; 11] = [false, false, false, false, true, false, true, true, true, false, true];
let pattern_b: [bool; 11] = [true, false, true, true, true, false, true, false, false, false, false];
for r in 0..n {
for c in 0..=n - 11 {
let mut buf = [false; 11];
for k in 0..11 {
buf[k] = matrix.get(r, c + k);
}
if buf == pattern_a || buf == pattern_b {
total += 40;
}
}
}
for c in 0..n {
for r in 0..=n - 11 {
let mut buf = [false; 11];
for k in 0..11 {
buf[k] = matrix.get(r + k, c);
}
if buf == pattern_a || buf == pattern_b {
total += 40;
}
}
}
total
}
fn rule4(matrix: &Matrix) -> u32 {
let n = matrix.size;
let total_modules = n * n;
let dark = matrix.modules_iter().filter(|&&v| v).count();
let percent = (dark * 100) / total_modules;
let deviation = if percent >= 50 { percent - 50 } else { 50 - percent };
(deviation as u32 / 5) * 10
}
pub fn score(matrix: &Matrix) -> u32 {
rule1(matrix) + rule2(matrix) + rule3(matrix) + rule4(matrix)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tables::Version;
#[test]
fn mask_self_inverse() {
let mut m = Matrix::new(Version::new(1));
let mut data_cells = Vec::new();
for r in 0..m.size {
for c in 0..m.size {
if !m.is_reserved(r, c) {
let v = (r * 3 + c * 7) % 2 == 0;
m.set_data(r, c, v);
data_cells.push((r, c, v));
}
}
}
for mask in 0..8u8 {
apply_mask(&mut m, mask);
apply_mask(&mut m, mask);
for &(r, c, v) in &data_cells {
assert_eq!(m.get(r, c), v, "mask {} 双向 XOR 后 ({},{}) 不还原", mask, r, c);
}
}
}
#[test]
fn mask_condition_table() {
assert!(mask_condition(0, 0, 0));
assert!(!mask_condition(0, 0, 1));
assert!(mask_condition(1, 0, 5));
assert!(!mask_condition(1, 1, 5));
assert!(mask_condition(2, 7, 0));
assert!(!mask_condition(2, 7, 1));
}
#[test]
fn rule1_long_run() {
let mut m = Matrix::new(Version::new(1));
for c in 9..17 {
if !m.is_reserved(10, c) {
m.set_data(10, c, true);
}
}
let s = rule1(&m);
assert!(s >= 6, "rule1 score was {}", s);
}
#[test]
fn select_best_mask_picks_low_score() {
let mut m = Matrix::new(Version::new(1));
for r in 0..m.size {
for c in 0..m.size {
if !m.is_reserved(r, c) {
m.set_data(r, c, (r * 13 + c * 31) % 7 < 3);
}
}
}
let (mask, score_val) = select_best_mask(&mut m, |_m, _mask| {
});
assert!(mask < 8);
assert!(score_val < 10_000); }
}
#[allow(dead_code)]
pub fn select_best_mask<F>(matrix: &mut Matrix, mut write_format_info: F) -> (u8, u32)
where
F: FnMut(&mut Matrix, u8),
{
let mut best = (0u8, u32::MAX);
for m in 0u8..8 {
apply_mask(matrix, m);
write_format_info(matrix, m);
let s = score(matrix);
if s < best.1 {
best = (m, s);
}
apply_mask(matrix, m); }
apply_mask(matrix, best.0);
write_format_info(matrix, best.0);
best
}