From f2f5b3519a837e26e482a54a691e8185edf68649 Mon Sep 17 00:00:00 2001 From: Apress Date: Wed, 19 Oct 2016 13:39:45 +0100 Subject: [PATCH] First commit --- 9781484209967.jpg | Bin 0 -> 12251 bytes LICENSE.txt | 27 + README.md | 15 + contributing.md | 14 + sources/Generator/brute.c | 69 +++ sources/Generator/brute.h | 21 + sources/Generator/brute_comp.c | 52 ++ sources/Generator/brute_comp.h | 26 + sources/Generator/count_solved.c | 19 + sources/Generator/count_solved.h | 13 + sources/Generator/def.h | 37 ++ sources/Generator/display.c | 42 ++ sources/Generator/display.h | 13 + sources/Generator/display_string.c | 19 + sources/Generator/display_string.h | 13 + sources/Generator/fill.c | 38 ++ sources/Generator/fill.h | 15 + sources/Generator/fill_digit.c | 81 +++ sources/Generator/fill_digit.h | 15 + sources/Generator/in_box.c | 14 + sources/Generator/in_box.h | 13 + sources/Generator/inconsistent_grid.c | 24 + sources/Generator/inconsistent_grid.h | 14 + sources/Generator/inconsistent_unit.c | 28 + sources/Generator/inconsistent_unit.h | 15 + sources/Generator/init.c | 25 + sources/Generator/init.h | 13 + sources/Generator/list_solved.c | 27 + sources/Generator/list_solved.h | 15 + sources/Generator/multi_html.c | 195 ++++++ sources/Generator/multi_html.h | 23 + sources/Generator/save_html.c | 88 +++ sources/Generator/save_html.h | 13 + sources/Generator/sudoku_gen.c | 749 +++++++++++++++++++++++ sources/Solver/backtrack.c | 128 ++++ sources/Solver/backtrack.h | 15 + sources/Solver/box_line.c | 26 + sources/Solver/box_line.h | 13 + sources/Solver/box_line_unit.c | 75 +++ sources/Solver/box_line_unit.h | 13 + sources/Solver/cleanup.c | 24 + sources/Solver/cleanup.h | 13 + sources/Solver/cleanup_around.c | 22 + sources/Solver/cleanup_around.h | 13 + sources/Solver/cleanup_unit.c | 37 ++ sources/Solver/cleanup_unit.h | 13 + sources/Solver/count_candidates.c | 25 + sources/Solver/count_candidates.h | 14 + sources/Solver/count_solved.c | 25 + sources/Solver/count_solved.h | 13 + sources/Solver/def.h | 83 +++ sources/Solver/display.c | 50 ++ sources/Solver/display.h | 13 + sources/Solver/display_strats_in_clear.c | 18 + sources/Solver/display_strats_in_clear.h | 13 + sources/Solver/display_string.c | 27 + sources/Solver/display_string.h | 13 + sources/Solver/execute_strategies.c | 55 ++ sources/Solver/execute_strategies.h | 13 + sources/Solver/footprint.c | 43 ++ sources/Solver/footprint.h | 33 + sources/Solver/hidden_pair.c | 29 + sources/Solver/hidden_pair.h | 15 + sources/Solver/hidden_pair_unit.c | 77 +++ sources/Solver/hidden_pair_unit.h | 15 + sources/Solver/hidden_triple.c | 29 + sources/Solver/hidden_triple.h | 15 + sources/Solver/hidden_triple_unit.c | 92 +++ sources/Solver/hidden_triple_unit.h | 15 + sources/Solver/inconsistent_grid.c | 24 + sources/Solver/inconsistent_grid.h | 14 + sources/Solver/inconsistent_unit.c | 40 ++ sources/Solver/inconsistent_unit.h | 15 + sources/Solver/init.c | 66 ++ sources/Solver/init.h | 15 + sources/Solver/intersection.c | 72 +++ sources/Solver/intersection.h | 38 ++ sources/Solver/keep_going.c | 13 + sources/Solver/keep_going.h | 14 + sources/Solver/lines.c | 93 +++ sources/Solver/lines.h | 15 + sources/Solver/lines_2.c | 35 ++ sources/Solver/lines_2.h | 15 + sources/Solver/lines_3.c | 38 ++ sources/Solver/lines_3.h | 15 + sources/Solver/lines_4.c | 41 ++ sources/Solver/lines_4.h | 15 + sources/Solver/naked_pair.c | 29 + sources/Solver/naked_pair.h | 15 + sources/Solver/naked_pair_unit.c | 96 +++ sources/Solver/naked_pair_unit.h | 15 + sources/Solver/naked_quad.c | 29 + sources/Solver/naked_quad.h | 15 + sources/Solver/naked_quad_unit.c | 125 ++++ sources/Solver/naked_quad_unit.h | 15 + sources/Solver/naked_triple.c | 29 + sources/Solver/naked_triple.h | 15 + sources/Solver/naked_triple_unit.c | 114 ++++ sources/Solver/naked_triple_unit.h | 15 + sources/Solver/pairs_data.c | 9 + sources/Solver/pairs_data.h | 14 + sources/Solver/pairs_find.c | 142 +++++ sources/Solver/pairs_find.h | 15 + sources/Solver/pointing_line.c | 24 + sources/Solver/pointing_line.h | 16 + sources/Solver/pointing_line_box.c | 87 +++ sources/Solver/pointing_line_box.h | 15 + sources/Solver/rectangle.c | 30 + sources/Solver/rectangle.h | 15 + sources/Solver/rectangle_cell.c | 59 ++ sources/Solver/rectangle_cell.h | 15 + sources/Solver/rectangle_pattern.c | 58 ++ sources/Solver/rectangle_pattern.h | 17 + sources/Solver/rectangle_step.c | 92 +++ sources/Solver/rectangle_step.h | 17 + sources/Solver/remove_candidate.c | 25 + sources/Solver/remove_candidate.h | 15 + sources/Solver/solve.c | 39 ++ sources/Solver/solve.h | 13 + sources/Solver/sudoku_solver.c | 238 +++++++ sources/Solver/unique.c | 26 + sources/Solver/unique.h | 15 + sources/Solver/unique_loop.c | 26 + sources/Solver/unique_loop.h | 15 + sources/Solver/unique_unit.c | 65 ++ sources/Solver/unique_unit.h | 15 + sources/Solver/xy_chain.c | 21 + sources/Solver/xy_chain.h | 15 + sources/Solver/xy_chain_digit.c | 54 ++ sources/Solver/xy_chain_digit.h | 15 + sources/Solver/xy_chain_step.c | 153 +++++ sources/Solver/xy_chain_step.h | 24 + sources/Solver/y_wing.c | 21 + sources/Solver/y_wing.h | 15 + sources/Solver/y_wing_digit.c | 139 +++++ sources/Solver/y_wing_digit.h | 15 + 136 files changed, 5581 insertions(+) create mode 100644 9781484209967.jpg create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 contributing.md create mode 100644 sources/Generator/brute.c create mode 100644 sources/Generator/brute.h create mode 100644 sources/Generator/brute_comp.c create mode 100644 sources/Generator/brute_comp.h create mode 100644 sources/Generator/count_solved.c create mode 100644 sources/Generator/count_solved.h create mode 100644 sources/Generator/def.h create mode 100644 sources/Generator/display.c create mode 100644 sources/Generator/display.h create mode 100644 sources/Generator/display_string.c create mode 100644 sources/Generator/display_string.h create mode 100644 sources/Generator/fill.c create mode 100644 sources/Generator/fill.h create mode 100644 sources/Generator/fill_digit.c create mode 100644 sources/Generator/fill_digit.h create mode 100644 sources/Generator/in_box.c create mode 100644 sources/Generator/in_box.h create mode 100644 sources/Generator/inconsistent_grid.c create mode 100644 sources/Generator/inconsistent_grid.h create mode 100644 sources/Generator/inconsistent_unit.c create mode 100644 sources/Generator/inconsistent_unit.h create mode 100644 sources/Generator/init.c create mode 100644 sources/Generator/init.h create mode 100644 sources/Generator/list_solved.c create mode 100644 sources/Generator/list_solved.h create mode 100644 sources/Generator/multi_html.c create mode 100644 sources/Generator/multi_html.h create mode 100644 sources/Generator/save_html.c create mode 100644 sources/Generator/save_html.h create mode 100644 sources/Generator/sudoku_gen.c create mode 100644 sources/Solver/backtrack.c create mode 100644 sources/Solver/backtrack.h create mode 100644 sources/Solver/box_line.c create mode 100644 sources/Solver/box_line.h create mode 100644 sources/Solver/box_line_unit.c create mode 100644 sources/Solver/box_line_unit.h create mode 100644 sources/Solver/cleanup.c create mode 100644 sources/Solver/cleanup.h create mode 100644 sources/Solver/cleanup_around.c create mode 100644 sources/Solver/cleanup_around.h create mode 100644 sources/Solver/cleanup_unit.c create mode 100644 sources/Solver/cleanup_unit.h create mode 100644 sources/Solver/count_candidates.c create mode 100644 sources/Solver/count_candidates.h create mode 100644 sources/Solver/count_solved.c create mode 100644 sources/Solver/count_solved.h create mode 100644 sources/Solver/def.h create mode 100644 sources/Solver/display.c create mode 100644 sources/Solver/display.h create mode 100644 sources/Solver/display_strats_in_clear.c create mode 100644 sources/Solver/display_strats_in_clear.h create mode 100644 sources/Solver/display_string.c create mode 100644 sources/Solver/display_string.h create mode 100644 sources/Solver/execute_strategies.c create mode 100644 sources/Solver/execute_strategies.h create mode 100644 sources/Solver/footprint.c create mode 100644 sources/Solver/footprint.h create mode 100644 sources/Solver/hidden_pair.c create mode 100644 sources/Solver/hidden_pair.h create mode 100644 sources/Solver/hidden_pair_unit.c create mode 100644 sources/Solver/hidden_pair_unit.h create mode 100644 sources/Solver/hidden_triple.c create mode 100644 sources/Solver/hidden_triple.h create mode 100644 sources/Solver/hidden_triple_unit.c create mode 100644 sources/Solver/hidden_triple_unit.h create mode 100644 sources/Solver/inconsistent_grid.c create mode 100644 sources/Solver/inconsistent_grid.h create mode 100644 sources/Solver/inconsistent_unit.c create mode 100644 sources/Solver/inconsistent_unit.h create mode 100644 sources/Solver/init.c create mode 100644 sources/Solver/init.h create mode 100644 sources/Solver/intersection.c create mode 100644 sources/Solver/intersection.h create mode 100644 sources/Solver/keep_going.c create mode 100644 sources/Solver/keep_going.h create mode 100644 sources/Solver/lines.c create mode 100644 sources/Solver/lines.h create mode 100644 sources/Solver/lines_2.c create mode 100644 sources/Solver/lines_2.h create mode 100644 sources/Solver/lines_3.c create mode 100644 sources/Solver/lines_3.h create mode 100644 sources/Solver/lines_4.c create mode 100644 sources/Solver/lines_4.h create mode 100644 sources/Solver/naked_pair.c create mode 100644 sources/Solver/naked_pair.h create mode 100644 sources/Solver/naked_pair_unit.c create mode 100644 sources/Solver/naked_pair_unit.h create mode 100644 sources/Solver/naked_quad.c create mode 100644 sources/Solver/naked_quad.h create mode 100644 sources/Solver/naked_quad_unit.c create mode 100644 sources/Solver/naked_quad_unit.h create mode 100644 sources/Solver/naked_triple.c create mode 100644 sources/Solver/naked_triple.h create mode 100644 sources/Solver/naked_triple_unit.c create mode 100644 sources/Solver/naked_triple_unit.h create mode 100644 sources/Solver/pairs_data.c create mode 100644 sources/Solver/pairs_data.h create mode 100644 sources/Solver/pairs_find.c create mode 100644 sources/Solver/pairs_find.h create mode 100644 sources/Solver/pointing_line.c create mode 100644 sources/Solver/pointing_line.h create mode 100644 sources/Solver/pointing_line_box.c create mode 100644 sources/Solver/pointing_line_box.h create mode 100644 sources/Solver/rectangle.c create mode 100644 sources/Solver/rectangle.h create mode 100644 sources/Solver/rectangle_cell.c create mode 100644 sources/Solver/rectangle_cell.h create mode 100644 sources/Solver/rectangle_pattern.c create mode 100644 sources/Solver/rectangle_pattern.h create mode 100644 sources/Solver/rectangle_step.c create mode 100644 sources/Solver/rectangle_step.h create mode 100644 sources/Solver/remove_candidate.c create mode 100644 sources/Solver/remove_candidate.h create mode 100644 sources/Solver/solve.c create mode 100644 sources/Solver/solve.h create mode 100644 sources/Solver/sudoku_solver.c create mode 100644 sources/Solver/unique.c create mode 100644 sources/Solver/unique.h create mode 100644 sources/Solver/unique_loop.c create mode 100644 sources/Solver/unique_loop.h create mode 100644 sources/Solver/unique_unit.c create mode 100644 sources/Solver/unique_unit.h create mode 100644 sources/Solver/xy_chain.c create mode 100644 sources/Solver/xy_chain.h create mode 100644 sources/Solver/xy_chain_digit.c create mode 100644 sources/Solver/xy_chain_digit.h create mode 100644 sources/Solver/xy_chain_step.c create mode 100644 sources/Solver/xy_chain_step.h create mode 100644 sources/Solver/y_wing.c create mode 100644 sources/Solver/y_wing.h create mode 100644 sources/Solver/y_wing_digit.c create mode 100644 sources/Solver/y_wing_digit.h diff --git a/9781484209967.jpg b/9781484209967.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ae436664876eecf5ac222a64a04f2b8573290cc GIT binary patch literal 12251 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 +#include +#include +#include "brute.h" +#include "def.h" +#include "inconsistent_unit.h" + +int brute() { + int result = BRUTE_SUCCESSFUL; + unsigned long start_time = clock()/CLOCKS_PER_SEC; + unsigned long this_time; + char initial[9][9]; + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + initial[k][j] = grid[k][j]; + } + } + int k = 0; + int j = 0; + do { + do { + if (initial[k][j] == 0) { + int i = grid[k][j] + 1; + if (i > 9) { + grid[k][j] = 0; + do { + do { j--; } while (j >= 0 && initial[k][j] != 0); + if (j < 0) { + k--; + if (k < 0) { + result = BRUTE_IMPOSSIBLE; + goto done; //==> + } + j = 8; + } + } while (initial[k][j] != 0); + } // if (i.. + else { + grid[k][j] = i; + int kB = k/3*3+j/3; + if ( !inconsistent_unit("row", k, row[k]) + && !inconsistent_unit("column", j, col[j]) + && !inconsistent_unit("box", kB, box[kB]) + ) { + j++; + } + } // if (i.. else + } // if (initial[k][j].. + else { + j++; + } + this_time = clock()/CLOCKS_PER_SEC; + if (this_time - start_time > BRUTE_MAX_TIME) { + result = BRUTE_TIMEOUT; + goto done; //==> + } + } while (j < 9); + k++; + j = 0; + } while (k < 9); + +done: + return result; + } diff --git a/sources/Generator/brute.h b/sources/Generator/brute.h new file mode 100644 index 0000000..03b63d6 --- /dev/null +++ b/sources/Generator/brute.h @@ -0,0 +1,21 @@ +/* brute.h + * + * Solves a Sudoku by brute force + * + * See below for the return codes + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef BRUTE +#define BRUTE + +#define BRUTE_SUCCESSFUL 0 +#define BRUTE_IMPOSSIBLE -1 +#define BRUTE_TIMEOUT -2 + +#define BRUTE_MAX_TIME 10 + +int brute(void); + +#endif diff --git a/sources/Generator/brute_comp.c b/sources/Generator/brute_comp.c new file mode 100644 index 0000000..c9795df --- /dev/null +++ b/sources/Generator/brute_comp.c @@ -0,0 +1,52 @@ +/* brute_comp.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include +#include "brute.h" +#include "brute_comp.h" +#include "def.h" +#include "inconsistent_grid.h" + +// This messages in clear must match the codes defined in brute_comp.h +const char *brute_comp_err[] = { + /* 0 */ "same as reference", + /* 1 */ "different from reference", + /* 2 */ "timeout", + /* 3 */ "grid inconsistent", + /* 4 */ "puzzle impossible", + /* 5 */ "unknown result" + }; + +int brute_comp() { + int result = BRUTE_COMP_PROBLEM; + int brute_result = brute(); + switch (brute_result) { + + case BRUTE_SUCCESSFUL: + result = BRUTE_COMP_OK; + for (int kk = 0; kk < 9 && result == BRUTE_COMP_OK; kk++) { + for (int jj = 0; jj < 9 && result == BRUTE_COMP_OK; jj++) { + if (solved[kk][jj] != grid[kk][jj]) result = BRUTE_COMP_DIFFERENT; + } + } + if (inconsistent_grid()) result = BRUTE_COMP_INCONSISTENT; + break; + + case BRUTE_IMPOSSIBLE: + result = BRUTE_COMP_IMPOSSIBLE; + break; + + case BRUTE_TIMEOUT: + result = BRUTE_COMP_TIMEOUT; + break; + + default: + result = BRUTE_COMP_PROBLEM; + break; + } + return result; + } diff --git a/sources/Generator/brute_comp.h b/sources/Generator/brute_comp.h new file mode 100644 index 0000000..048ae52 --- /dev/null +++ b/sources/Generator/brute_comp.h @@ -0,0 +1,26 @@ +/* brute_comp.h + * + * Solves a Sudoku by executing brute() and then compares the result with + * the reference + * + * See below for the return codes + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef BRUTE_COMPARE +#define BRUTE_COMPARE + +// If you modify the following list, change brute_comp_err accordingly +#define BRUTE_COMP_OK 0 +#define BRUTE_COMP_DIFFERENT 1 +#define BRUTE_COMP_TIMEOUT 2 +#define BRUTE_COMP_INCONSISTENT 3 +#define BRUTE_COMP_IMPOSSIBLE 4 +#define BRUTE_COMP_PROBLEM 5 + +extern const char *brute_comp_err[]; + +int brute_comp(void); + +#endif diff --git a/sources/Generator/count_solved.c b/sources/Generator/count_solved.c new file mode 100644 index 0000000..f22405d --- /dev/null +++ b/sources/Generator/count_solved.c @@ -0,0 +1,19 @@ +/* count_solved.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "count_solved.h" +#include "def.h" + +int count_solved() { + int result = 0; + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + if (grid[k][j] != 0) result++; + } + } + return result; + } diff --git a/sources/Generator/count_solved.h b/sources/Generator/count_solved.h new file mode 100644 index 0000000..ebe7458 --- /dev/null +++ b/sources/Generator/count_solved.h @@ -0,0 +1,13 @@ +/* count_solved.h + * + * Counts the number of solved cells. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef COUNT_SOLVED +#define COUNT_SOLVED + +int count_solved(void); + +#endif diff --git a/sources/Generator/def.h b/sources/Generator/def.h new file mode 100644 index 0000000..a9830c9 --- /dev/null +++ b/sources/Generator/def.h @@ -0,0 +1,37 @@ +/* def.h + * + * Definitions and declarations + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef DEF +#define DEF + +// General definitions +#define FALSE 0 +#define TRUE 1 + +// Used in some strategies for clarity +#define ROW 0 +#define COL 1 +#define BOX 2 +extern char *unit_names[3]; + +// grid declarations +extern char grid[9][9]; +extern char row[9][9][2]; +extern char col[9][9][2]; +extern char box[9][9][2]; +extern char solved[9][9]; + +// Flags +extern int silent; + +// Patch because Windows doesn't recognise srandom() and random() +#ifdef __WIN32__ +#define srandom srand +#define random rand +#endif + +#endif diff --git a/sources/Generator/display.c b/sources/Generator/display.c new file mode 100644 index 0000000..07e13b3 --- /dev/null +++ b/sources/Generator/display.c @@ -0,0 +1,42 @@ +/* display.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "display.h" + +void display() { + char *h = " ++---+---+---++---+---+---++---+---+---++"; + char *hh = " ++===+===+===++===+===+===++===+===+===++"; + int jBase[] = {2, 6, 10, 15, 19, 23, 28, 32, 36}; + printf(" 0 1 2 3 4 5 6 7 8\n"); + for (int k = 0; k < 9; k++) { + if (k%3 == 0) { + printf("%s\n", hh); + } + else { + printf("%s\n", h); + } + // 000 000 111 111 122 222 223 333 333 + // 234 678 012 567 901 345 890 234 678 + char top[42] = "|| | | || | | || | | ||"; + char mid[42] = "|| | | || | | || | | ||"; + char bot[42] = "|| | | || | | || | | ||"; + char *displ[42] = {top, mid, bot}; + for (int j = 0; j < 9; j++) { + if (grid[k][j] == 0) { + mid[jBase[j]+1] = ' '; + } + else { + mid[jBase[j]+1] = '0' + grid[k][j]; + } + } // for (int j.. + printf(" %s\n", displ[0]); + printf("%d %s\n", k, displ[1]); + printf(" %s\n", displ[2]); + } + printf("%s\n", hh); + } diff --git a/sources/Generator/display.h b/sources/Generator/display.h new file mode 100644 index 0000000..b4eb3fd --- /dev/null +++ b/sources/Generator/display.h @@ -0,0 +1,13 @@ +/* display.h + * + * Displays the sudoku grid. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef DISPLAY +#define DISPLAY + +void display(); + +#endif diff --git a/sources/Generator/display_string.c b/sources/Generator/display_string.c new file mode 100644 index 0000000..ccb9163 --- /dev/null +++ b/sources/Generator/display_string.c @@ -0,0 +1,19 @@ +/* display_string.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "display_string.h" + +void display_string(char *name) { + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + printf("%d", grid[k][j]); + } + } + if (name != NULL) printf(" \"%s\"", name); + printf("\n"); + } diff --git a/sources/Generator/display_string.h b/sources/Generator/display_string.h new file mode 100644 index 0000000..9ff3af7 --- /dev/null +++ b/sources/Generator/display_string.h @@ -0,0 +1,13 @@ +/* display_string.h + * + * Displays the sudoku string. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef DISPLAY_STRING +#define DISPLAY_STRING + +void display_string(char *name); + +#endif diff --git a/sources/Generator/fill.c b/sources/Generator/fill.c new file mode 100644 index 0000000..37057ff --- /dev/null +++ b/sources/Generator/fill.c @@ -0,0 +1,38 @@ +/* fill.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "display.h" +#include "fill.h" +#include "fill_digit.h" + +int fill(void) { + int problem_found = FALSE; + int i; + int kkount = 0; + for (i = 1; i <= 9 && kkount < 729; i++) { + int kount = 0; + do { + kount++; + problem_found = fill_digit((char)i); + if (!silent) printf("fill %d [%d %d]: %s\n", + i, kount, kkount, (problem_found) ? "failed" : "succeeded" + ); + } while (problem_found && kount < 9); + + if (problem_found) { + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + if (grid[k][j] == i || grid[k][j] == i-1) grid[k][j] = 0; + } + } + i -= 2; + } // if (problem_found.. + kkount++; + } + return problem_found || kkount >= 729; + } diff --git a/sources/Generator/fill.h b/sources/Generator/fill.h new file mode 100644 index 0000000..11ef7fd --- /dev/null +++ b/sources/Generator/fill.h @@ -0,0 +1,15 @@ +/* fill.h + * + * Fills in a Sudoku + * + * Returns zero if everything is OK + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef FILL +#define FILL + +int fill(void); + +#endif diff --git a/sources/Generator/fill_digit.c b/sources/Generator/fill_digit.c new file mode 100644 index 0000000..88728b6 --- /dev/null +++ b/sources/Generator/fill_digit.c @@ -0,0 +1,81 @@ +/* fill_digit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "fill_digit.h" + +int fill_digit(char i) { + const int other_box[9][2][2] = { // [this box][row/column][..] + {/* 0 */ {-1 }, {-1 }}, + {/* 1 */ { 0, -1}, {-1 }}, + {/* 2 */ { 0, 1}, {-1 }}, + {/* 3 */ {-1 }, { 0, -1}}, + {/* 4 */ { 3, -1}, { 1, -1}}, + {/* 5 */ { 3, 4}, { 2, -1}}, + {/* 6 */ {-1 }, { 0, 3}}, + {/* 7 */ { 6, -1}, { 1, 4}}, + {/* 8 */ { 6, 7}, { 2, 5}}, + }; + int solved_cells[2][9] = {{-1, -1, -1, -1, -1, -1, -1, -1, -1}}; + + int problem_found = FALSE; + int n_cells; + int cell[9][2]; + for (int kB = 0; kB < 9 && !problem_found; kB++) { + problem_found = TRUE; + n_cells = 0; + for (int k = 0; k < 9; k++) { + int kR = box[kB][k][ROW]; + int kC = box[kB][k][COL]; + if (grid[kR][kC] == 0) { + int rc[2]; + rc[ROW] = kR; + rc[COL] = kC; + int conflict = FALSE; + for (int kRC = 0; kRC < 2 && !conflict; kRC++) { + int kkS = other_box[kB][kRC][0]; + if (kkS >= 0) { + if (rc[kRC] == solved_cells[kRC][kkS]) { + conflict = TRUE; + } + else { + kkS = other_box[kB][kRC][1]; + if (kkS >= 0 && rc[kRC] == solved_cells[kRC][kkS]) { + conflict = TRUE; + } + } + } // if (kkS.. + } // for (int kRC.. + + if (!conflict) { + cell[n_cells][ROW] = kR; + cell[n_cells][COL] = kC; + n_cells++; + } + } // if (grid[kR][kC].. + } // for (int k.. + + // Pick a cell of the box + if (n_cells > 0) { + problem_found = FALSE; + int kE = rand() % n_cells; + solved_cells[ROW][kB] = cell[kE][ROW]; + solved_cells[COL][kB] = cell[kE][COL]; + grid[solved_cells[ROW][kB]][solved_cells[COL][kB]] = i; + } + + } // for (int kB.. + + if (problem_found) { + + // Restore the grid to its initial status + for (int m = 0; m < 9 && solved_cells[ROW][m] >= 0; m++) { + grid[solved_cells[ROW][m]][solved_cells[COL][m]] = 0; + } + } + return problem_found; + } diff --git a/sources/Generator/fill_digit.h b/sources/Generator/fill_digit.h new file mode 100644 index 0000000..b392f53 --- /dev/null +++ b/sources/Generator/fill_digit.h @@ -0,0 +1,15 @@ +/* fill_digit.h + * + * Fills in one digit + * + * Returns zero if everything is OK + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef FILL_DIGIT +#define FILL_DIGIT + +int fill_digit(char i); + +#endif diff --git a/sources/Generator/in_box.c b/sources/Generator/in_box.c new file mode 100644 index 0000000..6abd10c --- /dev/null +++ b/sources/Generator/in_box.c @@ -0,0 +1,14 @@ +/* in_box.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "in_box.h" + +int in_box(int kR, int kC, int kB) { + int kkR = kB / 3 * 3; + int kkC = kB % 3 * 3; + return (kR >= kkR && kR < kkR + 3 && kC >= kkC && kC < kkC + 3); + } // in_box diff --git a/sources/Generator/in_box.h b/sources/Generator/in_box.h new file mode 100644 index 0000000..7d76044 --- /dev/null +++ b/sources/Generator/in_box.h @@ -0,0 +1,13 @@ +/* in_box.h + * + * Returns TRUE if the given cell is in the given box + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef IN_BOX +#define IN_BOX + +int in_box(int kR, int kC, int kB); + +#endif diff --git a/sources/Generator/inconsistent_grid.c b/sources/Generator/inconsistent_grid.c new file mode 100644 index 0000000..a16f401 --- /dev/null +++ b/sources/Generator/inconsistent_grid.c @@ -0,0 +1,24 @@ +/* inconsistent_grid.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "inconsistent_grid.h" +#include "inconsistent_unit.h" + +int inconsistent_grid() { + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + result |= inconsistent_unit("row", k, row[k]); + if (!result) { + result |= inconsistent_unit("column", k, col[k]); + if (!result) { + result |= inconsistent_unit("box", k, box[k]); + } + } + } // for (int k.. + return result; + } diff --git a/sources/Generator/inconsistent_grid.h b/sources/Generator/inconsistent_grid.h new file mode 100644 index 0000000..29dade5 --- /dev/null +++ b/sources/Generator/inconsistent_grid.h @@ -0,0 +1,14 @@ +/* inconsistent_grid.h + * + * Checks that there are no repeated solutions. + * Returns TRUE if it finds a problem. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef INCONSISTENT_GRID +#define INCONSISTENT_GRID + +int inconsistent_grid(); + +#endif diff --git a/sources/Generator/inconsistent_unit.c b/sources/Generator/inconsistent_unit.c new file mode 100644 index 0000000..da26075 --- /dev/null +++ b/sources/Generator/inconsistent_unit.c @@ -0,0 +1,28 @@ +/* inconsistent_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "inconsistent_unit.h" + +int inconsistent_unit(char *what, int kG, char unit[9][2]) { + int result = FALSE; + int i_vect[10] = {0}; + for (int k = 0; k < 9 && !result; k++) { + int kR = unit[k][ROW]; + int kC = unit[k][COL]; + int i = grid[kR][kC]; + if (i > 0) { + if (i_vect[i] == FALSE) { + i_vect[i] = TRUE; + } + else { // we have a duplicate solution + result = TRUE; + } + } // if (i.. + } // for (int k.. + return result; + } diff --git a/sources/Generator/inconsistent_unit.h b/sources/Generator/inconsistent_unit.h new file mode 100644 index 0000000..edc5967 --- /dev/null +++ b/sources/Generator/inconsistent_unit.h @@ -0,0 +1,15 @@ +/* inconsistent_unit.h + * + * Checks that there are no repeated solutions within a unit and that all + * cells have at least a candidate. + * Returns TRUE if it finds a problem. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef INCONSISTENT_UNIT +#define INCONSISTENT_UNIT + +int inconsistent_unit(char*, int, char[9][2]); + +#endif diff --git a/sources/Generator/init.c b/sources/Generator/init.c new file mode 100644 index 0000000..6e9288d --- /dev/null +++ b/sources/Generator/init.c @@ -0,0 +1,25 @@ +/* init.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "init.h" + +void init() { + + // Initialize the sudoku arrays + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = 0; + row[k][j][0] = k; + row[k][j][1] = j; + col[j][k][0] = k; + col[j][k][1] = j; + box[k/3*3+j/3][k%3*3+j%3][0] = k; + box[k/3*3+j/3][k%3*3+j%3][1] = j; + } + } + } diff --git a/sources/Generator/init.h b/sources/Generator/init.h new file mode 100644 index 0000000..a8153a0 --- /dev/null +++ b/sources/Generator/init.h @@ -0,0 +1,13 @@ +/* init.h + * + * Initializes a sudoku grid. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef INIT +#define INIT + +void init(void); + +#endif diff --git a/sources/Generator/list_solved.c b/sources/Generator/list_solved.c new file mode 100644 index 0000000..fa6f9a5 --- /dev/null +++ b/sources/Generator/list_solved.c @@ -0,0 +1,27 @@ +/* list_solved.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "list_solved.h" + +void list_solved(FILE *fp) { + if (fp == NULL) { + fp = stdout; + } + char spacing = (fp == stdout) ? ' ' : '\t'; + int digits[10] = {0}; + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + if (grid[k][j] != 0) { + digits[(int)grid[k][j]]++; + } + } // for (int j.. + } // for (int k.. + for (int i = 1; i <= 9; i++) { + fprintf(fp, "%c%d", spacing, digits[i]); + } + } diff --git a/sources/Generator/list_solved.h b/sources/Generator/list_solved.h new file mode 100644 index 0000000..ea1214a --- /dev/null +++ b/sources/Generator/list_solved.h @@ -0,0 +1,15 @@ +/* list_solved.h + * + * Lists the digits on the given file pointer + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef LIST_SOLVED +#define LIST_SOLVED + +#include + +void list_solved(FILE*); + +#endif diff --git a/sources/Generator/multi_html.c b/sources/Generator/multi_html.c new file mode 100644 index 0000000..2325710 --- /dev/null +++ b/sources/Generator/multi_html.c @@ -0,0 +1,195 @@ +/* multi_html.c + * + * This module must be able to display all different types of multi-grid + * puzzles (only the boxes are shown): + * + * +---+---+---+ + * | | | | + * +---+---+---+ + * | | 1 | | + * +---+---+---+---+---+ N_GRIDS == 2 (double Sudokus) + * | | | | | | + * +---+---+---+---+---+ + * | | 0 | | + * +---+---+---+ + * | | | | + * +---+---+---+ + * + * +---+---+---+ + * | | | | + * +---+---+---+ + * | | 1 | | + * +---+---+---+---+---+ + * | | | | | | + * +---+---+---+---+---+ + * | | 0 | | N_GRIDS == 3 + * +---+---+---+---+---+ + * | | | | | | + * +---+---+---+---+---+ + * | | 2 | | + * +---+---+---+ + * | | | | + * +---+---+---+ + * + * +---+---+---+ +---+---+---+ + * | | | | | | | | + * +---+---+---+ +---+---+---+ + * | | 1 | | | | 3 | | + * +---+---+---+---+---+---+---+ + * | | | | | | | | + * +---+---+---+---+---+---+---+ + * | | 0 | | N_GRIDS == 4 + * +---+---+---+---+---+ + * | | | | | | + * +---+---+---+---+---+ + * | | 2 | | + * +---+---+---+ + * | | | | + * +---+---+---+ + * + * +---+---+---+ +---+---+---+ + * | | | | | | | | + * +---+---+---+ +---+---+---+ + * | | 1 | | | | 3 | | + * +---+---+===+===+===+---+---+ + * | | I | | I | | + * +---+---+---+---+---+---+---+ + * I | 0 | I N_GRIDS == 5 (samurai) + * +---+---+---+---+---+---+---+ (the central puzzle is highlighted) + * | | I | | I | | + * +---+---+===+===+===+---+---+ + * | | 4 | | | | 2 | | + * +---+---+---+ +---+---+---+ + * | | | | | | | | + * +---+---+---+ +---+---+---+ + * + * The cell borders are defined like in save_html.c, but their position + * and the size of the HTML table depend on the type of puzzle. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include +#include "def.h" +#include "in_box.h" +#include "multi_html.h" + +// Define the number of rows and columns needed for the multi-grid +#if N_GRIDS == 2 +#define SIZE 15 +#else +#define SIZE 21 +#endif + +char multi_string[N_GRIDS][2][82]; + +// The following table identifies the box of each grid that overlaps with +// a corner box of grid 0 when creating multi-grid Sudokus. +// +// N_GRIDS kPuz=1 kPuz=2 kPuz=3 kPuz=4 +// 2 8 +// 3 8 0 +// 4 8 0 6 +// 5 8 0 6 2 +// +// Puzzle: 0 1 2 3 4 +int overlapping_box[] = {0, 8, 0, 6, 2}; + +void multi_html(int seed, int what) { + char *header_1 = + "\n" + "\n" + "\n" + "" + ; + char *header_2 = + "\n" + "\n" + "\n\n\n\n" + ; + char *footer = "
\n\n"; + + // Puzzle offsets (row/column) for each puzzle (puzzle 0 is in the middle): + // 0 1 2 3 4 + int offs[5][2] = {{6,6}, {0,0}, {12,12}, {0,12}, {12,0}}; + + // Combined multi-string (+1 to be able to close each string with a '\0'). + char multi_s[SIZE][SIZE + 1]; + for (int k = 0; k < SIZE; k++) { + for (int j = 0; j < SIZE; j++) { + multi_s[k][j] = ' '; + } + multi_s[k][SIZE] = '\0'; + } + + // Copy the puzzles to the places they belong. + // The boxes that overlap are set twice, first for puzzle 0 and then for + // the other one. But it doesn't matter, as the two boxes are identical. + // To set them only once, it would be sufficient to do the setting only + // if (multi_s[baseR + kR][baseC + kC] == ' ') + for (int kPuz = 0; kPuz < N_GRIDS; kPuz++) { + int baseR = offs[kPuz][ROW]; + int baseC = offs[kPuz][COL]; + char *s = multi_string[kPuz][what]; + for (int i = 0; i < 81; i++) { + int kR = i / 9; + int kC = i - kR * 9; + multi_s[baseR + kR][baseC + kC] = (s[i] == '0') ? '.' : s[i]; + } + } + for (int k = 0; k < SIZE; k++) printf("%s\n", multi_s[k]); + printf("\n"); + + // Finally, save the HTML to disk + char f_name[64] = {0}; + sprintf(f_name, "%d_%d%c.html", seed, N_GRIDS, (what == SOL) ? 's' : 'p'); + FILE *fp = fopen(f_name, "w"); + if (fp == NULL) { + printf("Unable to open the file '%s' for writing\n", f_name); + } + else { + fprintf(fp, "%s%d%s", header_1, seed, header_2); + for (int kRow = 0; kRow < SIZE; kRow++) { + char *s = multi_s[kRow]; + fprintf(fp, ""); + for (int i = 0; i < SIZE; i++) { + if (s[i] == ' ') { + fprintf(fp, " "); + } + else { + fprintf(fp, "%c", + kRow % 3 * 3 + i % 3, + (what == SOL || (s[i] == '.') ? "White" : "LightGray"), + ((s[i] == '.') ? ' ' : s[i]) + ); + } + } + fprintf(fp, "\n"); + } + fprintf(fp, "%s\n", footer); + fclose(fp); + } + } diff --git a/sources/Generator/multi_html.h b/sources/Generator/multi_html.h new file mode 100644 index 0000000..07a87dc --- /dev/null +++ b/sources/Generator/multi_html.h @@ -0,0 +1,23 @@ +/* multi_html.h + * + * Saves a multi-grid puzzle to disk as a web page. + * When the suffix is the empty string, it saves the puzzle. Otherwise, + * it saves the solution. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef MULTI_HTML +#define MULTI_HTML + +#define N_GRIDS 1 + +#define PUZ 0 +#define SOL 1 + +extern char multi_string[N_GRIDS][2][82]; +extern int overlapping_box[]; + +void multi_html(int seed, int what); + +#endif diff --git a/sources/Generator/save_html.c b/sources/Generator/save_html.c new file mode 100644 index 0000000..2fe1906 --- /dev/null +++ b/sources/Generator/save_html.c @@ -0,0 +1,88 @@ +/* save_html.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "save_html.h" + +#define PLAIN_SUDOKU 0 +#define SYMBOLIC_SUDOKU 1 +#define IMAGE_SUDOKU 2 + +#define SUDOKU_TYPE PLAIN_SUDOKU + +void save_html(char *puzzle, int seed, char *suffix) { + char *header_1 = + "\n" + "\n" + "\n" + "" + ; + char *header_2 = + "\n" + "\n" + "\n\n\n\n" + ; + char *footer = "
\n\n"; + + char f_name[64] = {0}; + sprintf(f_name, "%d%s.html", seed, suffix); + + FILE *fp = fopen(f_name, "w"); + if (fp == NULL) { + printf("Unable to open the file '%s' for writing\n", f_name); + } + else { + fprintf(fp, "%s%d%s", header_1, seed, header_2); + for (int i = 0; i < 81; i++) { + int kR = i / 9; + int kC = i - kR * 9; + if (kC == 0) fprintf(fp, ""); + +#if SUDOKU_TYPE == PLAIN_SUDOKU + fprintf(fp, "%c", + kR % 3 * 3 + kC % 3, ((puzzle[i] == '0') ? ' ' : puzzle[i]) + ); +#elif SUDOKU_TYPE == SYMBOLIC_SUDOKU + char *symbols[10] = {"", "\u260E", "\u2622", "\u262F", "\u263C", "\u263D", + "\u2658", "\u269B", "\u2665", "\u266B" + }; + + fprintf(fp, "%s", + kR % 3 * 3 + kC % 3, symbols[(int)(puzzle[i] - '0')] + ); +#elif SUDOKU_TYPE == IMAGE_SUDOKU + fprintf(fp, " ", + kR % 3 * 3 + kC % 3, puzzle[i] + ); +#endif + + if ((kC + 1) % 3 == 0) fprintf(fp, "\n"); + if (kC == 8) fprintf(fp, "\n"); + } + fprintf(fp, "%s\n", footer); + fclose(fp); + } + } diff --git a/sources/Generator/save_html.h b/sources/Generator/save_html.h new file mode 100644 index 0000000..35495a6 --- /dev/null +++ b/sources/Generator/save_html.h @@ -0,0 +1,13 @@ +/* save_html.h + * + * Saves a puzzle to disk as a web page + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef SAVE_HTML +#define SAVE_HTML + +void save_html(char *puzzle, int seed, char *suffix); + +#endif diff --git a/sources/Generator/sudoku_gen.c b/sources/Generator/sudoku_gen.c new file mode 100644 index 0000000..5a72128 --- /dev/null +++ b/sources/Generator/sudoku_gen.c @@ -0,0 +1,749 @@ +/* sudoku_gen.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include +#include +#include "brute_comp.h" +#include "count_solved.h" +#include "def.h" +#include "display.h" +#include "display_string.h" +#include "fill.h" +#include "inconsistent_grid.h" +#include "inconsistent_unit.h" +#include "init.h" +#include "in_box.h" +#include "list_solved.h" +#include "multi_html.h" +#include "save_html.h" + +#define LOG_TO_FILE___NO +#define FILE_NAME "puzzles.txt" + +#define SAVE_HTML_PUZZLE +#define SAVE_HTML_SOLUTION + +#define DO_PATTERN___NO + +// N_GRIDS is defined in multi_html.h. +// When N_GRIDS is between 2 and 5, it triggers the creation of multi-grid +// puzzles. With any other value, the Generator creates a classic Sudoku. +#define DO_MULTI_GRID (N_GRIDS >= 2 && N_GRIDS <= 5) + +// Parameters +#define N_SET_QUADS 5 +#define N_SET_PAIRS 10 +#define N_SET_CELLS 0 +#define ADDITIONAL_CELLS TRUE +#define FIRST_SEED 12345 +#define N_SEEDS 1 + +// Global variables +char *unit_names[3] = {"row", "column", "box"}; +char grid[9][9]; +char row[9][9][2]; +char col[9][9][2]; +char box[9][9][2]; +char solved[9][9]; +int silent = TRUE; + +// Variables and functions local to this module +char puzzle[9][9]; +int r_1[81]; +int c_1[81]; +int k_cell; +int remove_quads(int k_puz); +int remove_pairs(int k_puz); +void make_clue_list(void); +int remove_clues(int k_puz); +void remove_more_clues(int k_puz); +int check_uniqueness(void); + +// The following table identifies the box of grid 0 that overlaps with other +// grids when creating multi-grid Sudokus. +// The first box refers to puzzle0 and second one to the other puzzle: +// +// N_GRIDS kPuz=1 kPuz=2 kPuz=3 kPuz=4 +// 2 b0-b8 +// 3 b0-b8 b8-b0 +// 4 b0-b8 b8-b0 b2-b6 +// 5 b0-b8 b8-b0 b2-b6 b6-b2 +// +// Puzzle: 0 1 2 3 4 +int box0[] = {-1, 0, 8, 2, 6}; + +#ifdef DO_PATTERN +const char KEEP0[82] = + "..1.1.1.." + ".1..1..1." + "1..1.1..1" + "..1...1.." + "11..1..11" + "..1...1.." + "1..1.1..1" + ".1..1..1." + "..1.1.1.." + ; +const char KEEP1[82] = + "..11111.." + ".1.....1." + "1..111..1" + "1.1...1.1" + "1.1.1.1.1" + "1.1...1.1" + "1..111..1" + ".1.....1." + "..11111.." + ; +const char KEEP2[82] = + "..11111.." + ".1.....1." + "1..111..1" + "1.1...1.1" + "1.1.1.1.1" + "1.1...1.1" + "1..111..1" + ".1.....1." + "..11111.." + ; +const char KEEP3[] = + "..11111.." + ".1.....1." + "1..111..1" + "1.1...1.1" + "1.1.1.1.1" + "1.1...1.1" + "1..111..1" + ".1.....1." + "..11111.." + ; +const char KEEP4[] = + "..11111.." + ".1.....1." + "1..111..1" + "1.1...1.1" + "1.1.1.1.1" + "1.1...1.1" + "1..111..1" + ".1.....1." + "..11111.." + ; +const char *KEEPS[5] = { KEEP0, KEEP1, KEEP2, KEEP3, KEEP4 }; +#endif + +//======================================================================== main +int main(int argc, char *argv[]) { + printf("*** sudoku_gen ***\n"); + char mess[32]; + int n_seeds = N_SEEDS; + int k_try = 0; + +#if DO_MULTI_GRID + // When creating multi-grid puzzles, set n_seed to the number of + // puzzles that you need + n_seeds = N_GRIDS; +#endif + + // Open a file to log the results + FILE *fp = NULL; +#ifdef LOG_TO_FILE + fp = fopen(FILE_NAME, "a"); + if (fp == NULL) { + printf("Unable to open the file '%s' for reading\n", FILE_NAME); + return EXIT_FAILURE; //==> + } +#endif + + // Try all the seeds in the given range + unsigned long start_time = clock(); + + for (int k_seed = 0; k_seed < n_seeds; k_seed++) { + int seed = FIRST_SEED + k_seed; + srand(seed); + int brute_result; + int n; + + // Keep repeating the generation until you find a unique solution + char puzzle_string[82]; + char solution_string[82]; + do { + + // Generate a solved Sudoku + do { init(); } while (fill()); + + // Save the solved Sudoku + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + solved[k][j] = grid[k][j]; + puzzle[k][j] = grid[k][j]; + } + } + +#if DO_MULTI_GRID + if (k_seed > 0) { + // You arrive here if you are creating a multi-grid puzzle and + // have already created the first one (puzzle 0). + int k0 = box0[k_seed]/3*3; + int j0 = box0[k_seed]%3*3; + int kk = overlapping_box[k_seed]/3*3; + int jj = overlapping_box[k_seed]%3*3; + + // Build the look-up list of numbers to match puzzle 0 when creating + // subsequent grids. + char map[10] = {0}; + for (int k = 0; k < 3; k++) { + for (int j = 0; j < 3; j++) { + map[(int)grid[(kk + k)][jj + j]] = + multi_string[0][SOL][(k0 + k)*9 + j0 + j] - '0' + ; + } + } + + // Convert the numbers in the grid and save the modified grid + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = map[(int)grid[k][j]]; + solved[k][j] = grid[k][j]; + puzzle[k][j] = grid[k][j]; + } + } +#ifndef DO_PATTERN + // Make the box that overlaps puzzle 0 identical to the + // corresponding one of puzzle 0 + for (int k = 0; k < 3; k++) { + for (int j = 0; j < 3; j++) { + grid[(kk + k)][jj + j] = + multi_string[0][PUZ][(k0 + k)*9 + j0 + j] - '0' + ; + } + } +#endif + } +#endif + +#ifdef DO_PATTERN + for (int i = 0; i < 81; i++) { + if (KEEPS[k_seed][i] == '.') { + int k = i / 9; + int j = i - k * 9; + grid[k][j] = 0; + puzzle[k][j] = 0; + } + } +#else + //========= Remove N_SET_QUADS quadruples of clues + if (N_SET_QUADS > 0) { + int success = remove_quads(k_seed); + if (!success) { + brute_result = BRUTE_COMP_DIFFERENT; + goto skip; //==> + } + } + + //========= Remove N_SET_PAIRS pairs of clues + if (N_SET_PAIRS > 0) { + int success = remove_pairs(k_seed); + if (!success) { + brute_result = BRUTE_COMP_DIFFERENT; + goto skip; //==> + } + } + + //========= Remove N_SET_CELLS individual clues and then some more + make_clue_list(); + k_cell = 0; + if (N_SET_CELLS > 0) { + int success = remove_clues(k_seed); + if (!success) { + brute_result = BRUTE_COMP_DIFFERENT; + goto skip; //==> + } + } + if (ADDITIONAL_CELLS && k_cell < 81) remove_more_clues(k_seed); +#endif + + //========= Check whether the solution is really unique + brute_result = check_uniqueness(); + + //========= Done + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + n = count_solved(); + if (!silent && fp == NULL) { + display(); + sprintf(mess, "seed %d %d", seed, n); + display_string(mess); + printf("The puzzle contains %d clues:", n); + list_solved(stdout); + } + +skip: // <== + k_try++; + printf("%d: %s\n", + k_try, + (brute_result == BRUTE_COMP_DIFFERENT) ? "No" : "Yes" + ); + } while (brute_result == BRUTE_COMP_DIFFERENT); + + // Save puzzle and solution into strings + int kar = 0; + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + puzzle_string[kar] = puzzle[k][j] + '0'; + solution_string[kar] = solved[k][j] + '0'; + kar++; + } + } + puzzle_string[kar] = '\0'; + solution_string[kar] = '\0'; + +#if DO_MULTI_GRID + // Save the puzzle and solution strings to be combined later into + // a multi-grid Sudoku + for (int i = 0; i < 82; i++) { // copy also the '\0' at the end + multi_string[k_seed][PUZ][i] = puzzle_string[i]; + multi_string[k_seed][SOL][i] = solution_string[i]; + } +#endif + +#ifdef SAVE_HTML_PUZZLE + save_html(puzzle_string, seed, "p"); +#endif + +#ifdef SAVE_HTML_SOLUTION + save_html(solution_string, seed, "s"); +#endif + if (fp != NULL) { + printf("#%d\n", k_seed); + fprintf(fp, "%s\t%d", puzzle_string, seed); + if (brute_result != BRUTE_COMP_DIFFERENT) { + fprintf(fp, "\t%d", n); + list_solved(fp); + } + fprintf(fp, "\n"); + } + } // for (k_seed.. + +#if DO_MULTI_GRID + multi_html(FIRST_SEED, PUZ); + multi_html(FIRST_SEED, SOL); +#endif + + unsigned long end_time = clock(); + printf("********* done in %ld microseconds\n", end_time - start_time); + if (fp != NULL) fclose(fp); + return EXIT_SUCCESS; + } + +//================================================================ remove_quads +#define N_QUADS 20 +int remove_quads(int kPuz) { + + // Build a random list of cells to be quadrupled + int r_4[N_QUADS]; + int c_4[N_QUADS]; + { + char quads[9][9] = {{0}}; + for (int k = 0; k < N_QUADS; k++) { + int kR; + int kC; + do { + int kk = rand() % N_QUADS; + kR = kk >> 2; + kC = kk - (kR << 2); + } while (quads[kR][kC] > 0); + r_4[k] = kR; + c_4[k] = kC; + quads[kR][kC] = 1; + } + } + + // Change quadruples until you get a matching solution + int k_quad = -1; + int n_quads = 0; + while (n_quads < N_SET_QUADS && k_quad < N_QUADS-1) { + k_quad++; + n_quads++; + int quad[4][2] = {{0}}; // [index][row/col] + int kR = r_4[k_quad]; + int kC = c_4[k_quad]; + quad[0][ROW] = kR; + quad[0][COL] = kC; + quad[1][ROW] = kR; + quad[1][COL] = 8 - kC; + if (kR == 4) { + quad[2][ROW] = kC; + quad[2][COL] = kR; + quad[3][ROW] = 8 - kC; + quad[3][COL] = kR; + } + else { + quad[2][ROW] = 8 - kR; + quad[2][COL] = kC; + quad[3][ROW] = 8 - kR; + quad[3][COL] = 8 - kC; + } + if (!silent) printf("Removed quad %d:", k_quad); + for (int k = 0; k < 4; k++) { + int kR = quad[k][ROW]; + int kC = quad[k][COL]; + + // The following 'if' is only needed when creating multi-grid puzzles + if (kPuz == 0 || !in_box(kR, kC, overlapping_box[kPuz])) { + grid[kR][kC] = 0; + } + if (!silent) printf("(%d,%d)", kR, kC); + } + if (!silent) printf("\n"); + + // Save the Sudoku puzzle after the removal + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + puzzle[k][j] = grid[k][j]; + } + } + + // Solve with brute() and see whether the solution matches + // the reference + int brute_result = brute_comp(); + if (!silent) printf("Brute after removing quad %d: %s\n", + k_quad, brute_comp_err[brute_result] + ); + + // If not, backtrack + if (brute_result != BRUTE_COMP_OK) { + if (!silent) printf("Backtracking the last quadruple\n"); + puzzle[kR][kC] = solved[kR][kC]; + puzzle[kR][8-kC] = solved[kR][8-kC]; + puzzle[8-kR][kC] = solved[8-kR][kC]; + puzzle[8-kR][8-kC] = solved[8-kR][8-kC]; + n_quads--; + } + + // Restore the puzzle to how it was before solving it + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + } // while (n_quads.. + int success = n_quads == N_SET_QUADS; + if (!silent) { + if (success) { + printf("%d clues left after removing the quadruples\n", count_solved()); + display(); + + // Save the Sudoku puzzle after removing the quadruples + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + } + else { + printf("No unique solution when removing quadruples. Run aborted.\n"); + } + } + return success; + } // remove_quads + +//================================================================ remove_pairs +#define N_PAIRS 40 +int remove_pairs(int kPuz) { + + // Build a random list of cells to be paired + int r_2[N_PAIRS]; + int c_2[N_PAIRS]; + { + char pairs[9][9] = {{0}}; + for (int k = 0; k < N_PAIRS; k++) { + int kR; + int kC; + do { + int kk = rand() % N_PAIRS; + kR = kk / 9; + kC = kk - kR * 9; + } while (pairs[kR][kC] > 0); + r_2[k] = kR; + c_2[k] = kC; + pairs[kR][kC] = 1; + } + } + + // Change pairs until you get a matching solution + int k_pair = -1; + int n_pairs = 0; + while (n_pairs < N_SET_PAIRS && k_pair < N_PAIRS-1) { + int kR; + int kC; + do { + k_pair++; + if (k_pair < N_PAIRS) { + kR = r_2[k_pair]; + kC = c_2[k_pair]; + if (grid[kR][kC] == 0) { + if (!silent) printf("Pair %d: (%d,%d) (%d,%d) overlaps" + " with quadruple\n", k_pair, kR, kC, 8-kR, 8-kC + ); + } + } + } while (grid[kR][kC] == 0 && k_pair < N_PAIRS); + if (k_pair < N_PAIRS) { + + // The following two 'if' are only needed when creating multi-grid puzzles + if (kPuz == 0 || !in_box(kR, kC, overlapping_box[kPuz])) { + grid[kR][kC] = 0; + } + if (kPuz == 0 || !in_box(8 - kR, 8 - kC, overlapping_box[kPuz])) { + grid[8-kR][8-kC] = 0; + } + n_pairs++; + if (!silent) printf("Removed pair %d: (%d,%d) (%d,%d)\n", + k_pair, kR, kC, 8-kR, 8-kC + ); + + // Save the Sudoku puzzle after the removal + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + puzzle[k][j] = grid[k][j]; + } + } + + // Solve with brute() and see whether the solution matches + // the reference + int brute_result = brute_comp(); + if (!silent) printf("Brute after removing pair %d: %s\n", + k_pair, brute_comp_err[brute_result] + ); + + // If not, backtrack + if (brute_result != BRUTE_COMP_OK) { + if (!silent) printf("Backtracking the last pair\n"); + puzzle[kR][kC] = solved[kR][kC]; + puzzle[8-kR][8-kC] = solved[8-kR][8-kC]; + n_pairs--; + } + + // Restore the puzzle to how it was before solving it + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + } // if (k_pair.. + } // while (n_pairs.. + + int success = n_pairs == N_SET_PAIRS; + if (!silent) { + if (success) { + printf("%d clues left after removing the pairs\n", count_solved()); + display(); + + // Save the Sudoku puzzle after removing the pairs + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + } + else { + printf("No unique solution when removing pairs. Run aborted.\n"); + } + } + return success; + } // remove_pairs + +//============================================================== make_clue_list +void make_clue_list() { + char singles[9][9] = {{0}}; + for (int k = 0; k < 81; k++) { + int kR; + int kC; + do { + int kk = rand() % 81; + kR = kk / 9; + kC = kk - kR * 9; + } while (singles[kR][kC] > 0); + r_1[k] = kR; + c_1[k] = kC; + singles[kR][kC] = 1; + } + } // make_clue_list + +//================================================================ remove_clues +int remove_clues(int kPuz) { + int success = TRUE; + int n_cells = 0; + while (n_cells < N_SET_CELLS && success) { + int kR; + int kC; + do { + kR = r_1[k_cell]; + kC = c_1[k_cell]; + if (grid[kR][kC] == 0) { + if (!silent) printf("1 Cell %d: (%d,%d) overlaps with quadruple" + " or pair\n", k_cell, kR, kC + ); + } + k_cell++; + } while (grid[kR][kC] == 0 && k_cell < 81); + if (k_cell > 81) { + if (!silent) printf("1 No more cells available after removing" + " %d clues. Run aborted.\n", n_cells + ); + success = FALSE; + } + // The following 'if' is only needed when creating multi-grid puzzles + else if (kPuz == 0 || !in_box(kR, kC, overlapping_box[kPuz])) { + grid[kR][kC] = 0; + n_cells++; + if (!silent) printf("1 Clue removal %d, removed" + " cell %d: (%d,%d)\n", n_cells, k_cell-1, kR, kC + ); + + // Save the Sudoku puzzle after the removal + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + puzzle[k][j] = grid[k][j]; + } + } + + // Solve with brute() and see whether the solution matches + // the reference + int brute_result = brute_comp(); + if (!silent) printf("1 Brute after removing cell %d: %s\n", + k_cell-1, brute_comp_err[brute_result] + ); + + // If not, backtrack + if (brute_result != BRUTE_COMP_OK) { + if (!silent) printf("1 Backtracking the last cell\n"); + puzzle[kR][kC] = solved[kR][kC]; + n_cells--; + } + + // Restore the puzzle to how it was before solving it + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + } // if (k_cell.. else .. + } // while (n_cells.. + + if (success) { + + // Save the Sudoku puzzle after removing the individual clues + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + if (!silent) { + printf("%d clues left after removing %d individual clues\n", + count_solved(), n_cells + ); + display(); + } + } + return success; + } // remove_clues + +//=========================================================== remove_more_clues +void remove_more_clues(int kPuz) { + int brute_result; + do { + int kR; + int kC; + do { + kR = r_1[k_cell]; + kC = c_1[k_cell]; + if (grid[kR][kC] == 0) { + if (!silent) printf("2 Cell %d: (%d,%d) overlaps with quadruple" + " or pair\n", k_cell, kR, kC + ); + } + k_cell++; + } while (grid[kR][kC] == 0 && k_cell < 81); + + // The second part of the following 'if' is only needed when creating + // multi-grid puzzles + if (k_cell <= 81 && + (kPuz == 0 || !in_box(kR, kC, overlapping_box[kPuz])) + ) { + grid[kR][kC] = 0; + if (!silent) printf("2 Clue removal %d, removed" + " cell %d: (%d,%d)\n", 81-count_solved(), k_cell-1, kR, kC + ); + + // Save the Sudoku puzzle after the removal + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + puzzle[k][j] = grid[k][j]; + } + } + + // Solve with brute() and see whether the solution matches the reference + brute_result = brute_comp(); + if (!silent) printf("2 Brute after removing cell %d: %s\n", + k_cell-1, brute_comp_err[brute_result] + ); + + // Restore the puzzle to how it was before solving it + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j] = puzzle[k][j]; + } + } + } // if (k_cell.. + } while (brute_result == BRUTE_COMP_OK && k_cell < 81); + + // Restore the last clue removed + if (brute_result != BRUTE_COMP_OK) { + int kR = r_1[k_cell-1]; + int kC = c_1[k_cell-1]; + puzzle[kR][kC] = solved[kR][kC]; + } + } // remove_more_clues + +//============================================================ check_uniqueness +int check_uniqueness() { + int brute_result = BRUTE_COMP_OK; + int incr = -8; + while (incr < 9 && brute_result != BRUTE_COMP_DIFFERENT) { + for (int k = 0; k < 9 && brute_result != BRUTE_COMP_DIFFERENT; k++) { + for (int j = 0; j < 9 && brute_result != BRUTE_COMP_DIFFERENT; j++) { + if (puzzle[k][j] == 0) { + for (int kk = 0; kk < 9; kk++) { + for (int jj = 0; jj < 9; jj++) { + grid[kk][jj] = puzzle[kk][jj]; + } // for (int jj.. + } // for (int kk.. + grid[k][j] = solved[k][j] + incr; + int kB = k/3*3+j/3; + if ( grid[k][j] < 1 + || grid[k][j] > 9 + || inconsistent_unit("row", k, row[k]) + || inconsistent_unit("column", j, col[j]) + || inconsistent_unit("box", kB, box[kB]) + ) { + grid[k][j] = 0; + } + else { + brute_result = brute_comp(); + } // if (grid[k][j].. + } // if (puzzle[k]j].. + } // for (int j.. + } // for (int k.. + incr++; + if (incr == 0) incr++; + } // while (incr.. + return brute_result; + } // check_uniqueness diff --git a/sources/Solver/backtrack.c b/sources/Solver/backtrack.c new file mode 100644 index 0000000..bf2fc96 --- /dev/null +++ b/sources/Solver/backtrack.c @@ -0,0 +1,128 @@ +/* backtrack.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "backtrack.h" +#include "cleanup_around.h" +#include "count_solved.h" +#include "def.h" +#include "display_strats_in_clear.h" +#include "solve.h" + +#define MAX_DEPTH 10 + +int backtrack(int depth) { +#ifdef LOG_IN_OUT + printf("--- backtrack (%d) >>>\n", depth); +#endif + + int result = FALSE; + char grid_backup[9][9][10]; + + // Select the cell + int k; + int j; + { + int max_i = -1; + int row_max_i; + int col_max_i; + for (k = 0; k < 9 && max_i < 9; k++) { + for (j = 0; j < 9 && max_i < 9; j++) { + if (grid[k][j][0] > max_i) { + max_i = grid[k][j][0]; + row_max_i = k; + col_max_i = j; + } + } + } + k = row_max_i; + j = col_max_i; + } + + // Process the cell + char *elem = grid[k][j]; + if (!silent) { + for (int kd = 0; kd < depth; kd++) printf(" "); + printf("backtrack (%d): (%d,%d) has candidates", depth, k, j); + for (int i = 1; i <= 9; i++) { + if (elem[i]) printf(" %d", i); + } + printf("\n"); + } // if (!silent.. + for (int i = 1; i <= 9 && !result; i++) { + if (elem[i]) { + + // Save the current state of the grid + for (int k1 = 0; k1 < 9; k1++) { + for (int j1 = 0; j1 < 9; j1++) { + for (int i1 = 0; i1 <= 9; i1++) { + grid_backup[k1][j1][i1] = grid[k1][j1][i1]; + } + } // for (int j1.. + } // for (int k1.. + + // Force a solution + for (int i1 = 1; i1 <= 9; i1++) { + elem[i1] = FALSE; + } + elem[i] = TRUE; + elem[0] = 1; + int orig_silent = silent; + silent = TRUE; + cleanup_around(k, j); + + // Attempt to solve the puzzle + solve(); + silent = orig_silent; + + // Check the result + if (problem_found) { + problem_found = FALSE; + if (!silent) { + for (int kd = 0; kd < depth; kd++) printf(" "); + printf("backtrack (%d): %d unsuccessful\n", depth, i); + } + } // if (problem_found.. + else { + if (!silent) { + for (int kd = 0; kd < depth; kd++) printf(" "); + printf("backtrack (%d): %d successful (%d solved)\n", + depth, i, count_solved() + ); + for (int kd = 0; kd < depth; kd++) printf(" "); + printf("backtrack (%d) strategies:", depth); + if (n_strats_used > 0) display_strats_in_clear(); + else printf("none"); + printf("\n"); + } + if (count_solved() == 81) { + strats_used[n_strats_used] = 40 + depth; + n_strats_used++; + result = TRUE; + } + else if (depth < MAX_DEPTH) { + result = backtrack(depth + 1); + } + } // if (problem_found.. else.. + + // If unsuccessful, restore the grid to its original content + if (!result) { + for (int k1 = 0; k1 < 9; k1++) { + for (int j1 = 0; j1 < 9; j1++) { + for (int i1 = 0; i1 <= 9; i1++) { + grid[k1][j1][i1] = grid_backup[k1][j1][i1]; + } + } // for (int j1.. + } // for (int k1.. + } // if (!result + } // if (elem[i].. + } // for (int i.. + +#ifdef LOG_IN_OUT + printf("<<< backtrack (%d) ---\n", depth); +#endif + return result; + } diff --git a/sources/Solver/backtrack.h b/sources/Solver/backtrack.h new file mode 100644 index 0000000..2c287b4 --- /dev/null +++ b/sources/Solver/backtrack.h @@ -0,0 +1,15 @@ +/* backtrack.h + * + * Trial and error strategy. + * + * Returns TRUE is successful. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef BACKTRACK +#define BACKTRACK + +int backtrack(int); + +#endif diff --git a/sources/Solver/box_line.c b/sources/Solver/box_line.c new file mode 100644 index 0000000..284f02b --- /dev/null +++ b/sources/Solver/box_line.c @@ -0,0 +1,26 @@ +/* box_line.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "box_line.h" +#include "box_line_unit.h" +#include "def.h" + +int box_line() { +#ifdef LOG_IN_OUT + printf("--- box_line >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + if (box_line_unit(ROW, row[k]) || box_line_unit(COL, col[k])) { + result = TRUE; + } + } +#ifdef LOG_IN_OUT + printf("<<< box_line ---\n"); +#endif + return result; + } diff --git a/sources/Solver/box_line.h b/sources/Solver/box_line.h new file mode 100644 index 0000000..60be73f --- /dev/null +++ b/sources/Solver/box_line.h @@ -0,0 +1,13 @@ +/* box_line.h + * + * box-line strategy. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef BOX_LINE +#define BOX_LINE + +int box_line(void); + +#endif diff --git a/sources/Solver/box_line_unit.c b/sources/Solver/box_line_unit.c new file mode 100644 index 0000000..e73e0b1 --- /dev/null +++ b/sources/Solver/box_line_unit.c @@ -0,0 +1,75 @@ +/* box_line_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "box_line_unit.h" +#include "cleanup_around.h" +#include "def.h" +#include "remove_candidate.h" + +int box_line_unit(int row_col, char line[9][2]) { +#ifdef LOG_IN_OUT + printf("--- box_line_unit (%s) >>>\n", unit_names[row_col]); +#endif + int result = FALSE; + int b[10]; for (int i = 0; i < 10; i++) { b[i] = -1; } + int rc[10]; + for (int j1 = 0; j1 < 9; j1++) { + int kR = line[j1][ROW]; + int kC = line[j1][COL]; + char *elem = grid[kR][kC]; + if (elem[0] > 1) { + for (int i = 1; i <= 9; i++) { + if (elem[i] != FALSE) { + int kB = kR/3*3 + kC/3; + if (b[i] == -1) { + b[i] = kB; + rc[i] = (row_col == ROW) ? kR : kC; + } + else if (b[i] != kB) { + b[i] = -2; + } + } // if (elem[i].. + } // for (int i.. + } // if (elem[0].. + } // for (int j1.. + + for (int i = 1; i <= 9; i++) { + if (b[i] >= 0) { + int log_printed = FALSE; + int kB = b[i]; + int kL = rc[i]; + for (int kE = 0; kE < 9; kE++) { + int kR = box[kB][kE][ROW]; + int kC = box[kB][kE][COL]; + int kRC = (row_col == ROW) ? kR : kC; + if (kRC != kL) { + char *elem = grid[kR][kC]; + if (elem[i] != FALSE) { + result = TRUE; +#ifdef LOG_BOX_LINE + if (!log_printed && !silent) { + printf("box_line_unit: all candidates for %d of %s %d" + " are in box %d\n", i, unit_names[row_col], kL, kB + ); + log_printed = TRUE; + } +#endif + remove_candidate("box_line_unit", i, kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } // if (elem[i].. + } // if (kRC.. + } // for (int kE.. + } // if (b[i].. + } // for (int i.. + +#ifdef LOG_IN_OUT + printf("<<< box_line_unit (%s) ---\n", unit_names[row_col]); +#endif + return result; + } diff --git a/sources/Solver/box_line_unit.h b/sources/Solver/box_line_unit.h new file mode 100644 index 0000000..edd4c65 --- /dev/null +++ b/sources/Solver/box_line_unit.h @@ -0,0 +1,13 @@ +/* box_line_unit.h + * + * box-line strategy. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef BOX_LINE_UNIT +#define BOX_LINE_UNIT + +int box_line_unit(int, char[9][2]); + +#endif diff --git a/sources/Solver/cleanup.c b/sources/Solver/cleanup.c new file mode 100644 index 0000000..7a7f13f --- /dev/null +++ b/sources/Solver/cleanup.c @@ -0,0 +1,24 @@ +/* cleanup.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup.h" +#include "cleanup_around.h" +#include "def.h" + +void cleanup() { +#ifdef LOG_IN_OUT + printf("--- cleanup >>>\n"); +#endif + for (int k = 0; k < 9 && !problem_found; k++) { + for (int j = 0; j < 9 && !problem_found; j++) { + if (grid[k][j][0] == 1) cleanup_around(k, j); + } + } +#ifdef LOG_IN_OUT + printf("<<< cleanup ---\n"); +#endif + } diff --git a/sources/Solver/cleanup.h b/sources/Solver/cleanup.h new file mode 100644 index 0000000..e91f31b --- /dev/null +++ b/sources/Solver/cleanup.h @@ -0,0 +1,13 @@ +/* cleanup.h + * + * Removes duplicate numbers from rows, columns, and boxes. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef CLEANUP +#define CLEANUP + +void cleanup(void); + +#endif diff --git a/sources/Solver/cleanup_around.c b/sources/Solver/cleanup_around.c new file mode 100644 index 0000000..d5a7ab8 --- /dev/null +++ b/sources/Solver/cleanup_around.c @@ -0,0 +1,22 @@ +/* cleanup_around.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "cleanup_unit.h" +#include "def.h" + +void cleanup_around(int k, int j) { +#ifdef LOG_IN_OUT + printf("--- cleanup_around (%d,%d) >>>\n", k, j); +#endif + cleanup_unit("row", k, j, row[k]); + if (!problem_found) cleanup_unit("column", k, j, col[j]); + if (!problem_found) cleanup_unit("box", k, j, box[k/3*3+j/3]); +#ifdef LOG_IN_OUT + printf("<<< cleanup_around (%d,%d) ---\n", k, j); +#endif + } diff --git a/sources/Solver/cleanup_around.h b/sources/Solver/cleanup_around.h new file mode 100644 index 0000000..fbcb55c --- /dev/null +++ b/sources/Solver/cleanup_around.h @@ -0,0 +1,13 @@ +/* cleanup_around.h + * + * Removes duplicate numbers around a cell. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef CLEANUP_AROUND +#define CLEANUP_AROUND + +void cleanup_around(int, int); + +#endif diff --git a/sources/Solver/cleanup_unit.c b/sources/Solver/cleanup_unit.c new file mode 100644 index 0000000..a256605 --- /dev/null +++ b/sources/Solver/cleanup_unit.c @@ -0,0 +1,37 @@ +/* cleanup_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "cleanup_unit.h" +#include "def.h" +#include "remove_candidate.h" + +void cleanup_unit(char *what, int kElem, int jElem, char unit[9][2]) { +#ifdef LOG_IN_OUT + printf("--- cleanup_unit (%s) for (%d,%d) >>>\n", what, kElem, jElem); +#endif + char *elem = grid[kElem][jElem]; + if (elem[0] == 1) { + int i = 0; + do { i++; } while (elem[i] == FALSE); + for (int j1 = 0; j1 < 9 && !problem_found; j1++) { + int kR = unit[j1][ROW]; + int kC = unit[j1][COL]; + if ((kR != kElem || kC != jElem) && grid[kR][kC][i] != FALSE) { + char mess[40]; + sprintf(mess, "cleanup_unit [%s of (%d,%d)]", what, kElem, jElem); + remove_candidate(mess, i, kR, kC); + if (grid[kR][kC][0] == 1 && !problem_found) { + cleanup_around(kR, kC); + } + } + } + } +#ifdef LOG_IN_OUT + printf("<<< cleanup_unit (%s) for (%d,%d) ---\n", what, kElem, jElem); +#endif + } diff --git a/sources/Solver/cleanup_unit.h b/sources/Solver/cleanup_unit.h new file mode 100644 index 0000000..b3a03f5 --- /dev/null +++ b/sources/Solver/cleanup_unit.h @@ -0,0 +1,13 @@ +/* cleanup_unit.h + * + * Removes duplicate numbers from a unit. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef CLEANUP_UNIT +#define CLEANUP_UNIT + +void cleanup_unit(char*, int, int, char[9][2]); + +#endif diff --git a/sources/Solver/count_candidates.c b/sources/Solver/count_candidates.c new file mode 100644 index 0000000..55128a2 --- /dev/null +++ b/sources/Solver/count_candidates.c @@ -0,0 +1,25 @@ +/* count_candidates.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "count_candidates.h" +#include "def.h" + +int count_candidates() { +#ifdef LOG_IN_OUT + printf("--- count_candidates >>>\n"); +#endif + int result = 0; + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + result += grid[k][j][0]; + } + } +#ifdef LOG_IN_OUT + printf("<<< count_candidates ---\n"); +#endif + return result; + } diff --git a/sources/Solver/count_candidates.h b/sources/Solver/count_candidates.h new file mode 100644 index 0000000..40cf1b4 --- /dev/null +++ b/sources/Solver/count_candidates.h @@ -0,0 +1,14 @@ +/* count_candidates.h + * + * Counts the total number of candidates in the game, including those in the + * solved cells. That is, a solved game has 81 candidates. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef COUNT_CANDIDATES +#define COUNT_CANDIDATES + +int count_candidates(void); + +#endif diff --git a/sources/Solver/count_solved.c b/sources/Solver/count_solved.c new file mode 100644 index 0000000..28d6240 --- /dev/null +++ b/sources/Solver/count_solved.c @@ -0,0 +1,25 @@ +/* count_solved.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "count_solved.h" +#include "def.h" + +int count_solved() { +#ifdef LOG_IN_OUT + printf("--- count_solved >>>\n"); +#endif + int result = 0; + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + if (grid[k][j][0] == 1) result++; + } + } +#ifdef LOG_IN_OUT + printf("<<< count_solved ---\n"); +#endif + return result; + } diff --git a/sources/Solver/count_solved.h b/sources/Solver/count_solved.h new file mode 100644 index 0000000..ebe7458 --- /dev/null +++ b/sources/Solver/count_solved.h @@ -0,0 +1,13 @@ +/* count_solved.h + * + * Counts the number of solved cells. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef COUNT_SOLVED +#define COUNT_SOLVED + +int count_solved(void); + +#endif diff --git a/sources/Solver/def.h b/sources/Solver/def.h new file mode 100644 index 0000000..e60ebe2 --- /dev/null +++ b/sources/Solver/def.h @@ -0,0 +1,83 @@ +/* def.h + * + * Definitions and declarations + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef DEF +#define DEF + +// General definitions +#define FALSE 0 +#define TRUE 1 + +// Definitions for logging +#define LOG_FOOTPRINT +#define LOG_HIDDEN_PAIR +#define LOG_NAKED_QUAD +#define LOG_HIDDEN_TRIPLE +//#define LOG_IN_OUT +#define LOG_LINES +#define LOG_NAKED_PAIR +#define LOG_NAKED_TRIPLE +#define LOG_POINTING_LINE +#define LOG_RECTANGLE +#define LOG_REMOVE_CANDIDATE +#define LOG_BOX_LINE +#define LOG_UNIQUE +#define LOG_XY_CHAIN +#define LOG_Y_WING + +// Definitions to distinguish between Y-wing and XY-chain when invoking +// pairs_find() +#define DEF_Y_WING 0 +#define DEF_XY_CHAIN 1 + +// Structure and typedef to build chains of cell coordinates. +// It makes possible to develop functions that return lists of cells. +#define MAX_INTER_N 13 +typedef struct rc_struct *rc_p_t; +typedef struct rc_struct { + int row; + int col; + rc_p_t next; + } rc_struct_t; + +// Strategy functions +typedef int (*f_ptr_t)(void); +extern f_ptr_t *strat_all[]; +extern char **strat_all_names[]; +extern int n_strat_all[]; +extern int n_levels; + +// List of strategies used in a solution +// 0 means 'unique', 40 means 'backtrack' +// Other strategies: (strat level) * 10 + (strat ID within the level) +extern int strats_used[]; +extern int n_strats_used; + +// Used in some strategies for clarity +#define ROW 0 +#define COL 1 +#define BOX 2 +extern char *unit_names[3]; + +// Sudoku declarations in sudoku_solver.c +extern char grid[9][9][10]; +extern char row[9][9][2]; +extern char col[9][9][2]; +extern char box[9][9][2]; + +// Flags +extern int problem_found; +extern int silent; +extern int backtracking; + +// Patch because Windows doesn't recognize srandom() and random() +#ifdef __WIN32__ +#define srandom srand +#define random rand +#endif + +#endif diff --git a/sources/Solver/display.c b/sources/Solver/display.c new file mode 100644 index 0000000..d04c8d6 --- /dev/null +++ b/sources/Solver/display.c @@ -0,0 +1,50 @@ +/* display.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "display.h" + +void display() { + char *h = " ++---+---+---++---+---+---++---+---+---++"; + char *hh = " ++===+===+===++===+===+===++===+===+===++"; + int jBase[] = {2, 6, 10, 15, 19, 23, 28, 32, 36}; + printf(" 0 1 2 3 4 5 6 7 8\n"); + for (int k = 0; k < 9; k++) { + if (k%3 == 0) { + printf("%s\n", hh); + } + else { + printf("%s\n", h); + } + // 000 000 111 111 122 222 223 333 333 + // 234 678 012 567 901 345 890 234 678 + char top[42] = "|| | | || | | || | | ||"; + char mid[42] = "|| | | || | | || | | ||"; + char bot[42] = "|| | | || | | || | | ||"; + char *displ[42] = {top, mid, bot}; + for (int j = 0; j < 9; j++) { + if (grid[k][j][0] == 1) { + int i = 0; + do { i++; } while (grid[k][j][i] == 0); + mid[jBase[j]] = '('; + mid[jBase[j]+1] = '0'+i; + mid[jBase[j]+2] = ')'; + } + else { + for (int i = 0; i < 9; i++) { + if (grid[k][j][i+1] != 0) { + displ[i/3][jBase[j] + i%3] = '0' + (i+1); + } + } // for (int i.. + } // else.. + } // for (int j.. + printf(" %s\n", displ[0]); + printf("%d %s\n", k, displ[1]); + printf(" %s\n", displ[2]); + } + printf("%s\n", hh); + } diff --git a/sources/Solver/display.h b/sources/Solver/display.h new file mode 100644 index 0000000..0e2ce02 --- /dev/null +++ b/sources/Solver/display.h @@ -0,0 +1,13 @@ +/* display.h + * + * Displays the sudoku grid. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef DISPLAY +#define DISPLAY + +void display(void); + +#endif diff --git a/sources/Solver/display_strats_in_clear.c b/sources/Solver/display_strats_in_clear.c new file mode 100644 index 0000000..039f9f0 --- /dev/null +++ b/sources/Solver/display_strats_in_clear.c @@ -0,0 +1,18 @@ +/* display_strats_in_clear.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "display_strats_in_clear.h" + +void display_strats_in_clear() { + for (int k = 0; k < n_strats_used; k++) { + int level = strats_used[k] / 10; + int k_strat = strats_used[k] - level * 10; + printf(" '%s'", strat_all_names[level][k_strat]); + } + printf("\n"); + } diff --git a/sources/Solver/display_strats_in_clear.h b/sources/Solver/display_strats_in_clear.h new file mode 100644 index 0000000..fa70b0b --- /dev/null +++ b/sources/Solver/display_strats_in_clear.h @@ -0,0 +1,13 @@ +/* display_strats_in_clear.h + * + * Displays the list of strategy used + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef DISPLAY_STRATS_IN_CLEAR +#define DISPLAY_STRATS_IN_CLEAR + +void display_strats_in_clear(); + +#endif diff --git a/sources/Solver/display_string.c b/sources/Solver/display_string.c new file mode 100644 index 0000000..3047e55 --- /dev/null +++ b/sources/Solver/display_string.c @@ -0,0 +1,27 @@ +/* display_string.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "display_string.h" + +void display_string() { + printf("****** "); + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + char *elem = grid[k][j]; + if (elem[0] > 1) { + printf("0"); + } + else { + int i = 0; + do { i++; } while (!elem[i]); + printf("%d", i); + } + } + } + printf("\n"); + } diff --git a/sources/Solver/display_string.h b/sources/Solver/display_string.h new file mode 100644 index 0000000..855ccf2 --- /dev/null +++ b/sources/Solver/display_string.h @@ -0,0 +1,13 @@ +/* display_string.h + * + * Displays the sudoku string. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef DISPLAY_STRING +#define DISPLAY_STRING + +void display_string(); + +#endif diff --git a/sources/Solver/execute_strategies.c b/sources/Solver/execute_strategies.c new file mode 100644 index 0000000..585189f --- /dev/null +++ b/sources/Solver/execute_strategies.c @@ -0,0 +1,55 @@ +/* execute_strategies.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "count_candidates.h" +#include "count_solved.h" +#include "def.h" +#include "display.h" +#include "display_string.h" +#include "execute_strategies.h" +#include "keep_going.h" +#include "unique_loop.h" + +/* + * It returns TRUE when at least one candidate is removed. + * + * It goes through all the strategies of the level, but only as long as no + * candidate is removed. When that happens, it aborts the loop and returns. + */ +int execute_strategies(int level) { +#ifdef LOG_IN_OUT + printf("--- execute_strategies >>>\n"); +#endif + f_ptr_t *strats = strat_all[level]; + char **strat_names = strat_all_names[level]; + int n_strat = n_strat_all[level]; + int n_candidates = count_candidates(); + int n_candidates_initial = n_candidates; + for ( int k = 0; + k < n_strat && keep_going() && n_candidates == n_candidates_initial; + k++ + ) { + (void)strats[k](); + n_candidates = count_candidates(); + if (n_candidates < n_candidates_initial) { + if (!backtracking) { + strats_used[n_strats_used] = level * 10 + k; + n_strats_used++; + } + if (!silent) { + printf("strategy: after '%s' the grid contains " + "%d solved cells\n\n", strat_names[k], count_solved() + ); + } + if (!silent) { display(); display_string(); } + } + } +#ifdef LOG_IN_OUT + printf("<<< execute_strategies ---\n"); +#endif + return (n_candidates < n_candidates_initial); + } diff --git a/sources/Solver/execute_strategies.h b/sources/Solver/execute_strategies.h new file mode 100644 index 0000000..f6e8429 --- /dev/null +++ b/sources/Solver/execute_strategies.h @@ -0,0 +1,13 @@ +/* execute_strategies.h + * + * Loops through all strategies of a unit as long as something happens. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef EXECUTE_STRATEGIES +#define EXECUTE_STRATEGIES + +int execute_strategies(int); + +#endif diff --git a/sources/Solver/footprint.c b/sources/Solver/footprint.c new file mode 100644 index 0000000..d49c315 --- /dev/null +++ b/sources/Solver/footprint.c @@ -0,0 +1,43 @@ +/* footprint.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "footprint.h" + +rc_p_t footprint(int row, int col, void *mem) { +#ifdef LOG_IN_OUT + printf("--- footprint >>>\n"); +#endif + + rc_p_t rc = (rc_p_t)mem; + int box = row/3*3+col/3; + + rc_p_t p = rc; + rc_p_t next; + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + if (k != row || j != col) { + if (k == row || j == col || k/3*3+j/3 == box) { + p->row = k; + p->col = j; + next = p + 1; + p->next = next; + p = next; + } // if (k == row.. + } // if (k.. + } // for (int j.. + } // for (int k.. + + // Terminate the chain by clearing the 'next' pointer of the last cell + p = rc + (FOOT_N - 1); + p->next = NULL; + +#ifdef LOG_IN_OUT + printf("<<< footprint ---\n"); +#endif + return rc; + } diff --git a/sources/Solver/footprint.h b/sources/Solver/footprint.h new file mode 100644 index 0000000..2375600 --- /dev/null +++ b/sources/Solver/footprint.h @@ -0,0 +1,33 @@ +/* footprint.h + * + * It returns the list of cells affected by a given cell. + * + * The cells affected are those that share either the row, the column, or the + * box with the given cell. After eliminating the duplicates, it makes a + * total of 20 cells. The function returns the coordinates by rows and then + * columns, with the lowest row ID first and, within each row, with the lowest + * column ID first. + * + * Parameters: + * 1: IN, row ID of the cell for which the footprint is to be determined. + * 2: IN, column ID of the cell for which the footprint is to be determined. + * 3: IN, pointer to an area of memory large enough to contain the list = + * sizeof(struct rc_struct) * 20. + * + * Development note: + * The chunk of memory in which to store the list is passed in as an argument + * to avoid allocating within the function the memory that must then be + * released outside the function. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef FOOTPRINT +#define FOOTPRINT +#include "def.h" + +#define FOOT_N 20 + +rc_p_t footprint(int p1, int p2, void* p3); + +#endif diff --git a/sources/Solver/hidden_pair.c b/sources/Solver/hidden_pair.c new file mode 100644 index 0000000..c3d01e5 --- /dev/null +++ b/sources/Solver/hidden_pair.c @@ -0,0 +1,29 @@ +/* hidden_pair.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "hidden_pair.h" +#include "hidden_pair_unit.h" + +int hidden_pair() { +#ifdef LOG_IN_OUT + printf("--- hidden_pair >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + if ( hidden_pair_unit("row", row[k]) + || hidden_pair_unit("column", col[k]) + || hidden_pair_unit("box", box[k]) + ) { + result = TRUE; + } + } +#ifdef LOG_IN_OUT + printf("<<< hidden_pair ---\n"); +#endif + return result; + } diff --git a/sources/Solver/hidden_pair.h b/sources/Solver/hidden_pair.h new file mode 100644 index 0000000..c927e89 --- /dev/null +++ b/sources/Solver/hidden_pair.h @@ -0,0 +1,15 @@ +/* hidden_pair.h + * + * Hidden pair strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef HIDDEN_PAIR +#define HIDDEN_PAIR + +int hidden_pair(void); + +#endif diff --git a/sources/Solver/hidden_pair_unit.c b/sources/Solver/hidden_pair_unit.c new file mode 100644 index 0000000..44a0cdb --- /dev/null +++ b/sources/Solver/hidden_pair_unit.c @@ -0,0 +1,77 @@ +/* hidden_pair_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "hidden_pair_unit.h" +#include "remove_candidate.h" + +int hidden_pair_unit(char *what, char unit[9][2]) { +#ifdef LOG_IN_OUT + printf("--- hidden_pair_unit (%s) >>>\n", what); +#endif + int result = FALSE; + int n[10] = {0}; + int coords[10][2][2]; + for (int j1 = 0; j1 < 9; j1++) { + int kR = unit[j1][ROW]; + int kC = unit[j1][COL]; + char *elem = grid[kR][kC]; + if (elem[0] > 1) { + for (int i = 1; i <= 9; i++) { + if (elem[i] != FALSE) { + if (n[i] < 2) { + coords[i][n[i]][ROW] = kR; + coords[i][n[i]][COL] = kC; + } + n[i]++; + } // if (elem[i].. + } // for (int i.. + } // if (elem[0].. + } // for (int j1.. + + for (int i = 1; i <= 9; i++) { + if (n[i] == 2) { + int log_printed = FALSE; + for (int ii = i+1; ii <= 9; ii++) { + if (n[ii] == 2 + && coords[i][0][ROW] == coords[ii][0][ROW] + && coords[i][0][COL] == coords[ii][0][COL] + && coords[i][1][ROW] == coords[ii][1][ROW] + && coords[i][1][COL] == coords[ii][1][COL] + ) { + for (int kCell = 0; kCell < 2; kCell++) { + int kR = coords[i][kCell][ROW]; + int kC = coords[i][kCell][COL]; + char *elem = grid[kR][kC]; + if (elem[0] > 2) { + result = TRUE; +#ifdef LOG_HIDDEN_PAIR + if (log_printed == FALSE && !silent) { + printf("hidden_pair_unit: %d and %d are only in (%d,%d) and (%d,%d) of the same %s\n", + i, ii, coords[i][0][0], coords[i][0][1], coords[i][1][0], coords[i][1][1], what + ); + log_printed = TRUE; + } +#endif + for (int iii = 1; iii <= 9; iii++) { + if (elem[iii] != FALSE && i != iii && ii != iii) { + remove_candidate("hidden_pair_unit", iii, kR, kC); + } + } + } // if (elem[0].. + } // for (int kCell.. + } // if (n[ii].. + } // for (int ii.. + } // if (n[i].. + } // for (int i.. + +#ifdef LOG_IN_OUT + printf("<<< hidden_pair_unit (%s) ---\n", what); +#endif + return result; + } diff --git a/sources/Solver/hidden_pair_unit.h b/sources/Solver/hidden_pair_unit.h new file mode 100644 index 0000000..17e005c --- /dev/null +++ b/sources/Solver/hidden_pair_unit.h @@ -0,0 +1,15 @@ +/* hidden_pair_unit.h + * + * Applies the hidden pair strategy to a unit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef HIDDEN_PAIR_UNIT +#define HIDDEN_PAIR_UNIT + +int hidden_pair_unit(char*, char[9][2]); + +#endif diff --git a/sources/Solver/hidden_triple.c b/sources/Solver/hidden_triple.c new file mode 100644 index 0000000..007c6f5 --- /dev/null +++ b/sources/Solver/hidden_triple.c @@ -0,0 +1,29 @@ +/* hidden_triple.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "hidden_triple.h" +#include "hidden_triple_unit.h" + +int hidden_triple() { +#ifdef LOG_IN_OUT + printf("--- hidden_triple >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + if ( hidden_triple_unit("row", row[k]) + || hidden_triple_unit("column", col[k]) + || hidden_triple_unit("box", box[k]) + ) { + result = TRUE; + } + } +#ifdef LOG_IN_OUT + printf("<<< hidden_triple ---\n"); +#endif + return result; + } diff --git a/sources/Solver/hidden_triple.h b/sources/Solver/hidden_triple.h new file mode 100644 index 0000000..f661257 --- /dev/null +++ b/sources/Solver/hidden_triple.h @@ -0,0 +1,15 @@ +/* hidden_triple.h + * + * Hidden triple strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef HIDDEN_TRIPLE +#define HIDDEN_TRIPLE + +int hidden_triple(void); + +#endif diff --git a/sources/Solver/hidden_triple_unit.c b/sources/Solver/hidden_triple_unit.c new file mode 100644 index 0000000..c3fa560 --- /dev/null +++ b/sources/Solver/hidden_triple_unit.c @@ -0,0 +1,92 @@ +/* hidden_triple_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "hidden_triple_unit.h" +#include "remove_candidate.h" + +int hidden_triple_unit(char *what, char unit[9][2]) { +#ifdef LOG_IN_OUT + printf("--- hidden_triple_unit (%s) >>>\n", what); +#endif + int result = FALSE; + int n[10] = {0}; + int coords[10][3][2]; + for (int j1 = 0; j1 < 9; j1++) { + int kR = unit[j1][ROW]; + int kC = unit[j1][COL]; + char *elem = grid[kR][kC]; + if (elem[0] > 1) { + for (int i = 1; i <= 9; i++) { + if (elem[i] != FALSE) { + if (n[i] < 3) { + coords[i][n[i]][ROW] = kR; + coords[i][n[i]][COL] = kC; + } + n[i]++; + } // if (elem[i].. + } // for (int i.. + } // if (elem[0].. + } // for (int j1.. + + for (int i1 = 1; i1 <= 9; i1++) { + if (n[i1] == 3) { + int log_printed = FALSE; + for (int i2 = i1+1; i2 <= 9; i2++) { + if ( n[i2] == 3 + && coords[i1][0][ROW] == coords[i2][0][ROW] + && coords[i1][0][COL] == coords[i2][0][COL] + && coords[i1][1][ROW] == coords[i2][1][ROW] + && coords[i1][1][COL] == coords[i2][1][COL] + && coords[i1][2][ROW] == coords[i2][2][ROW] + && coords[i1][2][COL] == coords[i2][2][COL] + ) { + for (int i3 = i2+1; i3 <= 9; i3++) { + if ( n[i3] == 3 + && coords[i1][0][ROW] == coords[i3][0][ROW] + && coords[i1][0][COL] == coords[i3][0][COL] + && coords[i1][1][ROW] == coords[i3][1][ROW] + && coords[i1][1][COL] == coords[i3][1][COL] + && coords[i1][2][ROW] == coords[i3][2][ROW] + && coords[i1][2][COL] == coords[i3][2][COL] + ) { + for (int kCell = 0; kCell < 3; kCell++) { + int kRow = coords[i1][kCell][ROW]; + int kCol = coords[i1][kCell][COL]; + char *elem = grid[kRow][kCol]; + if (elem[0] > 3) { + result = TRUE; +#ifdef LOG_HIDDEN_TRIPLE + if (log_printed == FALSE && !silent) { + printf("hidden_triple_unit: %d, %d, and %d are only in " + "(%d,%d), (%d,%d), and (%d,%d) of the same %s\n", + i1, i2, i3, coords[i1][0][0], coords[i1][0][1], + coords[i1][1][0], coords[i1][1][1], + coords[i1][2][0], coords[i1][2][1], what + ); + log_printed = TRUE; + } +#endif + for (int ki = 1; ki <= 9; ki++) { + if (elem[ki] && ki != i1 && ki != i2 && ki != i3) { + remove_candidate("hidden_triple_unit", ki, kRow, kCol); + } + } + } // if (elem[0].. + } // for (int kCell.. + } // if (n[i3].. + } // for (int i3.. + } // if (n[i2].. + } // for (int i2.. + } // if (n[i1].. + } // for (int i1.. + +#ifdef LOG_IN_OUT + printf("<<< hidden_triple_unit (%s) ---\n", what); +#endif + return result; + } diff --git a/sources/Solver/hidden_triple_unit.h b/sources/Solver/hidden_triple_unit.h new file mode 100644 index 0000000..e2a159f --- /dev/null +++ b/sources/Solver/hidden_triple_unit.h @@ -0,0 +1,15 @@ +/* hidden_triple_unit.h + * + * Applies the hidden triple strategy to a unit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef HIDDEN_TRIPLE_UNIT +#define HIDDEN_TRIPLE_UNIT + +int hidden_triple_unit(char*, char[9][2]); + +#endif diff --git a/sources/Solver/inconsistent_grid.c b/sources/Solver/inconsistent_grid.c new file mode 100644 index 0000000..a16f401 --- /dev/null +++ b/sources/Solver/inconsistent_grid.c @@ -0,0 +1,24 @@ +/* inconsistent_grid.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "inconsistent_grid.h" +#include "inconsistent_unit.h" + +int inconsistent_grid() { + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + result |= inconsistent_unit("row", k, row[k]); + if (!result) { + result |= inconsistent_unit("column", k, col[k]); + if (!result) { + result |= inconsistent_unit("box", k, box[k]); + } + } + } // for (int k.. + return result; + } diff --git a/sources/Solver/inconsistent_grid.h b/sources/Solver/inconsistent_grid.h new file mode 100644 index 0000000..29dade5 --- /dev/null +++ b/sources/Solver/inconsistent_grid.h @@ -0,0 +1,14 @@ +/* inconsistent_grid.h + * + * Checks that there are no repeated solutions. + * Returns TRUE if it finds a problem. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef INCONSISTENT_GRID +#define INCONSISTENT_GRID + +int inconsistent_grid(); + +#endif diff --git a/sources/Solver/inconsistent_unit.c b/sources/Solver/inconsistent_unit.c new file mode 100644 index 0000000..7e84ed1 --- /dev/null +++ b/sources/Solver/inconsistent_unit.c @@ -0,0 +1,40 @@ +/* inconsistent_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "inconsistent_unit.h" + +int inconsistent_unit(char *what, int kG, char unit[9][2]) { + int result = FALSE; + int i_vect[10] = {0}; + for (int k = 0; k < 9 && !result; k++) { + int kR = unit[k][ROW]; + int kC = unit[k][COL]; + char *elem = grid[kR][kC]; + if (elem[0] < 1) { // we have an empty cell + result = TRUE; + if (!silent) { + printf("*** (%d,%d) has %d candidates\n", kR, kC, elem[0]); + } + } // if (elem[0].. + else if (elem[0] == 1) { + int i = 0; + do { i++; } while (!elem[i]); + if (i_vect[i] == FALSE) { + i_vect[i] = TRUE; + } + else { // we have a duplicate solution + result = TRUE; + if (!silent) { + printf("*** More than a single %d solution in %s %d\n", i, what, kG); + } + } // else.. + } // else if (elem[0].. + } // for (int k.. + problem_found = result; + return result; + } diff --git a/sources/Solver/inconsistent_unit.h b/sources/Solver/inconsistent_unit.h new file mode 100644 index 0000000..edc5967 --- /dev/null +++ b/sources/Solver/inconsistent_unit.h @@ -0,0 +1,15 @@ +/* inconsistent_unit.h + * + * Checks that there are no repeated solutions within a unit and that all + * cells have at least a candidate. + * Returns TRUE if it finds a problem. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef INCONSISTENT_UNIT +#define INCONSISTENT_UNIT + +int inconsistent_unit(char*, int, char[9][2]); + +#endif diff --git a/sources/Solver/init.c b/sources/Solver/init.c new file mode 100644 index 0000000..47a21d2 --- /dev/null +++ b/sources/Solver/init.c @@ -0,0 +1,66 @@ +/* init.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "init.h" + +void init(char *arg) { +#ifdef LOG_IN_OUT + printf("--- init >>>\n"); +#endif + + // Initialize the sudoku arrays + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + grid[k][j][0] = 9; + for (int i = 1; i <= 9; i++) { + grid[k][j][i] = TRUE; + } + row[k][j][0] = k; + row[k][j][1] = j; + col[j][k][0] = k; + col[j][k][1] = j; + box[k/3*3+j/3][k%3*3+j%3][0] = k; + box[k/3*3+j/3][k%3*3+j%3][1] = j; + } + } + + // Save the sudoku string into the array + for (int k = 0; k < 81; k++) { + int kR = k / 9; + int kC = k - kR * 9; + if (arg[k] != '0') { + for (int i = 1; i <= 9; i++) { + grid[kR][kC][i] = FALSE; + } + grid[kR][kC][arg[k] - '0'] = TRUE; + grid[kR][kC][0] = 1; + } + } + +/* + // Display the allocated numbers + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + if (grid[k][j][0] == 1) { + int i = 0; + do { + i++; + } while (grid[k][j][i] == 0); + printf("%d", i); + } + else { + printf("."); + } + } + printf("\n"); + } +*/ +#ifdef LOG_IN_OUT + printf("<<< init ---\n"); +#endif + } diff --git a/sources/Solver/init.h b/sources/Solver/init.h new file mode 100644 index 0000000..3bf1b15 --- /dev/null +++ b/sources/Solver/init.h @@ -0,0 +1,15 @@ +/* init.h + * + * Initialises a sudoku grid. + * + * Returns the number of solved cells. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef INIT +#define INIT + +void init(char*); + +#endif diff --git a/sources/Solver/intersection.c b/sources/Solver/intersection.c new file mode 100644 index 0000000..5262690 --- /dev/null +++ b/sources/Solver/intersection.c @@ -0,0 +1,72 @@ +/* intersection.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "footprint.h" +#include "intersection.h" + +rc_p_t intersection(int r1, int c1, int r2, int c2, void *mem) { +#ifdef LOG_IN_OUT + printf("--- intersection (%d,%d) (%d,%d) >>>\n", r1, c1, r2, c2); +#endif + + int cells_found = FALSE; + rc_p_t retval = (rc_p_t)mem; + rc_p_t foot1; + rc_p_t foot2; + + // Allocate the memory for the two footprints + void *mem_foots = malloc((FOOT_N * sizeof(rc_struct_t)) << 1); + if (mem_foots != NULL) { + rc_p_t mem1 = (rc_p_t)mem_foots; + rc_p_t mem2 = (rc_p_t)(mem_foots + FOOT_N * sizeof(rc_struct_t)); + + // Obtain the two footprints + foot1 = footprint(r1, c1, mem1); + foot2 = footprint(r2, c2, mem2); + } + else { + printf("*** intersection (%d,%d) (%d,%d): malloc failure\n", + r1, c1, r2, c2 + ); + exit(EXIT_FAILURE); + } + + // Build the list of cells common to the two footprints + rc_p_t p = retval; + rc_p_t p1 = foot1; + rc_p_t p_prev = NULL; + rc_p_t p_temp; + do { + rc_p_t p2 = foot2; + do { + if (p2->row == p1->row && p2->col == p1->col) { + cells_found = TRUE; + p->row = p1->row; + p->col = p1->col; + p_prev = p; + p_temp = p + 1; + p->next = p_temp; + p = p_temp; + } + p_temp = p2->next; + p2 = p_temp; + } while (p2 != NULL); + p_temp = p1->next; + p1 = p_temp; + } while (p1 != NULL); + + // Terminate the chain by clearing the 'next' pointer of the last cell + p_prev->next = NULL; + + free(mem_foots); + +#ifdef LOG_IN_OUT + printf("<<< intersection(%d,%d) (%d,%d) ---\n", r1, c1, r2, c2); +#endif + return (cells_found) ? retval : NULL; + } diff --git a/sources/Solver/intersection.h b/sources/Solver/intersection.h new file mode 100644 index 0000000..e6d7bb5 --- /dev/null +++ b/sources/Solver/intersection.h @@ -0,0 +1,38 @@ +/* intersection.h + * + * It returns the list of cells affected by two distinct cells or NULL if + * the intersection is empty. + * The coordinates are ordered by rows and then by columns, with the lowest + * row ID first and, within each row, with the lowest column ID first. + * + * Two cells can affect different numbers of cells, depending on how their + * units overlap: + * No overlapping: 2 cells, (r1,c2) and (r2,c1). + * Line overlapping: 7 cells, (r1,!c1 && !c2) or (!r1 && !r2, c1). + * box overlapping: 7 cells, all within the box except the two. + * box and line overlapping: 13 cells, all within the box except the + * two, plus all within the line except the one within the same box. + * + * Parameters: + * 1: IN, row ID of cell 1. + * 2: IN, column ID of cell 1. + * 3: IN, row ID of cell 2. + * 4: IN, column ID of cell 2. + * 5: IN, pointer to an area of memory large enough to contain the list = + * sizeof(struct rc_struct) * 13. + * + * Development note: + * The chunk of memory in which to store the list is passed in as an argument + * to avoid allocating within the function the memory that must then be + * released outside the function. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef INTERSECTION +#define INTERSECTION +#include "def.h" + +rc_p_t intersection(int p1, int p2, int p3, int p4, void* p5); + +#endif diff --git a/sources/Solver/keep_going.c b/sources/Solver/keep_going.c new file mode 100644 index 0000000..d268676 --- /dev/null +++ b/sources/Solver/keep_going.c @@ -0,0 +1,13 @@ +/* keep_going.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "count_solved.h" +#include "def.h" + +int keep_going(void) { + return (count_solved() < 81 && !problem_found); + } diff --git a/sources/Solver/keep_going.h b/sources/Solver/keep_going.h new file mode 100644 index 0000000..d6325a0 --- /dev/null +++ b/sources/Solver/keep_going.h @@ -0,0 +1,14 @@ +/* keep_going.h + * + * It checks for errors and whether the puzzle has been completed. + * It returns TRUE if there are no errors and the puzzle has not been completed. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef KEEP_GOING +#define KEEP_GOING + +int keep_going(void); + +#endif diff --git a/sources/Solver/lines.c b/sources/Solver/lines.c new file mode 100644 index 0000000..6bfddc9 --- /dev/null +++ b/sources/Solver/lines.c @@ -0,0 +1,93 @@ +/* lines.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "lines.h" +#include "remove_candidate.h" + +int lines(int kRC, char *comb, int n, int i) { +#ifdef LOG_IN_OUT + printf("--- lines (%s) [", unit_names[kRC]); + for (int k = 0; k < n; k++) printf("%d", comb[k]); + printf("] %d >>>\n", i); +#endif + int result = FALSE; + int list[9] = {0}; + int n_list = 0; + int abort = FALSE; + for (int k = 0; k < n && !abort; k++) { + int kk[2]; + kk[0] = comb[k]; + for (int j = 0; j < 9 && !abort; j++) { + kk[1] = j; + int kR = kk[kRC]; + int kC = kk[1-kRC]; + char *elem = grid[kR][kC]; + if (elem[i]) { + if (elem[0] == 1) { + abort = TRUE; + } + else { + if (!list[j]) n_list++; + list[j] = TRUE; + } + } // if (elem[i].. + } // for (int j.. + } // for (int k.. + + if (!abort && n_list == n) { + int log_printed = FALSE; + for (int jj = 0; jj < 9; jj++) { + if (list[jj]) { + int kk[2]; + kk[1] = jj; + for (int k = 0; k < 9; k++) { + if (memchr(comb, k, n) == NULL) { + kk[0] = k; + int kR = kk[kRC]; + int kC = kk[1-kRC]; + char *elem = grid[kR][kC]; + + // As usual, we assume that the Sudoku is not corrupted. It means + // that 'i' doesn't solve any of the cells we are considering. + // Otherwise, the candidate for 'i' would have already been removed + // from the row, and the column ID would have not been saved in list. + if (elem[i]) { + result = TRUE; +#ifdef LOG_LINES + if (!log_printed && !silent) { + printf("lines(%d): the %ss", n, unit_names[kRC]); + for (int k1 = 0; k1 < n; k1++) printf(" %d", comb[k1]); + printf(" let us eliminate %d from the %ss", i, + unit_names[1-kRC] + ); + for (int k1 = 0; k1 < 9; k1++) { + if (list[k1]) printf(" %d", k1); + } + printf("\n"); + log_printed = TRUE; + } +#endif + remove_candidate("lines", i, kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } // if (elem[i].. + } // if (memchr(.. + } // for (int k.. + } // if (list[jj].. + } // for (int jj.. + } // if (!abort.. +#ifdef LOG_IN_OUT + printf("<<< lines (%s) [", unit_names[kRC]); + for (int k = 0; k < n; k++) printf("%d", comb[k]); + printf("] %d ---\n", i); +#endif + return result; + } diff --git a/sources/Solver/lines.h b/sources/Solver/lines.h new file mode 100644 index 0000000..eac643e --- /dev/null +++ b/sources/Solver/lines.h @@ -0,0 +1,15 @@ +/* lines.h + * + * Multiple-line strategy applied to a combination of lines and a digit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef LINES +#define LINES + +int lines(int, char*, int, int); + +#endif diff --git a/sources/Solver/lines_2.c b/sources/Solver/lines_2.c new file mode 100644 index 0000000..447bb82 --- /dev/null +++ b/sources/Solver/lines_2.c @@ -0,0 +1,35 @@ +/* lines_2.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "lines.h" +#include "lines_2.h" + +int lines_2() { +#ifdef LOG_IN_OUT + printf("--- lines_2 >>>\n"); +#endif + int result = FALSE; + char comb[2]; + + // Try all 9*8 / 2! = 36 combinations + for (int kRC = 0; kRC < 2 && !result; kRC++) { + for (int k1 = 0; k1 < 8 && !result; k1++) { + for (int k2 = k1+1; k2 < 9 && !result; k2++) { + comb[0] = k1; + comb[1] = k2; + for (int i = 1; i <= 9 && !result; i++) { + result |= lines(kRC, comb, 2, i); + } + } // for (int k2.. + } // for (int k1.. + } // for int kRC.. +#ifdef LOG_IN_OUT + printf("<<< lines_2 ---\n"); +#endif + return result; + } diff --git a/sources/Solver/lines_2.h b/sources/Solver/lines_2.h new file mode 100644 index 0000000..233b7bf --- /dev/null +++ b/sources/Solver/lines_2.h @@ -0,0 +1,15 @@ +/* lines_2.h + * + * Two-line strategy (X-wing). + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef LINES_2 +#define LINES_2 + +int lines_2(void); + +#endif diff --git a/sources/Solver/lines_3.c b/sources/Solver/lines_3.c new file mode 100644 index 0000000..9f4525b --- /dev/null +++ b/sources/Solver/lines_3.c @@ -0,0 +1,38 @@ +/* lines_3.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "lines.h" +#include "lines_3.h" + +int lines_3() { +#ifdef LOG_IN_OUT + printf("--- lines_3 >>>\n"); +#endif + int result = FALSE; + char comb[3]; + + // Try all 9*8*7 / 3! = 84 combinations + for (int kRC = 0; kRC < 2 && !result; kRC++) { + for (int k1 = 0; k1 < 7 && !result; k1++) { + for (int k2 = k1+1; k2 < 8 && !result; k2++) { + for (int k3 = k2+1; k3 < 9 && !result; k3++) { + comb[0] = k1; + comb[1] = k2; + comb[2] = k3; + for (int i = 1; i <= 9 && !result; i++) { + result |= lines(kRC, comb, 3, i); + } + } // for (int k3.. + } // for (int k2.. + } // for (int k1.. + } // for int kRC.. +#ifdef LOG_IN_OUT + printf("<<< lines_3 ---\n"); +#endif + return result; + } diff --git a/sources/Solver/lines_3.h b/sources/Solver/lines_3.h new file mode 100644 index 0000000..4481552 --- /dev/null +++ b/sources/Solver/lines_3.h @@ -0,0 +1,15 @@ +/* lines_3.h + * + * Three-line strategy (Swordfish). + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef LINES_3 +#define LINES_3 + +int lines_3(void); + +#endif diff --git a/sources/Solver/lines_4.c b/sources/Solver/lines_4.c new file mode 100644 index 0000000..ba10825 --- /dev/null +++ b/sources/Solver/lines_4.c @@ -0,0 +1,41 @@ +/* lines_4.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "lines.h" +#include "lines_4.h" + +int lines_4() { +#ifdef LOG_IN_OUT + printf("--- lines_4 >>>\n"); +#endif + int result = FALSE; + char comb[4]; + + // Try all 9*8*7*6 / 4! = 126 combinations + for (int kRC = 0; kRC < 2 && !result; kRC++) { + for (int k1 = 0; k1 < 6 && !result; k1++) { + for (int k2 = k1+1; k2 < 7 && !result; k2++) { + for (int k3 = k2+1; k3 < 8 && !result; k3++) { + for (int k4 = k3+1; k4 < 9 && !result; k4++) { + comb[0] = k1; + comb[1] = k2; + comb[2] = k3; + comb[3] = k4; + for (int i = 1; i <= 9 && !result; i++) { + result |= lines(kRC, comb, 4, i); + } + } // for (int k4.. + } // for (int k3.. + } // for (int k2.. + } // for (int k1.. + } // for int kRC.. +#ifdef LOG_IN_OUT + printf("<<< lines_4 ---\n"); +#endif + return result; + } diff --git a/sources/Solver/lines_4.h b/sources/Solver/lines_4.h new file mode 100644 index 0000000..9a73423 --- /dev/null +++ b/sources/Solver/lines_4.h @@ -0,0 +1,15 @@ +/* lines_4.h + * + * Four-line strategy (Jellyfish). + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef LINES_4 +#define LINES_4 + +int lines_4(void); + +#endif diff --git a/sources/Solver/naked_pair.c b/sources/Solver/naked_pair.c new file mode 100644 index 0000000..546e606 --- /dev/null +++ b/sources/Solver/naked_pair.c @@ -0,0 +1,29 @@ +/* naked_pair.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "naked_pair.h" +#include "naked_pair_unit.h" + +int naked_pair() { +#ifdef LOG_IN_OUT + printf("--- naked_pair >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + if ( naked_pair_unit("row", row[k]) + || naked_pair_unit("column", col[k]) + || naked_pair_unit("box", box[k]) + ) { + result = TRUE; + } + } +#ifdef LOG_IN_OUT + printf("<<< naked_pair ---\n"); +#endif + return result; + } diff --git a/sources/Solver/naked_pair.h b/sources/Solver/naked_pair.h new file mode 100644 index 0000000..afa1dd8 --- /dev/null +++ b/sources/Solver/naked_pair.h @@ -0,0 +1,15 @@ +/* naked_pair.h + * + * Naked pair strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef NAKED_PAIR +#define NAKED_PAIR + +int naked_pair(void); + +#endif diff --git a/sources/Solver/naked_pair_unit.c b/sources/Solver/naked_pair_unit.c new file mode 100644 index 0000000..5424608 --- /dev/null +++ b/sources/Solver/naked_pair_unit.c @@ -0,0 +1,96 @@ +/* naked_pair_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "naked_pair_unit.h" +#include "remove_candidate.h" + +int naked_pair_unit(char *what, char unit[9][2]) { +#ifdef LOG_IN_OUT + printf("--- naked_pair_unit (%s) >>>\n", what); +#endif + int result = FALSE; + int i1[9] = {0}; + int i2[9] = {0}; + int kRow[9] = {0}; + int kCol[9] = {0}; + int kk[9] = {0}; + int n_pair = 0; + + // Make a list of the cells with naked pairs. + for (int k = 0; k < 9; k++) { + int kR = unit[k][0]; + int kC = unit[k][1]; + char *elem = grid[kR][kC]; + if (elem[0] == 2) { + kRow[n_pair] = kR; + kCol[n_pair] = kC; + kk[n_pair] = k; + for (int i = 1; i <= 9 && i2[n_pair] == 0; i++) { + if (elem[i] == TRUE) { + if (i1[n_pair] == 0) { + i1[n_pair] = i; + } + else { + i2[n_pair] = i; + } + } // if (elem[i].. + } // for (int i.. + n_pair++; + } // if (elem[0].. + } // for (int k.. + + if (n_pair > 1) { + for (int k1 = 0; k1 < n_pair - 1; k1++) { + for (int k2 = k1 + 1; k2 < n_pair; k2++) { + if (i1[k1] == i1[k2] && i2[k1] == i2[k2]) { + int printed = FALSE; + for (int k = 0; k < 9; k++) { + if (k != kk[k1] && k != kk[k2]) { + int kR = unit[k][0]; + int kC = unit[k][1]; + char *elem = grid[kR][kC]; + int i_remove[2]; + int n_remove = 0; + if (elem[i1[k1]] == TRUE) { + i_remove[n_remove] = i1[k1]; + n_remove++; + result = TRUE; + } + if (elem[i2[k1]] == TRUE) { + i_remove[n_remove] = i2[k1]; + n_remove++; + result = TRUE; + } +#ifdef LOG_NAKED_PAIR + if (n_remove > 0 && !printed && !silent) { + printf("naked_pair_unit: (%d,%d) and (%d,%d) in the same %s" + " only contain %d and %d\n", kRow[k1], kCol[k1], kRow[k2], + kCol[k2], what, i1[k1], i2[k1] + ); + printed = TRUE; + } +#endif + for (int ki = 0; ki < n_remove; ki++) { + remove_candidate("naked_pair_unit", i_remove[ki], kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } + } // if (k.. + } // for (int k.. + } // if (i1[k1].. + } // for (int k2.. + } // for (int k1.. + } // if (n_pair.. + +#ifdef LOG_IN_OUT + printf("<<< naked_pair_unit (%s) ---\n", what); +#endif + return result; + } diff --git a/sources/Solver/naked_pair_unit.h b/sources/Solver/naked_pair_unit.h new file mode 100644 index 0000000..6f354b1 --- /dev/null +++ b/sources/Solver/naked_pair_unit.h @@ -0,0 +1,15 @@ +/* naked_pair_unit.h + * + * Applies the naked pair strategy to a unit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef NAKED_PAIR_UNIT +#define NAKED_PAIR_UNIT + +int naked_pair_unit(char*, char[9][2]); + +#endif diff --git a/sources/Solver/naked_quad.c b/sources/Solver/naked_quad.c new file mode 100644 index 0000000..432c26a --- /dev/null +++ b/sources/Solver/naked_quad.c @@ -0,0 +1,29 @@ +/* naked_quad.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "naked_quad.h" +#include "naked_quad_unit.h" + +int naked_quad() { +#ifdef LOG_IN_OUT + printf("--- naked_quad >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + if ( naked_quad_unit("row", row[k]) + || naked_quad_unit("column", col[k]) + || naked_quad_unit("box", box[k]) + ) { + result = TRUE; + } + } +#ifdef LOG_IN_OUT + printf("<<< naked_quad ---\n"); +#endif + return result; + } diff --git a/sources/Solver/naked_quad.h b/sources/Solver/naked_quad.h new file mode 100644 index 0000000..419a1f8 --- /dev/null +++ b/sources/Solver/naked_quad.h @@ -0,0 +1,15 @@ +/* naked_quad.h + * + * Naked quad strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef NAKED_QUAD +#define NAKED_QUAD + +int naked_quad(void); + +#endif diff --git a/sources/Solver/naked_quad_unit.c b/sources/Solver/naked_quad_unit.c new file mode 100644 index 0000000..edbd88e --- /dev/null +++ b/sources/Solver/naked_quad_unit.c @@ -0,0 +1,125 @@ +/* naked_quad_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "naked_quad_unit.h" +#include "remove_candidate.h" + +int naked_quad_unit(char *what, char unit[9][2]) { +#ifdef LOG_IN_OUT + printf("--- naked_quad_unit (%s) >>>\n", what); +#endif + int result = FALSE; + int i1[9] = {0}; + int i2[9] = {0}; + int i3[9] = {0}; + int i4[9] = {0}; + int kRow[9] = {0}; + int kCol[9] = {0}; + int kk[9] = {0}; + int n_quad = 0; + + // Make a list of the cells with naked quads. + for (int k = 0; k < 9; k++) { + int kR = unit[k][0]; + int kC = unit[k][1]; + char *elem = grid[kR][kC]; + if (elem[0] == 4) { + kRow[n_quad] = kR; + kCol[n_quad] = kC; + kk[n_quad] = k; + for (int i = 1; i <= 9 && i4[n_quad] == 0; i++) { + if (elem[i] == TRUE) { + if (i1[n_quad] == 0) { + i1[n_quad] = i; + } + else if (i2[n_quad] == 0) { + i2[n_quad] = i; + } + else if (i3[n_quad] == 0) { + i3[n_quad] = i; + } + else { + i4[n_quad] = i; + } + } // if (elem[i].. + } // for (int i.. + n_quad++; + } // if (elem[0].. + } // for (int k.. + + if (n_quad > 3) { + for (int k1 = 0; k1 < n_quad - 3; k1++) { + for (int k2 = k1 + 1; k2 < n_quad - 2; k2++) { + for (int k3 = k2 + 1; k3 < n_quad - 1; k3++) { + for (int k4 = k3 + 1; k4 < n_quad; k4++) { + if ( i1[k1] == i1[k2] && i1[k1] == i1[k3] && i1[k1] == i1[k4] + && i2[k1] == i2[k2] && i2[k1] == i2[k3] && i2[k1] == i2[k4] + && i3[k1] == i3[k2] && i3[k1] == i3[k3] && i3[k1] == i3[k4] + && i4[k1] == i4[k2] && i4[k1] == i4[k3] && i4[k1] == i4[k4] + ) { + int printed = FALSE; + for (int k = 0; k < 9; k++) { + if (k != kk[k1] && k != kk[k2] && k != kk[k3] && k != kk[k4]) { + int kR = unit[k][0]; + int kC = unit[k][1]; + char *elem = grid[kR][kC]; + int i_remove[4]; + int n_remove = 0; + if (elem[i1[k1]] == TRUE) { + i_remove[n_remove] = i1[k1]; + n_remove++; + result = TRUE; + } + if (elem[i2[k1]] == TRUE) { + i_remove[n_remove] = i2[k1]; + n_remove++; + result = TRUE; + } + if (elem[i3[k1]] == TRUE) { + i_remove[n_remove] = i3[k1]; + n_remove++; + result = TRUE; + } + if (elem[i4[k1]] == TRUE) { + i_remove[n_remove] = i4[k1]; + n_remove++; + result = TRUE; + } +#ifdef LOG_NAKED_QUAD + if (n_remove > 0 && !printed && !silent) { + printf("naked_quad_unit: (%d,%d), (%d,%d), (%d,%d)," + " and (%d,%d)" + " in the same %s only contain %d, %d, %d, and %d\n", + kRow[k1], kCol[k1], kRow[k2], kCol[k2], kRow[k3], + kCol[k3], kRow[k4], kCol[k4], what, i1[k1], i2[k1], + i3[k1], i4[k1] + ); + printed = TRUE; + } +#endif + for (int ki = 0; ki < n_remove; ki++) { + remove_candidate("naked_quad_unit", i_remove[ki], kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } + } // if (k.. + } // for (int k.. + } // if (i1[k1].. + } // for (int k4.. + } // for (int k3.. + } // for (int k2.. + } // for (int k1.. + } // if (n_quad.. + +#ifdef LOG_IN_OUT + printf("<<< naked_quad_unit (%s) ---\n", what); +#endif + return result; + } diff --git a/sources/Solver/naked_quad_unit.h b/sources/Solver/naked_quad_unit.h new file mode 100644 index 0000000..0e32758 --- /dev/null +++ b/sources/Solver/naked_quad_unit.h @@ -0,0 +1,15 @@ +/* naked_quad_unit.h + * + * Applies the naked quad strategy to a unit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef NAKED_QUAD_UNIT +#define NAKED_QUAD_UNIT + +int naked_quad_unit(char*, char[9][2]); + +#endif diff --git a/sources/Solver/naked_triple.c b/sources/Solver/naked_triple.c new file mode 100644 index 0000000..f63c953 --- /dev/null +++ b/sources/Solver/naked_triple.c @@ -0,0 +1,29 @@ +/* naked_triple.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "naked_triple.h" +#include "naked_triple_unit.h" + +int naked_triple() { +#ifdef LOG_IN_OUT + printf("--- naked_triple >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + if ( naked_triple_unit("row", row[k]) + || naked_triple_unit("column", col[k]) + || naked_triple_unit("box", box[k]) + ) { + result = TRUE; + } + } +#ifdef LOG_IN_OUT + printf("<<< naked_triple ---\n"); +#endif + return result; + } diff --git a/sources/Solver/naked_triple.h b/sources/Solver/naked_triple.h new file mode 100644 index 0000000..cc21236 --- /dev/null +++ b/sources/Solver/naked_triple.h @@ -0,0 +1,15 @@ +/* naked_triple.h + * + * Naked triple strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef NAKED_TRIPLE +#define NAKED_TRIPLE + +int naked_triple(void); + +#endif diff --git a/sources/Solver/naked_triple_unit.c b/sources/Solver/naked_triple_unit.c new file mode 100644 index 0000000..452ad2d --- /dev/null +++ b/sources/Solver/naked_triple_unit.c @@ -0,0 +1,114 @@ +/* naked_triple_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "naked_triple_unit.h" +#include "remove_candidate.h" + +int naked_triple_unit(char *what, char unit[9][2]) { +#ifdef LOG_IN_OUT + printf("--- naked_triple_unit (%s) >>>\n", what); +#endif + int result = FALSE; + int i1[9] = {0}; + int i2[9] = {0}; + int i3[9] = {0}; + int kRow[9] = {0}; + int kCol[9] = {0}; + int kk[9] = {0}; + int n_triple = 0; + + // Make a list of the cells with naked triples. + for (int k = 0; k < 9; k++) { + int kR = unit[k][0]; + int kC = unit[k][1]; + char *elem = grid[kR][kC]; + if (elem[0] == 3) { + kRow[n_triple] = kR; + kCol[n_triple] = kC; + kk[n_triple] = k; + for (int i = 1; i <= 9 && i3[n_triple] == 0; i++) { + if (elem[i] == TRUE) { + if (i1[n_triple] == 0) { + i1[n_triple] = i; + } + else if (i2[n_triple] == 0) { + i2[n_triple] = i; + } + else { + i3[n_triple] = i; + } + } // if (elem[i].. + } // for (int i.. + n_triple++; + } // if (elem[0].. + } // for (int k.. + + if (n_triple > 2) { + for (int k1 = 0; k1 < n_triple - 2; k1++) { + for (int k2 = k1 + 1; k2 < n_triple - 1; k2++) { + for (int k3 = k2 + 1; k3 < n_triple; k3++) { + if ( i1[k1] == i1[k2] && i1[k1] == i1[k3] + && i2[k1] == i2[k2] && i2[k1] == i2[k3] + && i3[k1] == i3[k2] && i3[k1] == i3[k3] + ) { + int printed = FALSE; + for (int k = 0; k < 9; k++) { + if (k != kk[k1] && k != kk[k2] && k != kk[k3]) { + int kR = unit[k][0]; + int kC = unit[k][1]; + char *elem = grid[kR][kC]; + int i_remove[3]; + int n_remove = 0; + if (elem[i1[k1]] == TRUE) { + i_remove[n_remove] = i1[k1]; + n_remove++; + result = TRUE; + } + if (elem[i2[k1]] == TRUE) { + i_remove[n_remove] = i2[k1]; + n_remove++; + result = TRUE; + } + if (elem[i3[k1]] == TRUE) { + i_remove[n_remove] = i3[k1]; + n_remove++; + result = TRUE; + } +#ifdef LOG_NAKED_TRIPLE + if (n_remove > 0 && !printed && !silent) { + printf("naked_triple_unit: (%d,%d), (%d,%d)," + " and (%d,%d)" + " in the same %s only contain %d, %d, and %d\n", + kRow[k1], kCol[k1], kRow[k2], kCol[k2], kRow[k3], + kCol[k3], what, i1[k1], i2[k1], i3[k1] + ); + printed = TRUE; + } +#endif + for (int ki = 0; ki < n_remove; ki++) { + remove_candidate("naked_triple_unit", i_remove[ki], + kR, kC + ); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } + } // if (k.. + } // for (int k.. + } // if (i1[k1].. + } // for (int k3.. + } // for (int k2.. + } // for (int k1.. + } // if (n_triple.. + +#ifdef LOG_IN_OUT + printf("<<< naked_triple_unit (%s) ---\n", what); +#endif + return result; + } diff --git a/sources/Solver/naked_triple_unit.h b/sources/Solver/naked_triple_unit.h new file mode 100644 index 0000000..d2c5c39 --- /dev/null +++ b/sources/Solver/naked_triple_unit.h @@ -0,0 +1,15 @@ +/* naked_triple_unit.h + * + * Applies the naked triple strategy to a unit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef NAKED_TRIPLE_UNIT +#define NAKED_TRIPLE_UNIT + +int naked_triple_unit(char*, char[9][2]); + +#endif diff --git a/sources/Solver/pairs_data.c b/sources/Solver/pairs_data.c new file mode 100644 index 0000000..abe9cd4 --- /dev/null +++ b/sources/Solver/pairs_data.c @@ -0,0 +1,9 @@ +/* pairs_data.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include "pairs_data.h" + +char n_x_pairs[10][10] = {{0}}; +char *x_pairs[10][10][3] = {{{0}}}; // 0= row, 1=column, 2=box diff --git a/sources/Solver/pairs_data.h b/sources/Solver/pairs_data.h new file mode 100644 index 0000000..39268a5 --- /dev/null +++ b/sources/Solver/pairs_data.h @@ -0,0 +1,14 @@ +/* pairs_data.h + * + * Data arrays to implement the Y-wing and XY-wing strategies. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef PAIRS_DATA +#define PAIRS_DATA + +extern char n_x_pairs[10][10]; +extern char *x_pairs[10][10][3]; // 0= row, 1=column, 2=box + +#endif diff --git a/sources/Solver/pairs_find.c b/sources/Solver/pairs_find.c new file mode 100644 index 0000000..3b66167 --- /dev/null +++ b/sources/Solver/pairs_find.c @@ -0,0 +1,142 @@ +/* pairs_find.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "pairs_data.h" +#include "pairs_find.h" +#include "xy_chain_digit.h" +#include "y_wing_digit.h" + +int pairs_find(int chain_type) { +#ifdef LOG_IN_OUT + printf("--- pairs_find (%d) >>>\n", chain_type); +#endif + + int result = FALSE; + + for (int k = 0; k <= 9; k++) { + for (int j = 0; j <= 9; j++) { + n_x_pairs[k][j] = 0; + } + } + + // Scan the grid to determine the distribution of pairs across candidates + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + char *elem = grid[k][j]; + if (elem[0] == 2) { + int nn[2] = {0, 0}; + int n = 0; + for (int i = 1; j <= 9 && n < 2; i++) { + if (elem[i] == TRUE) { + nn[n] = i; + n++; + } + } + n_x_pairs[nn[0]][nn[1]]++; + n_x_pairs[nn[0]][0]++; + n_x_pairs[nn[1]][nn[0]]++; // let's make the matrix symmetrical... + n_x_pairs[nn[1]][0]++; // ...not needed but makes life simpler + n_x_pairs[0][0]++; // total count of pairs + } // if (elem[0].. + } // for (int j.. + } // for (int k.. + + // Allocate space for the lists of row, column, and box IDs. + // Multiply it by three to save row, column, and box (which is redundant). + char *data_block = (char*)malloc(n_x_pairs[0][0] * sizeof(char*) * 3); + if (data_block != NULL) { + char *offset = data_block; + for (int i1 = 1; i1 <= 9; i1++) { + for (int i2 = 1; i2 <= 9; i2++) { + for (int kkk = 0; kkk < 3; kkk++) { + x_pairs[i1][i2][kkk] = offset; + offset += n_x_pairs[i1][i2]; + } + } // for (int i2.. + } // for (int i1.. + } // if (data_block.. + else { + printf("*** pairs_find: malloc failure\n"); + exit(EXIT_FAILURE); + } + + // Clear the pair counters and then populate the pair grid while recounting + // the pairs + for (int i1 = 1; i1 <= 9; i1++) { + for (int i2 = 1; i2 <= 9; i2++) { + n_x_pairs[i1][i2] = 0; + } + } + for (int k = 0; k < 9; k++) { + for (int j = 0; j < 9; j++) { + char *elem = grid[k][j]; + if (elem[0] == 2) { + int nn[2] = {0, 0}; + int n = 0; + for (int i = 1; j <= 9 && n < 2; i++) { + if (elem[i] == TRUE) { + nn[n] = i; + n++; + } + } // for (int i.. + int i1 = nn[0]; + int i2 = nn[1]; + int np = n_x_pairs[i1][i2]; + n_x_pairs[i1][i2]++; + n_x_pairs[i2][i1]++; + x_pairs[i1][i2][ROW][np] = (char)k; + x_pairs[i1][i2][COL][np] = (char)j; + x_pairs[i1][i2][BOX][np] = (char)(k/3*3+j/3); + x_pairs[i2][i1][ROW][np] = (char)k; + x_pairs[i2][i1][COL][np] = (char)j; + x_pairs[i2][i1][BOX][np] = (char)(k/3*3+j/3); + } // if (elem[0].. + } // for (int j.. + } // for (int k.. + + // Go through the digits, but only if they have at least two pairs. + typedef int (*chain_funct_ptr_t)(int); + chain_funct_ptr_t chain_functs[] = {y_wing_digit, xy_chain_digit}; + for (int i = 1; i <= 9 && !result; i++) { + if (n_x_pairs[i][0] >= 2) { + result |= chain_functs[chain_type](i); + } + } // for (int i.. + +/* Displaying the list of pairs */ /* + for (int i1 = 1; i1 <= 9; i1++) { + if (n_x_pairs[i1][0] > 0) { + printf("\ntotal #pairs for %d: %d\n", i1, + n_x_pairs[i1][0] + ); + } + for (int i2 = 1; i2 <= 9; i2++) { + if (n_x_pairs[i1][i2] > 0) { + printf("%d %d [%d]:", i1, i2, + n_x_pairs[i1][i2] + ); + for (int kkk = 0; + kkk < n_x_pairs[i1][i2]; + kkk++ + ) { + printf(" (%d,%d)", x_pairs[i1][i2][0][kkk], + x_pairs[i1][i2][1][kkk] + ); + } + printf("\n"); + } + } + } +*/ + free(data_block); + +#ifdef LOG_IN_OUT + printf("<<< pairs_find (%d) ---\n", chain_type); +#endif + return result; + } diff --git a/sources/Solver/pairs_find.h b/sources/Solver/pairs_find.h new file mode 100644 index 0000000..954d348 --- /dev/null +++ b/sources/Solver/pairs_find.h @@ -0,0 +1,15 @@ +/* pairs_find.h + * + * Executes either the Y-wing or the XY-wing strategy + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef PAIRS_FIND +#define PAIRS_FIND + +int pairs_find(int); + +#endif diff --git a/sources/Solver/pointing_line.c b/sources/Solver/pointing_line.c new file mode 100644 index 0000000..4c0231e --- /dev/null +++ b/sources/Solver/pointing_line.c @@ -0,0 +1,24 @@ +/* pointing_line.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "pointing_line.h" +#include "pointing_line_box.h" + +int pointing_line() { +#ifdef LOG_IN_OUT + printf("--- pointing_line >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9 && !result; k++) { + result = pointing_line_box(k, box[k]); + } +#ifdef LOG_IN_OUT + printf("<<< pointing_line ---\n"); +#endif + return result; + } diff --git a/sources/Solver/pointing_line.h b/sources/Solver/pointing_line.h new file mode 100644 index 0000000..ee8c6a3 --- /dev/null +++ b/sources/Solver/pointing_line.h @@ -0,0 +1,16 @@ +/* pointing_line.h + * + * Pointing-line strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * along with this program. If not, see . + * + */ +#ifndef POINTING_LINE +#define POINTING_LINE + +int pointing_line(void); + +#endif diff --git a/sources/Solver/pointing_line_box.c b/sources/Solver/pointing_line_box.c new file mode 100644 index 0000000..0125214 --- /dev/null +++ b/sources/Solver/pointing_line_box.c @@ -0,0 +1,87 @@ +/* pointing_line_box.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "pointing_line_box.h" +#include "remove_candidate.h" + +int pointing_line_box(int boxid, char box[9][2]) { +#ifdef LOG_IN_OUT + printf("--- pointing_line_box >>>\n"); +#endif + int result = FALSE; + int rc[10][2]; + for (int i = 0; i < 10; i++) { + for (int k = 0; k < 2; k++) { + rc[i][k] = -1; + } + } + for (int k = 0; k < 9; k++) { + int kR = box[k][ROW]; + int kC = box[k][COL]; + char *elem = grid[kR][kC]; + if (elem[0] > 1) { + for (int i = 1; i <= 9; i++) { + if (elem[i]) { + for (int krc = 0; krc < 2; krc++) { + if (rc[i][krc] == -1) { + rc[i][krc] = box[k][krc]; + } + else if (box[k][krc] != rc[i][krc]) { + rc[i][krc] = -2; + } + } // for (int krc.. + } // if (elem[i].. + } // for (int i.. + } // if (elem[0].. + } // for (int k.. + + int log_printed = FALSE; + int kRC[2] = {0}; + for (int i = 1; i <= 9; i++) { + for (int krc = 0; krc < 2; krc++) { + if (rc[i][krc] >= 0) { + kRC[krc] = rc[i][krc]; + for (int kk = 0; kk < 9; kk++) { + kRC[1-krc] = kk; + int kR = kRC[ROW]; + int kC = kRC[COL]; + if (boxid != kR/3*3+kC/3) { + char *elem = grid[kR][kC]; + if (elem[i]) { + result = TRUE; +#ifdef LOG_POINTING_LINE + if (!log_printed && !silent) { + printf("pointing_line_box: all candidates for %d" + " of box %d are in ", i, boxid + ); + if (krc == ROW) { + printf("row %d\n", kR); + } + else { + printf("column %d\n", kC); + } + log_printed = TRUE; + } +#endif + remove_candidate("pointing_line_box", i, kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } // if (elem[i].. + } // if (boxid.. + } // for (int kk.. + } // if (rc[i][k].. + } // for (int k.. + } // for (int i.. + +#ifdef LOG_IN_OUT + printf("<<< pointing_line_box ---\n"); +#endif + return result; + } diff --git a/sources/Solver/pointing_line_box.h b/sources/Solver/pointing_line_box.h new file mode 100644 index 0000000..fcc1815 --- /dev/null +++ b/sources/Solver/pointing_line_box.h @@ -0,0 +1,15 @@ +/* pointing_line_box.h + * + * Pointing-line strategy applied to a single box. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef POINTING_LINE_BOX +#define POINTING_LINE_BOX + +int pointing_line_box(int, char[9][2]); + +#endif diff --git a/sources/Solver/rectangle.c b/sources/Solver/rectangle.c new file mode 100644 index 0000000..7ec3875 --- /dev/null +++ b/sources/Solver/rectangle.c @@ -0,0 +1,30 @@ +/* rectangle.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "rectangle.h" +#include "rectangle_cell.h" +#include "rectangle_pattern.h" + +int rectangle() { +#ifdef LOG_IN_OUT + printf("--- rectangle >>>\n"); +#endif + int result = FALSE; + int pattern[9][4] = { + {0,1,4,3}, {0,2,5,3}, {0,1,7,6}, + {0,2,8,6}, {1,2,5,4}, {1,2,8,7}, + {3,4,7,6}, {3,5,8,6}, {4,5,8,7} + }; + for (int k = 0; k < 9 && !result; k++) { + result = rectangle_pattern(pattern[k]); + } +#ifdef LOG_IN_OUT + printf("<<< rectangle ---\n"); +#endif + return result; + } diff --git a/sources/Solver/rectangle.h b/sources/Solver/rectangle.h new file mode 100644 index 0000000..70c1aca --- /dev/null +++ b/sources/Solver/rectangle.h @@ -0,0 +1,15 @@ +/* rectangle.h + * + * Rectangle strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef RECTANGLE +#define RECTANGLE + +int rectangle(void); + +#endif diff --git a/sources/Solver/rectangle_cell.c b/sources/Solver/rectangle_cell.c new file mode 100644 index 0000000..d24f6d2 --- /dev/null +++ b/sources/Solver/rectangle_cell.c @@ -0,0 +1,59 @@ +/* rectangle_cell.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "rectangle_cell.h" +#include "rectangle_step.h" +#include "remove_candidate.h" + +int rectangle_cell(int seq[4], int kR, int kC) { +#ifdef LOG_IN_OUT + printf("--- rectangle_cell ["); + for (int k = 0; k < 4; k++) { + if (k > 0) printf(","); + printf("%d", seq[k]); + } + printf("] (%d,%d) >>>\n", kR, kC); +#endif + int result = FALSE; + + char *elem = grid[kR][kC]; + for (int i = 1; i <= 9 && !result; i++) { + if (elem[i]) { + int res = rectangle_step(seq, 0, kR, kC, i, kR, kC); + if (res == 0) { +#ifdef LOG_RECTANGLE + if (!silent) { + printf("rectangle_cell: %d in (%d,%d) leads to contradiction" + " when chained through the boxes [%d,%d,%d,%d]\n", + i, kR, kC, seq[0], seq[1], seq[2], seq[3] + ); + } +#endif + remove_candidate("rectangle_cell", i, kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + result = TRUE; + } // if (res.. + else if (res == -2) { + result = TRUE; + } + } // if (elem[i].. + } // for (int i.. + +#ifdef LOG_IN_OUT + printf("<<< rectangle_cell ["); + for (int k = 0; k < 4; k++) { + if (k > 0) printf(","); + printf("%d", seq[k]); + } + printf("] (%d,%d) ---\n", kR, kC); +#endif + return result; + } diff --git a/sources/Solver/rectangle_cell.h b/sources/Solver/rectangle_cell.h new file mode 100644 index 0000000..d7e1271 --- /dev/null +++ b/sources/Solver/rectangle_cell.h @@ -0,0 +1,15 @@ +/* rectangle_cell.h + * + * Rectangle strategy applied to all numbers in a cell within a pattern. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef RECTANGLE_CELL +#define RECTANGLE_CELL + +int rectangle_cell(int[4], int, int); + +#endif diff --git a/sources/Solver/rectangle_pattern.c b/sources/Solver/rectangle_pattern.c new file mode 100644 index 0000000..8ee2895 --- /dev/null +++ b/sources/Solver/rectangle_pattern.c @@ -0,0 +1,58 @@ +/* rectangle_pattern.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "rectangle_cell.h" +#include "rectangle_pattern.h" + +int rectangle_pattern(int pattern[4]) { +#ifdef LOG_IN_OUT + printf("--- rectangle_pattern ("); + for (int k = 0; k < 4; k++) { + if (k > 0) printf(","); + printf("%d", pattern[k]); + } + printf(") >>>\n"); +#endif + int result = FALSE; + int cells = TRUE; + for (int kB = 0; kB < 4 && cells; kB++) { + cells = FALSE; + int sID = pattern[kB]; + for (int kE = 0; kE < 9 && !cells; kE++) { + int kR = box[sID][kE][ROW]; + int kC = box[sID][kE][COL]; + if (grid[kR][kC][0] > 1) cells = TRUE; + } + } + + if (cells) { + for (int kB = 0; kB < 4 && !result; kB++) { + int seq[4]; + for (int k = 0; k < 4; k++) { + int kk = (kB + k) % 4; + seq[k] = pattern[kk]; + } + int sID = seq[0]; + for (int kE = 0; kE < 9 && !result; kE++) { + int kR = box[sID][kE][ROW]; + int kC = box[sID][kE][COL]; + if (grid[kR][kC][0] > 1) result = rectangle_cell(seq, kR, kC); + } // for (int kE.. + } // for (int kB.. + } // if (cells.. + +#ifdef LOG_IN_OUT + printf("<<< rectangle_pattern ("); + for (int k = 0; k < 4; k++) { + if (k > 0) printf(","); + printf("%d", pattern[k]); + } + printf(") ---\n"); +#endif + return result; + } diff --git a/sources/Solver/rectangle_pattern.h b/sources/Solver/rectangle_pattern.h new file mode 100644 index 0000000..f85c9b2 --- /dev/null +++ b/sources/Solver/rectangle_pattern.h @@ -0,0 +1,17 @@ +/* rectangle_pattern.h + * + * Rectangle strategy applied to everything inside four specified boxes. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef RECTANGLE_PATTERN +#define RECTANGLE_PATTERN + +#include "rectangle_cell.h" + +int rectangle_pattern(int[4]); + +#endif diff --git a/sources/Solver/rectangle_step.c b/sources/Solver/rectangle_step.c new file mode 100644 index 0000000..e51df5b --- /dev/null +++ b/sources/Solver/rectangle_step.c @@ -0,0 +1,92 @@ +/* rectangle_step.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "rectangle_cell.h" +#include "rectangle_step.h" +#include "remove_candidate.h" + +int rectangle_step(int seq[4], int kBeq, int kR, int kC, int kN, + int iR, int iC + ) { +#ifdef LOG_IN_OUT + printf("--- rectangle_step ["); + for (int k = kBeq; k < 4; k++) { + printf("%d", seq[k]); + if (k < 3) printf(","); + } + printf("] (%d,%d) %d >>>\n", kR, kC, kN); +#endif + int result = 0; + int kB = seq[kBeq+1]; + if (kBeq == 3) kB = seq[0]; + int n = 0; + int rows[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + int cols[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + for (int kE = 0; kE < 9 && result >= 0; kE++) { + int kkR = box[kB][kE][0]; + int kkC = box[kB][kE][1]; + char *elem = grid[kkR][kkC]; + if (elem[0] == 1) { + if (elem[kN] != FALSE) { + result = -1; + } + } // if (elem[0].. + else if (elem[kN] != FALSE && kkR != kR && kkC != kC) { + rows[n] = kkR; + cols[n] = kkC; + n++; + } // if (elem[0].. else.. + } // for (int kE.. + + if (n > 0 || kBeq == 3) { + if (kBeq < 3) { + for (int k = 0; k < n && result >= 0; k++) { + int res = rectangle_step(seq, kBeq+1, rows[k], cols[k], kN, iR, iC); + if (res < 0) { + result = res; + } + else { + result |= res; + } + } // for (int k.. + } // if (kBeq.. + else { + for (int k = 0; k < n && result == 0; k++) { + if (rows[k] == iR && cols[k] == iC) { + result = 1; + } + } // for (int k.. + } // if (kBeq.. else.. + } // if (n > 0.. + else if (n == 0 && result != -1) { // the pointing-line strategy applies + result = -2; +#ifdef LOG_POINTING_LINE + if (!silent) { + printf("rectangle_step: %d cannot solve (%d,%d) because all the %ds " + "in box %d are aligned with it (pointing-line strategy)\n", + kN, kR, kC, kN, kB + ); + } +#endif + remove_candidate("rectangle_step", kN, kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } // if (n > 0.. else if (n.. + +#ifdef LOG_IN_OUT + printf("<<< rectangle_step ["); + for (int k = kBeq; k < 4; k++) { + printf("%d", seq[k]); + if (k < 3) printf(","); + } + printf("] (%d,%d) %d return=%d ---\n", kR, kC, kN, result); +#endif + return result; + } diff --git a/sources/Solver/rectangle_step.h b/sources/Solver/rectangle_step.h new file mode 100644 index 0000000..fe04630 --- /dev/null +++ b/sources/Solver/rectangle_step.h @@ -0,0 +1,17 @@ +/* rectangle_step.h + * + * Rectangle strategy: moving to the next box. + * + * Returns 0 if no paths are possible, 1 or more if at least one path is + * possible, -1 if the search on this cell and candidate should be aborted, + * and -2 if rectangle should be aborted. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef RECTANGLE_STEP +#define RECTANGLE_STEP + +int rectangle_step(int[4], int, int, int, int, int, int); + +#endif diff --git a/sources/Solver/remove_candidate.c b/sources/Solver/remove_candidate.c new file mode 100644 index 0000000..8add8a9 --- /dev/null +++ b/sources/Solver/remove_candidate.c @@ -0,0 +1,25 @@ +/* remove_candidate.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "remove_candidate.h" + +void remove_candidate(char *caller, int i, int k, int j) { + grid[k][j][i] = FALSE; + grid[k][j][0]--; +#ifdef LOG_REMOVE_CANDIDATE + if (!silent) { + printf("%s: removed %d from (%d,%d)\n", caller, i, k, j); + } +#endif + if (grid[k][j][0] < 1) { + if (!silent) { + printf("*** No candidates left in (%d,%d)\n", k, j); + } + problem_found = TRUE; + } + } diff --git a/sources/Solver/remove_candidate.h b/sources/Solver/remove_candidate.h new file mode 100644 index 0000000..d1f4a1d --- /dev/null +++ b/sources/Solver/remove_candidate.h @@ -0,0 +1,15 @@ +/* remove_candidate.h + * + * Removes a single candidate. + * It is appropriate to have all the removals done through a single function, + * so that consistency checks can be performed. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef REMOVE_CANDIDATE +#define REMOVE_CANDIDATE + +void remove_candidate(char *, int, int, int); + +#endif diff --git a/sources/Solver/solve.c b/sources/Solver/solve.c new file mode 100644 index 0000000..d522e5d --- /dev/null +++ b/sources/Solver/solve.c @@ -0,0 +1,39 @@ +/* solve.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include +#include "count_candidates.h" +#include "def.h" +#include "display.h" +#include "display_string.h" +#include "execute_strategies.h" +#include "keep_going.h" +#include "solve.h" + +void solve() { +#ifdef LOG_IN_OUT + printf("--- solve >>>\n"); +#endif + if (!backtracking) n_strats_used = 0; + int n_candidates = count_candidates(); + int n_candidates_old = n_candidates + 1; + + while (keep_going() && n_candidates < n_candidates_old) { + n_candidates_old = n_candidates; + if (keep_going() && !execute_strategies(0)) { + if (keep_going() && !execute_strategies(1)) { + if (keep_going() && !execute_strategies(2)) { + execute_strategies(3); + } + } + } + n_candidates = count_candidates(); + } +#ifdef LOG_IN_OUT + printf("<<< solve ---\n"); +#endif + } diff --git a/sources/Solver/solve.h b/sources/Solver/solve.h new file mode 100644 index 0000000..d180cc1 --- /dev/null +++ b/sources/Solver/solve.h @@ -0,0 +1,13 @@ +/* solve.h + * + * Solves a Sudoku without backtracking. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef SOLVE +#define SOLVE + +void solve(); + +#endif diff --git a/sources/Solver/sudoku_solver.c b/sources/Solver/sudoku_solver.c new file mode 100644 index 0000000..7bdf3fb --- /dev/null +++ b/sources/Solver/sudoku_solver.c @@ -0,0 +1,238 @@ +/* sudoku_solver.c + * + * Main program. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include +#include "backtrack.h" +#include "box_line.h" +#include "cleanup.h" +#include "count_candidates.h" +#include "count_solved.h" +#include "def.h" +#include "display.h" +#include "display_strats_in_clear.h" +#include "display_string.h" +#include "hidden_pair.h" +#include "hidden_triple.h" +#include "inconsistent_grid.h" +#include "init.h" +#include "lines_2.h" +#include "lines_3.h" +#include "lines_4.h" +#include "naked_pair.h" +#include "naked_quad.h" +#include "naked_triple.h" +#include "pointing_line.h" +#include "rectangle.h" +#include "solve.h" +#include "unique_loop.h" +#include "xy_chain.h" +#include "y_wing.h" + +#define USE_FILES___NO +#define N_LEVELS 4 // levels of strategies + +// Sudoku grid and Sudoku indexing arrays +char grid[9][9][10]; +char row[9][9][2]; +char col[9][9][2]; +char box[9][9][2]; + +// unit names used for display in clear +char *unit_names[3] = {"row", "column", "box"}; + +// Arrays of strategies +// +// Trivial strategies (level 0) +f_ptr_t strat0[] = {unique_loop}; +char *strat0_names[] = { + "unique-loop" + }; +// +// Easy strategies (level 1) +f_ptr_t strat1[] = {naked_pair, hidden_pair, box_line, pointing_line}; +char *strat1_names[] = { + "naked-pair", "hidden-pair", "box-line", "pointing-line" + }; +// +// Intermediate strategies (level 2) +f_ptr_t strat2[] = {naked_triple, hidden_triple, lines_2, + naked_quad, y_wing + }; +char *strat2_names[] = { + "naked-triple", "hidden-triple", "lines-2", "naked-quad", "Y-wing" + }; +// +// Complex strategies (level 3) +f_ptr_t strat3[] = {rectangle, xy_chain, lines_3, lines_4}; +char *strat3_names[] = { + "rectangle", "XY-chain", "lines-3", "lines-4" + }; +// +// All strategies +f_ptr_t *strat_all[] = { + &strat0[0], &strat1[0], &strat2[0], &strat3[0] + }; +char **strat_all_names[] = { + &strat0_names[0], &strat1_names[0], &strat2_names[0], &strat3_names[0] + }; +int n_strat_all[] = { + sizeof(strat0)/sizeof(f_ptr_t), + sizeof(strat1)/sizeof(f_ptr_t), + sizeof(strat2)/sizeof(f_ptr_t), + sizeof(strat3)/sizeof(f_ptr_t) + }; +int n_levels = N_LEVELS; + +// List of used strategies (never seen more than 27) +int strats_used[50]; +int n_strats_used; + +// Global flags, to 'pop out' from nested loops and calls +int problem_found = FALSE; +int silent = FALSE; +int backtracking = FALSE; + +//==================================================================== main +int main(int argc, char *argv[]) { + printf("*** sudoku_solver ***\n"); + +#ifdef USE_FILES + + char *infile = "puzzles.txt"; + char *outfile = "solutions.txt"; + FILE *fpr = fopen(infile, "r"); + FILE *fpw = fopen(outfile, "a"); + if (fpr == NULL) { + printf("File \"%s\" failed to open\n", infile); + } + else if (fpw == NULL) { + printf("File \"%s\" failed to open\n", outfile); + } + else { + silent = TRUE; + + // Keep reading from file until you reach the EOF + int n_lines = 0; + int n_hints = 0; + while (!feof(fpr) && n_hints >= 0) { + char line[100]; // 90 would be enough, but... + line[0] = '\0'; + (void)fgets(line, 99, fpr); + if (line != NULL && strlen(line) > 80) { + char sudoku_s[82]; + int seed; + n_hints = -1; + sscanf(line, "%s\t%d\t%d", sudoku_s, &seed, &n_hints); + if (n_hints > 0) { + fprintf(fpw, "%s\t%d\t%d", sudoku_s, seed, n_hints); + init(sudoku_s); + cleanup(); + solve(); + if (count_solved() < 81) { + backtracking = TRUE; + backtrack(0); + backtracking = FALSE; + } + printf("%d\n", n_lines); + fprintf(fpw, "\t%s\t%d\t%d", + inconsistent_grid() ? "inconsistent" : "consistent", + count_solved(), + n_strats_used + ); + for (int k = 0; k < n_strats_used; k++) { + fprintf(fpw, "\t%d", strats_used[k]); + } + fprintf(fpw, "\n"); + n_lines++; + } // if (n_hints.. + } // if (line.. + } // while (TRUE.. + } // if (fpr .. else .. + if (fpr != NULL) fclose(fpr); + if (fpw != NULL) fclose(fpw); + +#else + + // Check for the presence of an input Sudoku string + if (argc < 2) { + puts("*** You need to provide a sudoku string"); + return EXIT_FAILURE; + } + + // Check that the Sudoku string is 81-characters long + if (strlen(argv[1]) != 81) { + puts("*** The sudoku string must be 81 characters long"); + return EXIT_FAILURE; + } + + // Check that the Sudoku string consists of digits between 0 and 9 + for (int k = 0; k < 81; k++) { + if (argv[1][k] < '0' || argv[1][k] > '9') { + puts("*** The sudoku string must only contain 0 to 9 digits"); + return EXIT_FAILURE; + } + } + + // Print the Sudoku string + if (argc > 2) { + printf("--- \"%s\"\n", argv[2]); + } + printf("--- \"%s\"\n", argv[1]); + + // Initialize the Sudoku arrays + init(argv[1]); + display(); + + // Remove the impossible numbers with an initial cleanup without + // displaying any logging messages + printf("sudoku: the initial grid contains %d solved cells\n", + count_solved() + ); + silent = TRUE; + cleanup(); + silent = FALSE; + printf("sudoku: after the initial cleanup, the grid" + " contains %d solved cells\n", count_solved() + ); + display(); + + // Execute the strategies + solve(); + + // Backtrack if necessary + if (count_solved() < 81) { + backtracking = TRUE; + backtrack(0); + backtracking = FALSE; + } + + + // Check that everything is OK + if (inconsistent_grid()) { + printf("*** The grid is inconsistent\n"); + } + + printf("sudoku: the final grid contains %d solved cells\n", + count_solved() + ); + display(); + display_string(); + printf("Strategies used %d:", n_strats_used); +/* + for (int k = 0; k < n_strats_used; k++) { + printf(" %d", strats_used[k]); + } + printf("\n"); +*/ + display_strats_in_clear(); + +#endif + + return EXIT_SUCCESS; + } diff --git a/sources/Solver/unique.c b/sources/Solver/unique.c new file mode 100644 index 0000000..8e9d5d4 --- /dev/null +++ b/sources/Solver/unique.c @@ -0,0 +1,26 @@ +/* unique.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "unique.h" +#include "unique_unit.h" + +int unique() { +#ifdef LOG_IN_OUT + printf("--- unique >>>\n"); +#endif + int result = FALSE; + for (int k = 0; k < 9; k++) { + result |= unique_unit("row", row[k]); + result |= unique_unit("column", col[k]); + result |= unique_unit("box", box[k]); + } +#ifdef LOG_IN_OUT + printf("<<< unique ---\n"); +#endif + return result; + } diff --git a/sources/Solver/unique.h b/sources/Solver/unique.h new file mode 100644 index 0000000..d953afa --- /dev/null +++ b/sources/Solver/unique.h @@ -0,0 +1,15 @@ +/* unique.h + * + * Looks for unique candidates. + * + * Returns TRUE if it finds at least one unique number. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef UNIQUE +#define UNIQUE + +int unique(void); + +#endif diff --git a/sources/Solver/unique_loop.c b/sources/Solver/unique_loop.c new file mode 100644 index 0000000..3d2ec1c --- /dev/null +++ b/sources/Solver/unique_loop.c @@ -0,0 +1,26 @@ +/* unique_loop.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "unique.h" +#include "unique_loop.h" + +int unique_loop() { +#ifdef LOG_IN_OUT + printf("--- unique_loop >>>\n"); +#endif + int found; + int something = FALSE; + do { + found = unique(); + something |= found; + } while (found && !problem_found); +#ifdef LOG_IN_OUT + printf("<<< unique_loop ---\n"); +#endif + return something; + } diff --git a/sources/Solver/unique_loop.h b/sources/Solver/unique_loop.h new file mode 100644 index 0000000..4d2c323 --- /dev/null +++ b/sources/Solver/unique_loop.h @@ -0,0 +1,15 @@ +/* unique_loop.h + * + * Keeps looking for unique candidates until it doesn't find any. + * + * Returns TRUE if something is found. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef UNIQUE_LOOP +#define UNIQUE_LOOP + +int unique_loop(void); + +#endif diff --git a/sources/Solver/unique_unit.c b/sources/Solver/unique_unit.c new file mode 100644 index 0000000..ca6ade4 --- /dev/null +++ b/sources/Solver/unique_unit.c @@ -0,0 +1,65 @@ +/* unique_unit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "remove_candidate.h" +#include "unique_unit.h" + +int unique_unit(char *what, char unit[9][2]) { +#ifdef LOG_IN_OUT + printf("--- unique_unit (%s) >>>\n", what); +#endif + int result = FALSE; + int r[10]; for (int i = 0; i < 10; i++) { r[i] = -1; } + int c[10]; + for (int j1 = 0; j1 < 9; j1++) { + int kR = unit[j1][0]; + int kC = unit[j1][1]; + char *elem = grid[kR][kC]; + if (elem[0] > 1) { + for (int i = 1; i <= 9; i++) { + if (elem[i] != FALSE) { + if (r[i] == -1) { + r[i] = kR; + c[i] = kC; + } + else { + r[i] = -2; + } + } // if (elem[i].. + } // for (int i.. + } // if (elem[0].. + } // for (int j1.. + + for (int i = 1; i <= 9 && !problem_found; i++) { + if (r[i] >= 0) { + result = TRUE; + int kR = r[i]; + int kC = c[i]; +#ifdef LOG_UNIQUE + if (!silent) { + printf("unique_unit: %d in (%d,%d) is unique within the %s\n", + i, kR, kC, what + ); + } +#endif + char *elem = grid[kR][kC]; + for (int ii = 1; ii <= 9 && !problem_found; ii++) { + if (elem[ii] != FALSE && i != ii) { + remove_candidate("unique_unit", ii, kR, kC); + } + } + if (!problem_found) cleanup_around(kR, kC); + } // if (r[i].. + } // for int i.. + +#ifdef LOG_IN_OUT + printf("<<< unique_unit (%s) ---\n", what); +#endif + return result; + } diff --git a/sources/Solver/unique_unit.h b/sources/Solver/unique_unit.h new file mode 100644 index 0000000..6fb6ac6 --- /dev/null +++ b/sources/Solver/unique_unit.h @@ -0,0 +1,15 @@ +/* unique_unit.h + * + * Looks for unique candidates in a unit. + * + * Returns TRUE if it finds at least one unique number within the unit. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef UNIQUE_UNIT +#define UNIQUE_UNIT + +int unique_unit(char*, char[9][2]); + +#endif diff --git a/sources/Solver/xy_chain.c b/sources/Solver/xy_chain.c new file mode 100644 index 0000000..ed4f3ba --- /dev/null +++ b/sources/Solver/xy_chain.c @@ -0,0 +1,21 @@ +/* xy_chain.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "pairs_find.h" +#include "xy_chain.h" + +int xy_chain() { +#ifdef LOG_IN_OUT + printf("--- xy_chain >>>\n"); +#endif + int result = pairs_find(DEF_XY_CHAIN); +#ifdef LOG_IN_OUT + printf("<<< xy_chain ---\n"); +#endif + return result; + } diff --git a/sources/Solver/xy_chain.h b/sources/Solver/xy_chain.h new file mode 100644 index 0000000..07339fd --- /dev/null +++ b/sources/Solver/xy_chain.h @@ -0,0 +1,15 @@ +/* xy_chain.h + * + * XY-chain strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef XY_CHAIN +#define XY_CHAIN + +int xy_chain(void); + +#endif diff --git a/sources/Solver/xy_chain_digit.c b/sources/Solver/xy_chain_digit.c new file mode 100644 index 0000000..aabce53 --- /dev/null +++ b/sources/Solver/xy_chain_digit.c @@ -0,0 +1,54 @@ +/* xy_chain_digit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "pairs_data.h" +#include "xy_chain_digit.h" +#include "xy_chain_step.h" + +int xy_chain_digit(int i0) { +#ifdef LOG_IN_OUT + printf("--- xy_chain_digit (%d) >>>\n", i0); +#endif + + int n_found = 0; + for (int i1 = 1; i1 <= 9 && n_found == 0; i1++) { + for (int i01 = 0; i01 < n_x_pairs[i0][i1] && n_found == 0; i01++) { + + // Flag x_pairs[i0][i1][ROW][i01] and x_pairs[i0][i1][COL][i01] + // to avoid using the same cell more than once within the chain + int kR01 = x_pairs[i0][i1][ROW][i01]; + int kC01 = x_pairs[i0][i1][COL][i01]; + x_pairs[i0][i1][ROW][i01] += 10; + x_pairs[i1][i0][ROW][i01] += 10; + + // Start the chain. + { + int kB01 = x_pairs[i0][i1][BOX][i01]; + chain_info_struct_t i0_info; + chain_info_struct_t i1_info; + i0_info.digit = i0; + i1_info.digit = i1; + i1_info.coords[ROW] = i0_info.coords[ROW] = kR01; + i1_info.coords[COL] = i0_info.coords[COL] = kC01; + i1_info.coords[BOX] = i0_info.coords[BOX] = kB01; + i0_info.next = &i1_info; + i1_info.next = NULL; + n_found += xy_chain_step(&i0_info, 1); + } + + // Restore the grid. + x_pairs[i0][i1][ROW][i01] -= 10; + x_pairs[i1][i0][ROW][i01] -= 10; + } // for (int i01.. + } // for (int i1 = 1.. + +#ifdef LOG_IN_OUT + printf("<<< xy_chain_digit (%d) ---\n", i0); +#endif + return n_found > 0; + } diff --git a/sources/Solver/xy_chain_digit.h b/sources/Solver/xy_chain_digit.h new file mode 100644 index 0000000..65e1b56 --- /dev/null +++ b/sources/Solver/xy_chain_digit.h @@ -0,0 +1,15 @@ +/* xy_chain_digit.h + * + * XY-chain strategy applied to a single digit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef XY_CHAIN_DIGIT +#define XY_CHAIN_DIGIT + +int xy_chain_digit(int); + +#endif diff --git a/sources/Solver/xy_chain_step.c b/sources/Solver/xy_chain_step.c new file mode 100644 index 0000000..3324518 --- /dev/null +++ b/sources/Solver/xy_chain_step.c @@ -0,0 +1,153 @@ +/* xy_chain_step.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "intersection.h" +#include "pairs_data.h" +#include "remove_candidate.h" +#include "xy_chain_step.h" + +#define MAX_DEPTH 8 + +int chain_length; + +int xy_chain_step(chain_info_struct_p info, int depth) { +#ifdef LOG_IN_OUT + printf("--- xy_chain_step (%d) >>>\n", depth); +#endif + + int n_found = 0; + chain_info_struct_p next = info->next; + chain_info_struct_p ix_info_p; + do { + ix_info_p = next; + next = ix_info_p->next; + } while (next != NULL); + int i0 = info->digit; + int ix = ix_info_p->digit; + + for (int iy = 1; iy <= 9 && n_found == 0; iy++) { + for (int ixy = 0; ixy < n_x_pairs[ix][iy] && n_found == 0; ixy++) { + + int kRxy = x_pairs[ix][iy][ROW][ixy]; + if (kRxy < 9) { + int kCxy = x_pairs[ix][iy][COL][ixy]; + int kBxy = x_pairs[ix][iy][BOX][ixy]; + if ( kRxy == ix_info_p->coords[ROW] + || kCxy == ix_info_p->coords[COL] + || kBxy == ix_info_p->coords[BOX] + ) { + int found_something_this_time = FALSE; + if (iy == i0 && depth > 2) { + int printed = FALSE; + void *mem_block = malloc(MAX_INTER_N * sizeof(struct rc_struct)); + if (mem_block == NULL) { + printf("*** xy_chain_step: malloc failure\n"); + exit(EXIT_FAILURE); + } + int kR0 = info->coords[ROW]; + int kC0 = info->coords[COL]; + rc_p_t inter = intersection(kR0, kC0, kRxy, kCxy, mem_block); + + // Check whether intersecting cells contain i0 as candidates. + rc_p_t p = inter; + rc_p_t pp = p; + while (p != NULL) { + int kR = pp->row; + int kC = pp->col; + if (kR < 9 && grid[kR][kC][i0]) { + found_something_this_time = TRUE; + n_found++; +#ifdef LOG_XY_CHAIN + if (!printed && !silent) { + printf("xy_chain_step: (%d,%d):%d", info->coords[ROW], + info->coords[COL], info->digit + ); + next = info->next; + printf("%d", next->digit); + do { + chain_info_struct_p next1 = next->next; + if (next1 != NULL) { + printf(" (%d,%d):%d%d", next1->coords[ROW], + next1->coords[COL], next->digit, next1->digit + ); + } + next = next1; + } while (next != NULL); + printf(" (%d,%d):%d%d\n", kRxy, kCxy, ix, iy); + printf("xy_chain_step: intersection of (%d,%d) and (%d,%d):", + kR0, kC0, kRxy, kCxy + ); + rc_p_t p = inter; + rc_p_t pp = p; + do { + printf(" (%d,%d)", pp->row, pp->col); + p = pp->next; + pp = p; + } while (p != NULL); + printf("\n"); + printed = TRUE; + } // if (!printed.. +#endif + + { // Scan the whole chain to determine its length + // and update chain_length + chain_info_struct_p info1 = info->next; + chain_length = 1; + do { + chain_length++; + chain_info_struct_p info2 = info1->next; + info1 = info2; + } while (info1 != NULL); + } + + remove_candidate("xy_chain_step", i0, kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } // if (elem[i0].. + p = pp->next; + pp = p; + } + + free(mem_block); + } // if (iy.. + + if (!found_something_this_time) { + + // The chain is to be extended + x_pairs[ix][iy][ROW][ixy] += 10; + x_pairs[iy][ix][ROW][ixy] += 10; + chain_info_struct_t iy_info; + iy_info.digit = iy; + iy_info.coords[ROW] = kRxy; + iy_info.coords[COL] = kCxy; + iy_info.coords[BOX] = kBxy; + iy_info.next = NULL; + ix_info_p->next = &iy_info; + + // Keep following the chain + if (depth < MAX_DEPTH) { + n_found += xy_chain_step(info, depth + 1); + } + + // Clean up behind you + ix_info_p->next = NULL; + x_pairs[ix][iy][ROW][ixy] -= 10; + x_pairs[iy][ix][ROW][ixy] -= 10; + } // if (!found_something_this_time.. + } // if (kRxy ==.. + } // if (kRxy <.. + } // for (int ixy.. + } // for (int iy.. + +#ifdef LOG_IN_OUT + printf("<<< xy_chain_step (%d) ---\n", depth); +#endif + return n_found; + } diff --git a/sources/Solver/xy_chain_step.h b/sources/Solver/xy_chain_step.h new file mode 100644 index 0000000..7ec39c6 --- /dev/null +++ b/sources/Solver/xy_chain_step.h @@ -0,0 +1,24 @@ +/* xy_chain_step.h + * + * XY-chain strategy: moving along the chain till the end. + * + * Returns the number of candidates removed. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef XY_CHAIN_STEP +#define XY_CHAIN_STEP + +typedef struct chain_info_struct *chain_info_struct_p; +typedef struct chain_info_struct { + int digit; + int coords[3]; + chain_info_struct_p next; + } chain_info_struct_t; + +extern int chain_length; + +int xy_chain_step(chain_info_struct_p, int); + +#endif diff --git a/sources/Solver/y_wing.c b/sources/Solver/y_wing.c new file mode 100644 index 0000000..72d3028 --- /dev/null +++ b/sources/Solver/y_wing.c @@ -0,0 +1,21 @@ +/* y_wing.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "def.h" +#include "pairs_find.h" +#include "y_wing.h" + +int y_wing() { +#ifdef LOG_IN_OUT + printf("--- y_wing >>>\n"); +#endif + int result = pairs_find(DEF_Y_WING); +#ifdef LOG_IN_OUT + printf("<<< y_wing ---\n"); +#endif + return result; + } diff --git a/sources/Solver/y_wing.h b/sources/Solver/y_wing.h new file mode 100644 index 0000000..799d235 --- /dev/null +++ b/sources/Solver/y_wing.h @@ -0,0 +1,15 @@ +/* y_wing.h + * + * Y-wing strategy. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef Y_WING +#define Y_WING + +int y_wing(void); + +#endif diff --git a/sources/Solver/y_wing_digit.c b/sources/Solver/y_wing_digit.c new file mode 100644 index 0000000..406f00d --- /dev/null +++ b/sources/Solver/y_wing_digit.c @@ -0,0 +1,139 @@ +/* y_wing_digit.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#include +#include "cleanup_around.h" +#include "def.h" +#include "intersection.h" +#include "pairs_data.h" +#include "remove_candidate.h" +#include "y_wing_digit.h" + +int y_wing_digit(int i0) { +#ifdef LOG_IN_OUT + printf("--- y_wing_digit (%d) >>>\n", i0); +#endif + +#define A 0 +#define B 1 +#define C 2 + + int success = FALSE; + + // +-------+ + // | i0+i1 | + // +-------+-------+ + // | i1+i2 | i2+i0 | + // +-------+-------+ + int coords[3][3]; // [cell][unit] + for (int i1 = 1; i1 <= 9; i1++) { + for (int i1k = 0; i1k < n_x_pairs[i0][i1]; i1k++) { + + // If we arrive here, i0 is paired with i1, and + // i1k goes through all the pairs of i0 with i1 + coords[A][ROW] = x_pairs[i0][i1][ROW][i1k]; + coords[A][COL] = x_pairs[i0][i1][COL][i1k]; + coords[A][BOX] = x_pairs[i0][i1][BOX][i1k]; + for (int i2 = 1; i2 <= 9; i2++) { + if (i2 != i0) { + for (int i2k = 0; i2k < n_x_pairs[i1][i2]; i2k++) { + + // If we arrive here, i1 is paired with i2, and + // i2k goes through all the pairs of i1 with i2 + coords[B][ROW] = x_pairs[i1][i2][ROW][i2k]; + coords[B][COL] = x_pairs[i1][i2][COL][i2k]; + coords[B][BOX] = x_pairs[i1][i2][BOX][i2k]; + + if (i2 > i1 && + ( coords[A][ROW] == coords[B][ROW] + || coords[A][COL] == coords[B][COL] + || coords[A][BOX] == coords[B][BOX] + ) + ) { + for (int i3k = 0; i3k < n_x_pairs[i2][i0]; i3k++) { + + // If we arrive here, i2 is paired with i0, and + // i3k goes through all the pairs of i2 made with i0 + coords[C][ROW] = x_pairs[i2][i0][ROW][i3k]; + coords[C][COL] = x_pairs[i2][i0][COL][i3k]; + coords[C][BOX] = x_pairs[i2][i0][BOX][i3k]; + + if ( coords[C][ROW] != coords[A][ROW] + && coords[C][COL] != coords[A][COL] + && coords[C][BOX] != coords[A][BOX] + && ( coords[C][ROW] == coords[B][ROW] + || coords[C][COL] == coords[B][COL] + || coords[C][BOX] == coords[B][BOX] + ) + ) { + int printed = FALSE; + void *mem_block = malloc(MAX_INTER_N*sizeof(struct rc_struct)); + if (mem_block == NULL) { + printf("*** y_wing_digit (%d): malloc failure\n", i0); + exit(EXIT_FAILURE); + } + rc_p_t inter = intersection(coords[A][ROW], coords[A][COL], + coords[C][ROW], coords[C][COL], mem_block + ); + rc_p_t p = inter; + rc_p_t pp = p; + while (p != NULL) { + int kR = pp->row; + int kC = pp->col; + char *elem = grid[kR][kC]; + if (elem[i0]) { + success = TRUE; +#ifdef LOG_Y_WING + if (!printed && !silent) { + printf("y_wing_digit: (%d,%d):%d%d (%d,%d):%d%d" + " (%d,%d):%d%d\n", + coords[A][ROW], coords[A][COL], i0, i1, + coords[B][ROW], coords[B][COL], i1, i2, + coords[C][ROW], coords[C][COL], i2, i0 + ); + printf("y_wing_digit: intersection of (%d,%d) and" + " (%d,%d):", coords[A][ROW], coords[A][COL], + coords[C][ROW], coords[C][COL] + ); + rc_p_t p1 = inter; + rc_p_t pp1 = p1; + do { + if ( pp1->row != coords[B][ROW] + || pp1->col != coords[B][COL] + ) { + printf(" (%d,%d)", pp1->row, pp1->col); + } + p1 = pp1->next; + pp1 = p1; + } while (p1 != NULL); + printf("\n"); + printed = TRUE; + } // if (!printed.. +#endif + remove_candidate("y_wing_digit", i0, kR, kC); + if (grid[kR][kC][0] == 1) { + cleanup_around(kR, kC); + } + } // if (elem[i0].. + p = pp->next; + pp = p; + } + + free(mem_block); + } // if (coords[C][ROW].. + } // for (int i3k.. + } // if (i2 > i1.. + } // for (int i2k.. + } // if (i2 != i0.. + } // for (int i2.. + } // for (int i1k.. + } // for (int i1.. + +#ifdef LOG_IN_OUT + printf("<<< y_wing_digit (%d) ---\n", i0); +#endif + return success; + } diff --git a/sources/Solver/y_wing_digit.h b/sources/Solver/y_wing_digit.h new file mode 100644 index 0000000..fd32711 --- /dev/null +++ b/sources/Solver/y_wing_digit.h @@ -0,0 +1,15 @@ +/* y_wing_digit.h + * + * Y-wing strategy applied to a single digit. + * + * Returns TRUE if it removes at least one candidate. + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#ifndef Y_WING_DIGIT +#define Y_WING_DIGIT + +int y_wing_digit(int); + +#endif