feat: 蛇形数据排列 + 8 掩码 + 评分
This commit is contained in:
+249
-1
@@ -1 +1,249 @@
|
||||
// FIXME: 掩码评分 — Task 8
|
||||
use crate::matrix::grid::Matrix;
|
||||
|
||||
/// 掩码函数: f(x, y) = true 时翻转该模块
|
||||
type MaskFn = fn(u8, u8) -> bool;
|
||||
|
||||
/// 8 种标准 QR 掩码
|
||||
pub const MASK_FNS: [MaskFn; 8] = [
|
||||
|x, y| (x + y) % 2 == 0,
|
||||
|_, y| y % 2 == 0,
|
||||
|x, _| x % 3 == 0,
|
||||
|x, y| (x + y) % 3 == 0,
|
||||
|x, y| ((y / 2) + (x / 3)) % 2 == 0,
|
||||
|x, y| (x as u32 * y as u32) % 2 + (x as u32 * y as u32) % 3 == 0,
|
||||
|x, y| ((x as u32 * y as u32) % 2 + (x as u32 * y as u32) % 3) % 2 == 0,
|
||||
|x, y| ((x as u32 + y as u32) % 2 + (x as u32 * y as u32) % 3) % 2 == 0,
|
||||
];
|
||||
|
||||
/// 应用掩码到矩阵的数据区域(跳过功能图案保留区域)
|
||||
pub fn apply_mask(matrix: &Matrix, mask_idx: u8) -> Matrix {
|
||||
let mask_fn = MASK_FNS[mask_idx as usize];
|
||||
let mut result = matrix.clone();
|
||||
for y in 0..matrix.size {
|
||||
for x in 0..matrix.size {
|
||||
if !matrix.is_reserved(x, y) && mask_fn(x, y) {
|
||||
let current = result.get(x, y);
|
||||
result.set(x, y, !current);
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// 惩罚评分(越低越好)
|
||||
pub fn score(matrix: &Matrix) -> u32 {
|
||||
score_rule1(matrix) + score_rule2(matrix) + score_rule3(matrix) + score_rule4(matrix)
|
||||
}
|
||||
|
||||
/// 规则 1: 连续 5+ 同色行/列 → N1 + k - 5
|
||||
fn score_rule1(matrix: &Matrix) -> u32 {
|
||||
let mut penalty = 0u32;
|
||||
let n = matrix.size as usize;
|
||||
|
||||
// 水平扫描
|
||||
for y in 0..n {
|
||||
let mut run = 1u32;
|
||||
let mut prev = matrix.get(0, y as u8);
|
||||
for x in 1..n {
|
||||
let cur = matrix.get(x as u8, y as u8);
|
||||
if cur == prev {
|
||||
run += 1;
|
||||
} else {
|
||||
if run >= 5 {
|
||||
penalty += 3 + run - 5;
|
||||
}
|
||||
run = 1;
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
if run >= 5 {
|
||||
penalty += 3 + run - 5;
|
||||
}
|
||||
}
|
||||
|
||||
// 垂直扫描
|
||||
for x in 0..n {
|
||||
let mut run = 1u32;
|
||||
let mut prev = matrix.get(x as u8, 0);
|
||||
for y in 1..n {
|
||||
let cur = matrix.get(x as u8, y as u8);
|
||||
if cur == prev {
|
||||
run += 1;
|
||||
} else {
|
||||
if run >= 5 {
|
||||
penalty += 3 + run - 5;
|
||||
}
|
||||
run = 1;
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
if run >= 5 {
|
||||
penalty += 3 + run - 5;
|
||||
}
|
||||
}
|
||||
|
||||
penalty
|
||||
}
|
||||
|
||||
/// 规则 2: 同色 2×2 方块,每个 +3
|
||||
fn score_rule2(matrix: &Matrix) -> u32 {
|
||||
let mut count = 0u32;
|
||||
let n = matrix.size;
|
||||
for y in 0..n - 1 {
|
||||
for x in 0..n - 1 {
|
||||
let v = matrix.get(x, y);
|
||||
if matrix.get(x + 1, y) == v
|
||||
&& matrix.get(x, y + 1) == v
|
||||
&& matrix.get(x + 1, y + 1) == v
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
count * 3
|
||||
}
|
||||
|
||||
/// 规则 3: 检测 1011101 模式(及其反转),每次 +40
|
||||
fn score_rule3(matrix: &Matrix) -> u32 {
|
||||
let mut penalty = 0u32;
|
||||
let n = matrix.size as usize;
|
||||
|
||||
// 水平方向
|
||||
for y in 0..n {
|
||||
for x in 0..n {
|
||||
if x + 10 >= n {
|
||||
continue;
|
||||
}
|
||||
// 正模式: 10111010000
|
||||
let forward = (0..11).all(|i| {
|
||||
let expected = [
|
||||
true, false, true, true, true, false, true, false, false, false, false,
|
||||
][i];
|
||||
matrix.get((x + i) as u8, y as u8) == expected
|
||||
});
|
||||
if forward {
|
||||
penalty += 40;
|
||||
}
|
||||
|
||||
// 反模式: 00001011101
|
||||
let reverse = (0..11).all(|i| {
|
||||
let expected = [
|
||||
false, false, false, false, true, false, true, true, true, false, true,
|
||||
][i];
|
||||
matrix.get((x + i) as u8, y as u8) == expected
|
||||
});
|
||||
if reverse {
|
||||
penalty += 40;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 垂直方向
|
||||
for y in 0..n {
|
||||
if y + 10 >= n {
|
||||
continue;
|
||||
}
|
||||
for x in 0..n {
|
||||
let forward = (0..11).all(|i| {
|
||||
let expected = [
|
||||
true, false, true, true, true, false, true, false, false, false, false,
|
||||
][i];
|
||||
matrix.get(x as u8, (y + i) as u8) == expected
|
||||
});
|
||||
if forward {
|
||||
penalty += 40;
|
||||
}
|
||||
|
||||
let reverse = (0..11).all(|i| {
|
||||
let expected = [
|
||||
false, false, false, false, true, false, true, true, true, false, true,
|
||||
][i];
|
||||
matrix.get(x as u8, (y + i) as u8) == expected
|
||||
});
|
||||
if reverse {
|
||||
penalty += 40;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
penalty
|
||||
}
|
||||
|
||||
/// 规则 4: 暗模块占比偏离 50%,每 5% +10
|
||||
fn score_rule4(matrix: &Matrix) -> u32 {
|
||||
let total = (matrix.size as u32) * (matrix.size as u32);
|
||||
let dark: u32 = (0..matrix.size)
|
||||
.flat_map(|y| (0..matrix.size).map(move |x| matrix.get(x, y) as u32))
|
||||
.sum();
|
||||
|
||||
let pct = (dark * 100 + total / 2) / total; // 四舍五入
|
||||
let deviation = ((pct as i32 - 50).unsigned_abs()) / 5;
|
||||
deviation * 10
|
||||
}
|
||||
|
||||
/// 评估所有 8 种掩码,返回最佳掩码编号和对应矩阵
|
||||
pub fn best_mask(matrix: &Matrix) -> (u8, Matrix) {
|
||||
let mut best_idx = 0u8;
|
||||
let mut best_score = u32::MAX;
|
||||
let mut best_matrix = matrix.clone();
|
||||
|
||||
for i in 0..8u8 {
|
||||
let masked = apply_mask(matrix, i);
|
||||
let s = score(&masked);
|
||||
if s < best_score {
|
||||
best_score = s;
|
||||
best_idx = i;
|
||||
best_matrix = masked;
|
||||
}
|
||||
}
|
||||
|
||||
(best_idx, best_matrix)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_apply_mask() {
|
||||
let m = Matrix::new(21);
|
||||
// 不设置 reserved,所有区域都是数据区
|
||||
let masked = apply_mask(&m, 0); // (x+y) % 2 == 0
|
||||
// 初始全白,掩码 0 会在 (x+y)%2==0 的位置翻转
|
||||
assert_eq!(masked.get(0, 0), true); // (0+0)%2=0 → 翻转
|
||||
assert_eq!(masked.get(1, 0), false); // (1+0)%2=1 → 不变
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_rule2() {
|
||||
let mut m = Matrix::new(3);
|
||||
// 全黑 → 4 个 2×2 方块
|
||||
for y in 0..3u8 {
|
||||
for x in 0..3u8 {
|
||||
m.set(x, y, true);
|
||||
}
|
||||
}
|
||||
assert_eq!(score_rule2(&m), 4 * 3); // 4 blocks × 3 = 12
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_rule4() {
|
||||
let m = Matrix::new(10);
|
||||
// 全部白色 → 0% dark → 偏离 50% = 10 × 5% → penalty = 10 × 10 = 100
|
||||
let s = score_rule4(&m);
|
||||
assert_eq!(s, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_best_mask_selects_something() {
|
||||
let mut m = Matrix::new(21);
|
||||
// 填一些随机数据
|
||||
for y in 0..21u8 {
|
||||
for x in 0..21u8 {
|
||||
m.set(x, y, (x as u32 * y as u32) % 3 == 0);
|
||||
}
|
||||
}
|
||||
let (idx, _masked) = best_mask(&m);
|
||||
assert!(idx < 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,80 @@
|
||||
// FIXME: 数据排列 — Task 8
|
||||
use crate::matrix::grid::Matrix;
|
||||
|
||||
/// 将码字比特按蛇形路径放入矩阵数据区域
|
||||
pub fn place_data(matrix: &mut Matrix, codewords: &[u8]) {
|
||||
let size = matrix.size as usize;
|
||||
// 展开字节为比特流
|
||||
let bits: Vec<bool> = codewords
|
||||
.iter()
|
||||
.flat_map(|&cw| (0..8).rev().map(move |i| (cw >> i) & 1 == 1))
|
||||
.collect();
|
||||
|
||||
let mut bit_idx = 0usize;
|
||||
|
||||
// 从右下角开始,向上扫描
|
||||
let mut col = (size - 1) as i16;
|
||||
let mut going_up = true;
|
||||
|
||||
while col >= 0 && bit_idx < bits.len() {
|
||||
// 跳过垂直时序线 (col=6)
|
||||
let actual_col = if col == 6 { 5 } else { col as usize };
|
||||
|
||||
if going_up {
|
||||
for row in (0..size).rev() {
|
||||
if bit_idx >= bits.len() {
|
||||
break;
|
||||
}
|
||||
place_bit(
|
||||
matrix,
|
||||
actual_col as u8,
|
||||
row as u8,
|
||||
bits[bit_idx],
|
||||
&mut bit_idx,
|
||||
);
|
||||
if bit_idx >= bits.len() {
|
||||
break;
|
||||
}
|
||||
place_bit(
|
||||
matrix,
|
||||
(actual_col - 1) as u8,
|
||||
row as u8,
|
||||
bits[bit_idx],
|
||||
&mut bit_idx,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for row in 0..size {
|
||||
if bit_idx >= bits.len() {
|
||||
break;
|
||||
}
|
||||
place_bit(
|
||||
matrix,
|
||||
actual_col as u8,
|
||||
row as u8,
|
||||
bits[bit_idx],
|
||||
&mut bit_idx,
|
||||
);
|
||||
if bit_idx >= bits.len() {
|
||||
break;
|
||||
}
|
||||
place_bit(
|
||||
matrix,
|
||||
(actual_col - 1) as u8,
|
||||
row as u8,
|
||||
bits[bit_idx],
|
||||
&mut bit_idx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
col -= 2;
|
||||
going_up = !going_up;
|
||||
}
|
||||
}
|
||||
|
||||
fn place_bit(matrix: &mut Matrix, x: u8, y: u8, bit: bool, idx: &mut usize) {
|
||||
if x < matrix.size && y < matrix.size && !matrix.is_reserved(x, y) {
|
||||
matrix.set(x, y, bit);
|
||||
*idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user