use crate::constraint::VarId;
use crate::domain::bitset::BitsetDomain;
use crate::ordering::Ordering;
use crate::{Csp, Pruning, SolveConfig};
pub struct FutoshikiPuzzle {
pub n: u32,
pub fixed_cells: Vec<(usize, u32)>,
pub inequalities: Vec<(usize, usize)>,
}
impl FutoshikiPuzzle {
pub fn parse(input: &str) -> Self {
let mut lines = input.lines();
let n: u32 = lines.next().unwrap().trim().parse().unwrap();
let ls: Vec<usize> = lines
.next()
.unwrap()
.split_whitespace()
.map(|s| s.parse().unwrap())
.collect();
let vs: Vec<u32> = lines
.next()
.unwrap()
.split_whitespace()
.map(|s| s.parse().unwrap())
.collect();
let fixed_cells: Vec<(usize, u32)> = ls.into_iter().zip(vs).collect();
let a_s: Vec<usize> = lines
.next()
.unwrap()
.split_whitespace()
.map(|s| s.parse().unwrap())
.collect();
let b_s: Vec<usize> = lines
.next()
.unwrap()
.split_whitespace()
.map(|s| s.parse().unwrap())
.collect();
let inequalities: Vec<(usize, usize)> = a_s.into_iter().zip(b_s).collect();
Self {
n,
fixed_cells,
inequalities,
}
}
}
pub fn create_futoshiki_csp(puzzle: &FutoshikiPuzzle) -> Csp<BitsetDomain> {
let n = puzzle.n;
let total = (n * n) as usize;
let mut csp = Csp::new();
let domain = BitsetDomain::new(1..=n);
for _ in 0..total {
csp.add_variable(domain.clone());
}
for &(cell, value) in &puzzle.fixed_cells {
csp.add_equals(cell as VarId, value);
}
for &(a, b) in &puzzle.inequalities {
csp.add_greater_than(a as VarId, b as VarId);
}
for i in 0..n {
let row: Vec<VarId> = (0..n).map(|j| (i * n + j) as VarId).collect();
csp.add_all_different(row);
}
for j in 0..n {
let col: Vec<VarId> = (0..n).map(|i| (i * n + j) as VarId).collect();
csp.add_all_different(col);
}
csp.finalize();
csp
}
pub fn solve_futoshiki(puzzle: &FutoshikiPuzzle) -> Vec<Vec<u32>> {
let mut csp = create_futoshiki_csp(puzzle);
let config = SolveConfig {
pruning: Pruning::ForwardChecking,
ordering: Ordering::FailFirst,
max_solutions: usize::MAX,
backjumping: false,
..Default::default()
};
csp.solve(&config)
}