]> git.itanic.dy.fi Git - sudoku/blob - sudoku.cpp
generator: Print all found sudokus in oneline format
[sudoku] / sudoku.cpp
1 #include <iostream>
2 #include <string>
3
4 #include "sudoku.h"
5 #include "debug.h"
6 #include "random.h"
7
8 #define EMPTY ' '
9
10 int verbose = 1;
11
12 sudoku::sudoku()
13 {
14         int x,y;
15
16         for (y = 0; y < 9; y++)
17                 for (x = 0; x < 9; x++)
18                         table[x][y] = EMPTY;
19
20         recursion_depth = 0;
21         guesses = 0;
22 }
23
24 sudoku::~sudoku()
25 {
26 }
27
28 static int check_validity(const int col, const int row)
29 {
30         if (col < 0 || col >= 9) {
31                 DPRINT << "Invalid column: " << col << std::endl;
32                 return 0;
33         }
34
35         if (row < 0 || row >= 9) {
36                 DPRINT << "Invalid row: " << row << std::endl;
37                 return 0;
38         }
39         return 1;
40 }
41
42 char sudoku::get(const int col, const int row)
43 {
44         if (!check_validity(row, col)) {
45                 DPRINT << "over here\n";
46                 return 0;
47         }
48
49         return table[col][row];
50 }
51
52 int sudoku::set(const int col, const int row, const char val)
53 {
54         std::string str;
55
56         if (!check_validity(col, row)) {
57                 DPRINT << "over here\n";
58                 return -1;
59         }
60
61         if (table[col][row] != EMPTY) {
62                 DPRINT << "Assigning value to a non-empty slot\n";
63                 return -1;
64         }
65
66         str = get_col_contents(col);
67         if (str.find(val, 0) != std::string::npos) {
68                 DPRINT << "Illegal assignment " << col << "." << row << ": "
69                        << val << std::endl;
70                 return -1;
71         }
72
73         str = get_row_contents(row);
74         if (str.find(val, 0) != std::string::npos) {
75                 DPRINT << "Illegal assignment " << col << "." << row << ": "
76                        << val << std::endl;
77                 return -1;
78         }
79
80         str = get_block_contents(col, row);
81         if (str.find(val, 0) != std::string::npos) {
82                 DPRINT << "Illegal assignment " << col << "." << row << ": "
83                        << val << std::endl;
84                 return -1;
85         }
86
87         table[col][row] = val;
88
89         return 0;
90 }
91
92 int sudoku::str_to_row(const int row, const std::string &str)
93 {
94         unsigned int i, col = 0;
95         char chr;
96
97         if (row < 0 || row >= 9) {
98                 DPRINT << "Illegal row " << row << std::endl;
99                 return -1;
100         }
101
102         for (i = 0; i < str.length(); i++) {
103                 chr = str.at(i);
104
105                 if ((chr == '?') ||
106                     (chr == '_') ||
107                     (chr == '.')){
108                         col++;
109                         continue;
110                 }
111
112                 /* Ignore non-numbers */
113                 if (chr < '1' || chr > '9')
114                         continue;
115
116                 set(col++, row, chr);
117                 if (col >= 9)
118                         break;
119         }
120         return 0;
121 }
122
123 void sudoku::print(void)
124 {
125         int x, y;
126
127         printf("+-------+------+--------+\n");
128         for (y = 0; y < 9; y++) {
129                 printf("| ");
130                 for (x = 0; x < 9; x++) {
131                         printf("%c ", table[x][y]);
132                         if (!((x  + 1)% 3))
133                                 printf("| ");
134                 }
135                 printf("\n");
136                 if (!((y + 1) % 3))
137                         printf("+-------+-------+-------+\n");
138         }
139 }
140
141 void sudoku::print_compact(void)
142 {
143         int x, y, i = 0;
144         char str[82];
145         str[81] = 0;
146
147         for (y = 0; y < 9; y++)
148                 for (x = 0; x < 9; x++, i++)
149                         str[i] = table[x][y] != EMPTY ? table[x][y] : '.';
150
151         printf("%s\n", str);
152 }
153
154 void sudoku::clone_to(sudoku &to)
155 {
156         sudoku clone;
157         int x, y;
158
159         for (x = 0; x < 9; x++)
160                 for (y = 0; y < 9; y++)
161                         to.table[x][y] = table[x][y];
162 }
163
164 std::string sudoku::get_row_contents(int row)
165 {
166         std::string s;
167         int col;
168
169         for (col = 0; col < 9; col++) {
170                 if (table[col][row] != EMPTY)
171                         s += table[col][row];
172         }
173
174         return s;
175 }
176
177 std::string sudoku::get_col_contents(int col)
178 {
179         std::string s;
180         int row;
181
182         for (row = 0; row < 9; row++) {
183                 if (table[col][row] != EMPTY)
184                         s += table[col][row];
185         }
186
187         return s;
188 }
189
190 std::string sudoku::get_block_contents(int col, int row)
191 {
192         std::string s;
193         int x, y;
194         char c;
195
196         if (col < 0 || col >= 9)
197                 DPRINT "Aiee " << col << std::endl;
198         if (row < 0 || row >= 9)
199                 DPRINT "Aiee " << row << std::endl;
200
201         col = (col /= 3) * 3;
202         row = (row /= 3) * 3;
203
204         for (y = 0; y < 3; y++)
205                 for (x = 0; x < 3; x++) {
206                         c = get(x + col, y + row);
207                         if (c != EMPTY)
208                                 s += c;
209                 }
210
211         return s;
212 }
213
214 static std::string exclude_common(std::string s1, std::string s2)
215 {
216         std::string::iterator a, b;
217
218 restart:
219         for (a = s1.begin(); a != s1.end(); a++) {
220                 for (b = s2.begin(); b != s2.end(); b++) {
221                         if (*a == *b) {
222                                 a = s1.erase(a);
223                                 b = s2.erase(b);
224                                 goto restart;
225                         }
226                 }
227         }
228         return s1;
229 }
230
231 std::string sudoku::get_legal_numbers(int col, int row)
232 {
233         char val;
234         std::string a, b, c, ret;
235
236         if (!check_validity(col, row)) {
237                 DPRINT << "over here\n";
238                 return NULL;
239         }
240
241         val = get(col, row);
242         if (val != EMPTY) {
243                 return "0";
244         }
245
246 //      std::cout << "[" << col << "." << row << "] ";
247
248         a = get_col_contents(col);
249 //      std::cout << " col " << a;
250
251         b = get_row_contents(row);
252 //      std::cout << " row " << b;
253
254         c = get_block_contents(col, row);
255 //      std::cout << " block " << c;
256
257         ret = exclude_common("123456789", a);
258         ret = exclude_common(ret, b);
259         ret = exclude_common(ret, c);
260
261 //      std::cout << " legal " << ret << std::endl;
262         return ret;
263 }
264
265 int sudoku::fill_missing()
266 {
267         int x, y, solved = 0;
268         std::string num;
269
270         for (y = 0; y < 9; y++)
271                 for (x = 0; x < 9; x++) {
272                                 num = get_legal_numbers(x, y);
273                                 if ((num != "0") && (num.length() == 1)) {
274                                         set(x, y, num[0]);
275                                         solved++;
276                         }
277                 }
278         if (verbose) {
279                 //std::cout << "Solved " << solved << " numbers\n";
280         }
281
282         return solved;
283 }
284
285 int sudoku::get_best_guess(int &col, int &row)
286 {
287         int x, y;
288         int min = 0, filled = 0, illegals = 0;
289         std::string numbers;
290
291         /*
292          * Initialize row and col with illegal values to spot possible
293          * misuse
294          */
295         col = -1;
296         row = -1;
297
298         for (y = 0; (y < 9) && !illegals; y++)
299                 for (x = 0; x < 9; x++) {
300                         numbers = get_legal_numbers(x, y);
301
302                         if (numbers == "0") {
303                                 filled++;
304                                 continue;
305                         }
306
307                         if (numbers.length() == 0) {
308                                 illegals++;
309                                 break;
310                         }
311
312                         if (!min) {
313                                 min = numbers.length();
314                                 col = x;
315                                 row = y;
316                                 continue;
317                         }
318
319                         if ((int)numbers.length() < min) {
320                                 min = numbers.length();
321                                 col = x;
322                                 row = y;
323                                 if (min == 1)
324                                         return min;
325                         }
326 //                      std::cout <<  x << "." << y << ": " << " min " 
327 //                                << numbers << col << "." << row << std::endl;
328                 }
329
330         if ((!min && !filled) || illegals) {
331 //              DPRINT << "Is this sudoku has no solutions?\n";
332                 return -1;
333         }
334
335         if (!min && (filled == 99)) {
336                 DPRINT << "This sudoku is solved\n";
337                 return 0;
338         }
339
340         return min;
341 }
342
343 std::vector<sudoku> sudoku::solve_all()
344 {
345         int x, y, i, j, ret;
346         std::string str;
347         std::vector<sudoku> sudokus;
348
349         // Fill in as much as possible
350         do {
351                 if (verbose) {
352                         std::cout << std::endl;
353                         print();
354                 }
355         } while (fill_missing() != 0);
356
357         ret = get_best_guess(x, y);
358
359         // Did we solve it yet?
360         switch (ret) {
361         case -1:
362                 // Unsolvable
363                 if (verbose) {
364                         std::cout << "This is unsolvable, recursion: " 
365                                   << recursion_depth << std::endl;
366                 }
367                 return sudokus;
368         case 0:
369                 // Solved
370                 if (verbose) {
371                         std::cout << "Solution found , recursion: " 
372                                   << recursion_depth << std::endl;
373                 }
374                 sudokus.push_back(*this);
375                 if (verbose)
376                         print();
377                 return sudokus;
378         }
379
380         if (verbose) {
381                 std::cout << "Not solved yet, guessing at depth "
382                           << recursion_depth << std::endl;
383         }
384
385         // Try guessim something
386         str = get_legal_numbers(x, y);
387         for (i = 0; i < (int)str.length(); i++) {
388                 std::vector<sudoku> results;
389
390                 sudoku guess = *this;
391                 guesses++;
392
393                 if (verbose) {
394                         std::cout << "Guessing number " << str[i] << " to "
395                                   << x << "." << y << std::endl;
396                 }
397
398                 guess.set(x, y, str[i]);
399                 guess.recursion_depth = recursion_depth + 1;
400                 guess.guesses = 0;
401
402                 results = guess.solve_all();
403                 guesses += guess.guesses;
404                 
405                 //Did it solve?
406                 if (!results.empty()) {
407 /*                      printf("Got %d solutions, guessed %d: %s\n",
408                                (int)results.size(), (int)str.size(),
409                                str.c_str());*/
410
411                         for(j = 0;j < (int)results.size(); j++) {
412                                 sudokus.push_back(results.at(j));
413                                 guesses += results.at(j).guesses;
414                         }
415                 }
416         }
417
418         if (verbose)
419                 std::cout << "Guesses: " << guesses << std::endl;
420
421         // Return what was solved
422         return sudokus;
423 }
424
425 sudoku sudoku::fill_with_random(int &solvable)
426 {
427         int x, y, i, ret;
428         std::string str;
429         sudoku empty;
430
431         // Fill in as much as possible
432         do {
433                 if (verbose) {
434                         //std::cout << std::endl;
435                         //print();
436                 }
437         } while (fill_missing() != 0);
438
439         ret = get_best_guess(x, y);
440
441         // Did we fill it yet?
442         switch (ret) {
443         case -1:
444                 // Unsolvable
445                 solvable = 0;
446                 if (verbose) {
447                         //std::cout << "This is unsolvable, recursion: "
448                         //  << recursion_depth << std::endl;
449                 }
450                 return empty;
451         case 0:
452                 // Solved
453                 solvable = 1;
454                 if (verbose) {
455                         //std::cout << "Solution found , recursion: "
456                         //  << recursion_depth << std::endl;
457                 }
458                 return *this;
459         }
460
461         if (verbose) {
462                 //std::cout << "Not solved yet, guessing at depth "
463                 //  << recursion_depth << std::endl;
464         }
465
466         // Try filling with something
467         str = get_legal_numbers(x, y);
468         while (str.length()) {
469                 i = get_random() % str.length();
470
471                 sudoku guess = *this;
472                 guess.set(x, y, str[i]);
473
474                 if (verbose) {
475                         //std::cout << "Trying number " << str[i] << " to "
476                         //  << x << "." << y << std::endl;
477                 }
478
479                 guess.recursion_depth = recursion_depth + 1;
480
481                 int can_solve;
482                 guess = guess.fill_with_random(can_solve);
483
484                 //Did it solve?
485                 if (can_solve) {
486                         solvable = 1;
487                         return guess;
488                 }
489
490                 str.erase(i,1);
491         }
492
493         // It didn't solve
494         solvable = 0;
495         return empty;
496 }
497
498 int sudoku::used_numbers()
499 {
500         int x, y, count = 0;
501         for (x = 0; x < 9; x++)
502                 for (y = 0; y < 9; y++)
503                         if (table[x][y] != EMPTY)
504                                 count++;
505         return count;
506 }
507
508 static int done = 0;
509
510 sudoku sudoku::remove_randomly(int min_guesses, int depth)
511 {
512         int count = used_numbers();
513         sudoku result;
514         int stop = 0;
515
516 #pragma omp parallel for firstprivate(stop)
517         for (int i = 0; i < count; i++) {
518                 int x, y;
519                 sudoku tmp = *this, tmp2;
520                 std::vector<sudoku> results;
521
522                 if (done || stop)
523                         continue;
524
525                 /* remove a number */
526                 do {
527                         x = get_random() % 9;
528                         y = get_random() % 9;
529                 } while(tmp.get(x,y) == EMPTY);
530                 tmp.table[x][y] = EMPTY;
531
532                 //printf("Got now:\n");
533                 //tmp.print();
534
535                 tmp2 = tmp;
536                 /* solve it */
537                 results = tmp.solve_all();
538
539                 /* Does it still have only one solution */
540                 if (results.size() == 1 && !done) {
541                         /* but not hard enough yet? */
542                         if (tmp.guesses >= min_guesses) {
543                                 /* It's good now */
544                                 printf("%d, Good!\n", depth);
545                                 result = tmp;
546                                 done = 1;
547                                 #pragma omp flush(done)
548                         } else {
549                                 printf("%2d, got only %3d guesses ", depth,
550                                        tmp.guesses);
551                                 tmp.print_compact();
552                         }
553
554                         if (!done) {
555                                 tmp = tmp2;
556                                 tmp2 = tmp.remove_randomly(min_guesses,
557                                                    depth + 1);
558
559                                 /* test for emptiness */
560                                 if (tmp2.guesses == 0) {
561                                         continue;
562                                 }
563
564                                 if (tmp2.guesses >= min_guesses) {
565                                         result = tmp2;
566                                         done = 1;
567                                         #pragma omp flush(done)
568                                 } else {
569                                         printf("%d: Guesses: %d, "
570                                                "not good enough\n",
571                                                depth, tmp2.guesses);
572                                 }
573                                 continue;
574                         }
575                 }
576
577                 if ((results.size() > 1) && !done) {
578                         /* oops, removed too many */
579                         sudoku empty;
580                         //printf("%d, Got multiple results\n", depth);
581                         result = empty;
582                         stop = 1;
583                 }
584
585                 if (results.empty() && !done) {
586                         fprintf(stderr, "%d, Fail. This can't happen?\n",
587                                 depth);
588                         sudoku empty;
589                         result = empty;
590                 }
591         }
592         return result;
593 }
594
595 int sudoku::generate(int min_guesses)
596 {
597         int solvable;
598         sudoku tmp = fill_with_random(solvable);
599
600         if (!solvable)
601                 return -1;
602
603         /* Right, we got it filled */
604         tmp = tmp.remove_randomly(min_guesses, 0);
605
606         *this = tmp;
607
608         return solvable;
609 }