use crate::constraint::VarId;
use crate::domain::bitset::BitsetDomain;
use crate::puzzles::sudoku::csp::sudoku_given;
use crate::{Csp, SolveConfig};
pub type Thermometer = Vec<usize>;
pub fn create_thermo_csp(
board: &[u32],
n: u32,
thermometers: &[Thermometer],
) -> (Csp<BitsetDomain>, Vec<(VarId, u32)>) {
let m = n * n;
let total = (m * m) as usize;
assert_eq!(board.len(), total, "board must have M*M = {total} elements");
let mut csp = Csp::new();
let domain = BitsetDomain::new(1..=m);
for _ in 0..total {
csp.add_variable(domain.clone());
}
let mut group: Vec<VarId> = Vec::with_capacity(m as usize);
for r in 0..m {
group.clear();
for c in 0..m {
group.push((r * m + c) as VarId);
}
csp.add_all_different(group.clone());
}
for c in 0..m {
group.clear();
for r in 0..m {
group.push((r * m + c) as VarId);
}
csp.add_all_different(group.clone());
}
for bi in 0..n {
for bj in 0..n {
group.clear();
for di in 0..n {
for dj in 0..n {
group.push(((bi * n + di) * m + (bj * n + dj)) as VarId);
}
}
csp.add_all_different(group.clone());
}
}
for thermo in thermometers {
for pair in thermo.windows(2) {
csp.add_less_than(pair[0] as VarId, pair[1] as VarId);
}
}
csp.finalize();
let given = sudoku_given(board);
(csp, given)
}
pub fn solve_thermo(
board: &[u32],
n: u32,
thermometers: &[Thermometer],
config: &SolveConfig,
) -> Option<Vec<u32>> {
let (mut csp, given) = create_thermo_csp(board, n, thermometers);
csp.solve_with_given(config, &given).into_iter().next()
}