1use crate::sat::clause_storage::LiteralStorage;
2use crate::sat::cnf::Cnf;
3use crate::sat::literal::Literal;
4use crate::sat::solver::Solutions;
5use std::collections::HashMap;
6use std::fmt::Display;
7use std::num::NonZeroI32;
8use std::path::PathBuf;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub enum Cell {
13 Unknown = 0,
15 Filled = 1,
17 Empty = 2,
19}
20
21impl Display for Cell {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 Cell::Empty => write!(f, "."),
25 Cell::Filled => write!(f, "#"),
26 Cell::Unknown => write!(f, " "),
27 }
28 }
29}
30
31type Constraint = Vec<u32>;
32type Size = usize;
33type Pattern = Vec<Cell>;
34
35#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct Nonogram {
38 rows: Vec<Constraint>,
39 cols: Vec<Constraint>,
40 solution: Vec<Vec<Cell>>,
41}
42
43impl Display for Nonogram {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 writeln!(f, "Nonogram:")?;
46 writeln!(f, "Rows: {:?}", self.rows)?;
47 writeln!(f, "Cols: {:?}", self.cols)?;
48
49 for row in &self.solution {
50 for cell in row {
51 write!(f, "{}", cell)?;
52 }
53 writeln!(f)?;
54 }
55 Ok(())
56 }
57}
58
59impl Nonogram {
60 pub fn new(rows: Vec<Constraint>, cols: Vec<Constraint>) -> Self {
62 let row_size = rows.len();
63 let col_size = cols.len();
64 let solution = vec![vec![Cell::Unknown; col_size]; row_size];
65 Nonogram {
66 rows,
67 cols,
68 solution,
69 }
70 }
71
72 pub fn num_rows(&self) -> usize {
74 self.rows.len()
75 }
76
77 pub fn num_cols(&self) -> usize {
79 self.cols.len()
80 }
81
82 pub fn decode(&self, assignment: &Solutions) -> Vec<Vec<Cell>> {
84 let mut solution = vec![vec![Cell::Unknown; self.num_cols()]; self.num_rows()];
85 let assignment_set: std::collections::HashSet<NonZeroI32> =
86 assignment.iter().cloned().collect();
87
88 for r in 0..self.num_rows() {
89 for c in 0..self.num_cols() {
90 let fill_var = Variable::new(r, c, Cell::Filled).encode(self);
91 let empty_var = Variable::new(r, c, Cell::Empty).encode(self);
92
93 let fill_var = NonZeroI32::new(fill_var as i32).unwrap();
94 let empty_var = NonZeroI32::new(empty_var as i32).unwrap();
95
96 if assignment_set.contains(&(fill_var)) {
97 solution[r][c] = Cell::Filled;
98 } else if assignment_set.contains(&(empty_var)) {
99 solution[r][c] = Cell::Empty;
100 }
101 }
102 }
103
104 solution
105 }
106
107 pub fn to_cnf<L: Literal, S: LiteralStorage<L>>(&self) -> Cnf<L, S> {
109 let num_vars_cell = self.num_rows() * self.num_cols() * 2;
110 let mut next_aux_var = (num_vars_cell + 1) as u32;
111
112 println!("Generating cell clauses...");
113 let cell_clauses = generate_cell_clauses(self);
114 println!("Generating cell unique clauses...");
115 let cell_unique_clauses = generate_cell_unique_clauses(self);
116
117 println!("Generating row clauses...");
118 let (row_clauses, next_aux_var_after_rows) =
119 generate_line_clauses(self, true, next_aux_var);
120 next_aux_var = next_aux_var_after_rows;
121 println!("Generating column clauses...");
122 let (col_clauses, _) = generate_line_clauses(self, false, next_aux_var);
123
124 println!("Combining clauses...");
125 let all_clauses: Vec<Vec<i32>> = cell_clauses
126 .into_iter()
127 .chain(cell_unique_clauses)
128 .chain(row_clauses)
129 .chain(col_clauses)
130 .collect();
131
132 println!(
133 "Generated {} clauses with {} variables.",
134 all_clauses.len(),
135 next_aux_var - 1
136 );
137 Cnf::new(all_clauses)
138 }
139}
140
141fn generate_cell_clauses(nonogram: &Nonogram) -> Vec<Vec<i32>> {
142 let mut clauses = Vec::new();
143 for r in 0..nonogram.num_rows() {
144 for c in 0..nonogram.num_cols() {
145 let fill_var = Variable::new(r, c, Cell::Filled).encode(nonogram);
146 let empty_var = Variable::new(r, c, Cell::Empty).encode(nonogram);
147
148 clauses.push(vec![-(fill_var as i32), -(empty_var as i32)]);
149 }
150 }
151 clauses
152}
153
154fn generate_cell_unique_clauses(nonogram: &Nonogram) -> Vec<Vec<i32>> {
155 let mut clauses = Vec::new();
156 for r in 0..nonogram.num_rows() {
157 for c in 0..nonogram.num_cols() {
158 let fill_var = Variable::new(r, c, Cell::Filled).encode(nonogram);
159 let empty_var = Variable::new(r, c, Cell::Empty).encode(nonogram);
160
161 clauses.push(vec![fill_var as i32, empty_var as i32]);
162 }
163 }
164 clauses
165}
166
167fn generate_line_clauses(
168 nonogram: &Nonogram,
169 is_row: bool,
170 mut next_aux_var: u32,
171) -> (Vec<Vec<i32>>, u32) {
172 let mut clauses = Vec::new();
173 let outer_loop_size = if is_row {
174 nonogram.num_rows()
175 } else {
176 nonogram.num_cols()
177 };
178 let line_size = if is_row {
179 nonogram.num_cols()
180 } else {
181 nonogram.num_rows()
182 };
183 let constraints_vec = if is_row {
184 &nonogram.rows
185 } else {
186 &nonogram.cols
187 };
188
189 let mut memo = HashMap::new();
190
191 for i in 0..outer_loop_size {
192 let constraint = &constraints_vec[i];
193 println!(
194 " Processing {} {}: Constraint {:?}, Size {}",
195 if is_row { "Row" } else { "Col" },
196 i,
197 constraint,
198 line_size
199 );
200
201 let possible_patterns = generate_possible_solutions_memo(line_size, constraint, &mut memo);
202 println!(
203 " Found {} possible patterns for {} {}",
204 possible_patterns.len(),
205 if is_row { "Row" } else { "Col" },
206 i
207 );
208
209 if possible_patterns.is_empty() {
210 println!(
211 "Warning: No possible patterns found for {} {} with constraints {:?}. Puzzle might be unsatisfiable.",
212 if is_row { "Row" } else { "Col" },
213 i,
214 constraint
215 );
216 clauses.push(vec![]);
217 continue;
218 }
219
220 let aux_vars: Vec<u32> = (0..possible_patterns.len())
221 .map(|_| {
222 let var = next_aux_var;
223 next_aux_var += 1;
224 var
225 })
226 .collect();
227
228 clauses.push(aux_vars.iter().map(|&v| v as i32).collect());
229
230 for j in 0..aux_vars.len() {
231 for k in (j + 1)..aux_vars.len() {
232 clauses.push(vec![-(aux_vars[j] as i32), -(aux_vars[k] as i32)]);
233 }
234 }
235
236 for (pattern_idx, pattern) in possible_patterns.iter().enumerate() {
237 let aux_var = aux_vars[pattern_idx];
238 for k in 0..line_size {
239 let cell_state = pattern[k];
240 let (r, c) = if is_row { (i, k) } else { (k, i) };
241 let cell_var = Variable::new(r, c, cell_state).encode(nonogram);
242
243 clauses.push(vec![-(aux_var as i32), cell_var as i32]);
244 }
245 }
246 }
247
248 (clauses, next_aux_var)
249}
250
251fn generate_possible_solutions_memo(
252 size: Size,
253 constraint: &Constraint,
254 memo: &mut HashMap<(Size, Constraint), Vec<Pattern>>,
255) -> Vec<Pattern> {
256 let key = (size, constraint.clone());
257 if let Some(cached) = memo.get(&key) {
258 return cached.clone();
259 }
260
261 let mut solutions = Vec::new();
262 generate_recursive(
263 size,
264 constraint,
265 0,
266 0,
267 &mut vec![Cell::Unknown; size],
268 &mut solutions,
269 );
270
271 memo.insert(key, solutions.clone());
272 solutions
273}
274
275fn generate_recursive(
276 size: Size,
277 constraints: &Constraint,
278 constraint_idx: usize,
279 start_pos: usize,
280 current_pattern: &mut Pattern,
281 solutions: &mut Vec<Pattern>,
282) {
283 if constraint_idx == constraints.len() {
284 for i in start_pos..size {
285 if current_pattern[i] == Cell::Unknown {
286 current_pattern[i] = Cell::Empty;
287 }
288 }
289 solutions.push(current_pattern.clone());
290 return;
291 }
292
293 let block_len = constraints[constraint_idx] as usize;
294 let remaining_constraints = &constraints[(constraint_idx + 1)..];
295 let min_len_for_remaining =
296 remaining_constraints.iter().sum::<u32>() as usize + remaining_constraints.len();
297
298 for pos in start_pos..=size {
299 let end_pos = pos + block_len;
300
301 if end_pos > size {
302 break;
303 }
304
305 let space_needed_after = if constraint_idx < constraints.len() - 1 {
306 min_len_for_remaining + 1
307 } else {
308 0
309 };
310 if end_pos + space_needed_after > size {
311 continue;
312 }
313
314 let mut possible = true;
315 for i in pos..end_pos {
316 if current_pattern[i] == Cell::Empty {
317 possible = false;
318 break;
319 }
320 }
321 if !possible {
322 continue;
323 }
324
325 if end_pos < size && current_pattern[end_pos] == Cell::Filled {
326 possible = false;
327 }
328 if !possible {
329 continue;
330 }
331
332 let original_pattern_slice: Vec<Cell> = current_pattern[pos..].to_vec();
333
334 for i in start_pos..pos {
335 if current_pattern[i] == Cell::Unknown {
336 current_pattern[i] = Cell::Empty;
337 } else if current_pattern[i] == Cell::Filled {
338 possible = false;
339 break;
340 }
341 }
342 if !possible {
343 current_pattern[start_pos..pos]
344 .copy_from_slice(&original_pattern_slice[(start_pos - pos)..0]);
345 continue;
346 }
347
348 for i in pos..end_pos {
349 current_pattern[i] = Cell::Filled;
350 }
351
352 let next_start_pos;
353 if end_pos < size {
354 current_pattern[end_pos] = Cell::Empty;
355 next_start_pos = end_pos + 1;
356 } else {
357 next_start_pos = end_pos;
358 }
359
360 generate_recursive(
361 size,
362 constraints,
363 constraint_idx + 1,
364 next_start_pos,
365 current_pattern,
366 solutions,
367 );
368
369 for i in pos..current_pattern
370 .len()
371 .min(pos + original_pattern_slice.len())
372 {
373 current_pattern[i] = original_pattern_slice[i - pos];
374 }
375 if end_pos < size {
376 if end_pos - pos < original_pattern_slice.len() {
377 current_pattern[end_pos] = original_pattern_slice[end_pos - pos];
378 } else {
379 current_pattern[end_pos] = Cell::Unknown;
380 }
381 }
382 }
383}
384
385#[derive(Clone, Debug, PartialEq, Eq, Hash)]
386struct Variable {
387 row: usize,
389 col: usize,
391 fill: Cell,
393}
394
395impl Variable {
396 fn new(row: usize, col: usize, fill: Cell) -> Self {
399 assert!(
400 fill == Cell::Filled || fill == Cell::Empty,
401 "Variable must represent Filled or Empty state"
402 );
403 Variable { row, col, fill }
404 }
405
406 fn encode(&self, nonogram: &Nonogram) -> u32 {
409 let num_cols = nonogram.num_cols();
410 let base = (self.row * num_cols + self.col) * 2;
411 (base + self.fill as usize) as u32
412 }
413}
414
415pub fn parse_nonogram(input: &str) -> Result<Nonogram, String> {
425 let mut lines = input.lines().map(str::trim).filter(|l| !l.is_empty());
426
427 let mut num_rows: Option<usize> = None;
428 let mut num_cols: Option<usize> = None;
429
430 if let Some(line) = lines.next() {
431 if line.starts_with("rows ") {
432 num_rows = line.split_whitespace().nth(1).and_then(|s| s.parse().ok());
433 } else {
434 return Err("Expected 'rows <num>' format".to_string());
435 }
436 } else {
437 return Err("Missing 'rows' line".to_string());
438 }
439
440 if let Some(line) = lines.next() {
441 if line.starts_with("cols ") {
442 num_cols = line.split_whitespace().nth(1).and_then(|s| s.parse().ok());
443 } else {
444 return Err("Expected 'cols <num>' format".to_string());
445 }
446 } else {
447 return Err("Missing 'cols' line".to_string());
448 }
449
450 let num_rows = num_rows.ok_or("Invalid number for rows")?;
451 let num_cols = num_cols.ok_or("Invalid number for cols")?;
452
453 if num_rows == 0 || num_cols == 0 {
454 return Err("Rows and columns must be positive".to_string());
455 }
456
457 let mut rows = Vec::with_capacity(num_rows);
458 for i in 0..num_rows {
459 let line = lines
460 .next()
461 .ok_or(format!("Missing row constraint line {}", i + 1))?;
462 let constraint: Constraint = line
463 .split_whitespace()
464 .map(|s| s.parse::<u32>())
465 .collect::<Result<_, _>>()
466 .map_err(|e| format!("Invalid number in row constraint {}: {}", i + 1, e))?;
467 if constraint.is_empty() || (constraint.len() == 1 && constraint[0] == 0) {
468 rows.push(vec![]);
469 } else {
470 rows.push(constraint);
471 }
472 }
473
474 let mut cols = Vec::with_capacity(num_cols);
475 for i in 0..num_cols {
476 let line = lines
477 .next()
478 .ok_or(format!("Missing column constraint line {}", i + 1))?;
479 let constraint: Constraint = line
480 .split_whitespace()
481 .map(|s| s.parse::<u32>())
482 .collect::<Result<_, _>>()
483 .map_err(|e| format!("Invalid number in column constraint {}: {}", i + 1, e))?;
484 if constraint.is_empty() || (constraint.len() == 1 && constraint[0] == 0) {
485 cols.push(vec![]);
486 } else {
487 cols.push(constraint);
488 }
489 }
490
491 if lines.next().is_some() {
492 return Err("Extra lines found after column constraints".to_string());
493 }
494
495 Ok(Nonogram::new(rows, cols))
496}
497
498pub fn parse_nonogram_file(file_path: &PathBuf) -> Result<Nonogram, String> {
500 let content = std::fs::read_to_string(file_path)
501 .map_err(|e| format!("Failed to read file '{}': {}", file_path.display(), e))?;
502 parse_nonogram(&content)
503}