use selen::prelude::*;
use std::time::Instant;
fn main() {
println!("๐ N-Queens Problem Solver");
println!("=========================\n");
let test_sizes = [4, 6, 8];
for &n in &test_sizes {
println!("๐ Solving {}-Queens problem:", n);
println!("๐ Problem stats: {} queens, {} variables, {} AllDifferent constraints", n, n, 3);
let start = Instant::now();
match solve_n_queens(n) {
Some((solution, stats)) => {
let duration = start.elapsed();
println!("โ
Solution found in {:.3}ms!", duration.as_secs_f64() * 1000.0);
println!("๐ Statistics: {} propagations, {} nodes explored", stats.propagations, stats.nodes);
println!("๐ Efficiency: {:.1} propagations/node", stats.propagations as f64 / stats.nodes.max(1) as f64);
print_board(&solution, n);
println!("");
}
None => {
println!("โ No solution found for {}-Queens", n);
}
}
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
}
println!("\n๐ Performance Testing:");
let performance_sizes = [10, 12, 20];
for &n in &performance_sizes {
println!("\n๐ Performance test: {}-Queens", n);
let start = Instant::now();
match solve_n_queens(n) {
Some((_, stats)) => {
let duration = start.elapsed();
println!("โ
Solved in {:.3}ms", duration.as_secs_f64() * 1000.0);
println!("๐ {} propagations, {} nodes, {:.1} prop/node",
stats.propagations, stats.nodes,
stats.propagations as f64 / stats.nodes.max(1) as f64);
}
None => {
println!("โ No solution found");
}
}
}
println!("\nโจ Summary:");
println!("N-Queens demonstrates AllDifferent optimization with:");
println!("โข Multiple AllDifferent constraints with different connectivity patterns");
println!("โข Row constraint: simple 1-to-1 variable mapping");
println!("โข Diagonal constraints: transformed variables with different connectivity");
println!("โข Universal optimization applies across all constraint types");
println!("โข ๐ฏ Perfect test case for validating general-purpose heuristics!");
}
struct SolverStats {
propagations: usize,
nodes: usize,
}
fn solve_n_queens(n: usize) -> Option<(Vec<i32>, SolverStats)> {
let mut model = Model::default();
let queen_rows = model.ints(n, 1, n as i32);
model.alldiff(&queen_rows.clone());
let ascending_diagonals: Vec<_> = queen_rows.iter().enumerate()
.map(|(i, &queen_row)| {
let col_offset = model.int(i as i32, i as i32);
model.add(queen_row, col_offset)
})
.collect();
model.alldiff(&ascending_diagonals);
let descending_diagonals: Vec<_> = queen_rows.iter().enumerate()
.map(|(i, &queen_row)| {
let col_offset = model.int(-(i as i32), -(i as i32));
model.add(queen_row, col_offset)
})
.collect();
model.alldiff(&descending_diagonals);
let solution = model.solve();
match solution {
Ok(sol) => {
let propagation_count = sol.stats.propagation_count;
let node_count = sol.stats.node_count;
let positions: Vec<i32> = queen_rows.iter()
.map(|&var| match sol[var] {
Val::ValI(row) => row,
_ => 0,
})
.collect();
let stats = SolverStats {
propagations: propagation_count,
nodes: node_count,
};
Some((positions, stats))
}
Err(_) => None,
}
}
fn print_board(solution: &[i32], n: usize) {
println!("\n๐ Solution:");
print!(" ");
for col in 1..=n {
print!("{:2} ", col);
}
println!();
for row in 1..=n {
print!("{:2} ", row);
for col in 0..n {
if solution[col] == row as i32 {
print!(" โ ");
} else {
print!(" ยท ");
}
}
println!();
}
print!("๐ฏ Queen positions: ");
for (col, &row) in solution.iter().enumerate() {
print!("C{}R{}", col + 1, row);
if col < solution.len() - 1 {
print!(", ");
}
}
println!();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_4_queens() {
let solution = solve_n_queens(4);
assert!(solution.is_some(), "4-Queens should have a solution");
if let Some((positions, _)) = solution {
assert_eq!(positions.len(), 4);
let mut rows = positions.clone();
rows.sort();
for i in 1..rows.len() {
assert_ne!(rows[i], rows[i-1], "Two queens in same row");
}
for i in 0..4 {
for j in i+1..4 {
assert_ne!(positions[i] + i as i32, positions[j] + j as i32,
"Two queens on same ascending diagonal");
assert_ne!(positions[i] - i as i32, positions[j] - j as i32,
"Two queens on same descending diagonal");
}
}
}
}
#[test]
fn test_8_queens() {
let solution = solve_n_queens(8);
assert!(solution.is_some(), "8-Queens should have a solution");
if let Some((_, stats)) = solution {
assert!(stats.propagations < 10000, "Too many propagations for 8-Queens");
assert!(stats.nodes < 1000, "Too many nodes for 8-Queens");
}
}
#[test]
fn test_optimization_effectiveness() {
let solution = solve_n_queens(10);
assert!(solution.is_some(), "10-Queens should have a solution");
if let Some((_, stats)) = solution {
println!("10-Queens stats: {} propagations, {} nodes", stats.propagations, stats.nodes);
assert!(stats.propagations < 50000, "Optimization may not be working - too many propagations");
assert!(stats.nodes < 5000, "Optimization may not be working - too many nodes");
}
}
}