diff --git a/9781484209967.jpg b/9781484209967.jpg new file mode 100644 index 0000000..8ae4366 Binary files /dev/null and b/9781484209967.jpg differ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..6a679b2 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Freeware License, some rights reserved + +Copyright (c) 2015 Giulio Zambon + +Permission is hereby granted, free of charge, to anyone obtaining a copy +of this software and associated documentation files (the "Software"), +to work with the Software within the limits of freeware distribution and fair use. +This includes the rights to use, copy, and modify the Software for personal use. +Users are also allowed and encouraged to submit corrections and modifications +to the Software for the benefit of other users. + +It is not allowed to reuse, modify, or redistribute the Software for +commercial use in any way, or for a user’s educational materials such as books +or blog articles without prior permission from the copyright holder. + +The above copyright notice and this permission notice need to be included +in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ce5504 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +#Apress Source Code + +This repository accompanies [*Sudoku Programming with C*](http://www.apress.com/9781484209967) by Giulio Zambon (Apress, 2015). + +![Cover image](9781484209967.jpg) + +Download the files as a zip using the green button, or clone the repository to your machine using Git. + +##Releases + +Release v1.0 corresponds to the code in the published book, without corrections or updates. + +##Contributions + +See the file Contributing.md for more information on how you can contribute to this repository. diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..f6005ad --- /dev/null +++ b/contributing.md @@ -0,0 +1,14 @@ +# Contributing to Apress Source Code + +Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. + +## How to Contribute + +1. Make sure you have a GitHub account. +2. Fork the repository for the relevant book. +3. Create a new branch on which to make your change, e.g. +`git checkout -b my_code_contribution` +4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. +5. Submit a pull request. + +Thank you for your contribution! \ No newline at end of file diff --git a/sources/Generator/brute.c b/sources/Generator/brute.c new file mode 100644 index 0000000..c70fe39 --- /dev/null +++ b/sources/Generator/brute.c @@ -0,0 +1,69 @@ +/* brute.c + * + * Copyright (C) 2015 Giulio Zambon - http://zambon.com.au/ + * + */ +#include +#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