pub const PATTERNS: [&str; 107] = [
"212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", "211232", "233111", ];
pub const STOP: &str = "2331112";
pub const START_A: u8 = 103;
pub const START_B: u8 = 104;
pub const START_C: u8 = 105;
pub const STOP_VALUE: u8 = 106;
pub const CODE_A: u8 = 101;
pub const CODE_B: u8 = 100;
pub const CODE_C: u8 = 99;
pub const SHIFT: u8 = 98;
pub const FNC1: u8 = 102;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CodeSet {
A,
B,
C,
}
impl CodeSet {
pub fn start_value(self) -> u8 {
match self {
CodeSet::A => START_A,
CodeSet::B => START_B,
CodeSet::C => START_C,
}
}
pub fn from_start(value: u8) -> Option<CodeSet> {
match value {
START_A => Some(CodeSet::A),
START_B => Some(CodeSet::B),
START_C => Some(CodeSet::C),
_ => None,
}
}
}
pub fn value_for_widths(widths: &[usize]) -> Option<u8> {
if widths.len() != 6 {
return None;
}
let mut buf = [0u8; 6];
for (slot, &w) in buf.iter_mut().zip(widths) {
if !(1..=4).contains(&w) {
return None;
}
*slot = b'0' + w as u8;
}
let s = core::str::from_utf8(&buf).ok()?;
PATTERNS.iter().position(|p| *p == s).map(|v| v as u8)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_pattern_sums_to_11() {
for (value, pat) in PATTERNS.iter().enumerate() {
let sum: u32 = pat.bytes().map(|b| (b - b'0') as u32).sum();
assert_eq!(sum, 11, "value {value} pattern {pat} width sum");
assert_eq!(pat.len(), 6, "value {value} must have six elements");
}
}
#[test]
fn stop_pattern_is_13_modules() {
let sum: u32 = STOP.bytes().map(|b| (b - b'0') as u32).sum();
assert_eq!(sum, 13);
assert_eq!(STOP.len(), 7);
}
#[test]
fn patterns_are_unique() {
for (i, a) in PATTERNS.iter().enumerate() {
for (j, b) in PATTERNS.iter().enumerate().skip(i + 1) {
assert_ne!(a, b, "values {i} and {j} collide");
}
}
}
}