// Copyright 2005 Thomas R. Davis // // This file is part of Sudoku. // // Sudoku is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Sudoku is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Sudoku; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // This code solves sudoku puzzles. // // Usage: sudoku [-shj] [-f filter] [-J seed] // // where contains the puzzle encoded as follows: // *** See below: the format is now much more flexible. /* ..48..... .9.46..7. .5....614 21.6..5.. 58.7.9.41 ..7..8.69 345....9. .6..37.2. .....41.. */ // If the -s option is used, the program will print the board // after each number or test number is filled in and you // need to type to go to the next step; otherwise // it just prints the answer (or indicates failure). If -s // is in effect, after every step (and at the beginning) // the program dumps an Encapsulated PostScript file showing // the current board position, but only if SIZE == 3. // // The -j option causes the board to be // totally shuffled before displaying it: there are random // transpositions, swaps within and among rows and columns, // as well as a random permutation of the entry values. The // random number seed is generated from the time of day so this // will yield a different jumbling with each use. // // The -J option is similar, but it jumbles based on the seed // given by the parameter for -J // // The -h option prints the help message // // The -f option filters the "possible" candidates shown in // each square that is not yet assigned. If this option is // not included, all candidates are listed. If the filter // is the single digit '0', none are printed. If the filter // is, say, '157' only those values are printed. This does // not work when compiled for SIZE == 4. // // SIZE is 3 or 4, depending on whether the puzzle is // 9x9 (with numbers 1 through 9) or 16x16 // (with numbers 0, 1, 2, ..., 9, a, b, c, d, e, f // (or A, B, C, D, E, F)). // // The "no entry" character is '.', 'x', or 'X'. // // Here's a 16 x 16 puzzle you can try if you compile sudoku with // SIZE = 4: /* 8xxxxxxxxebxxxxx xex4xxx8af1xxxx6 xx3xx729xd6xxe1x xxb6xefd8xxx2xxx x6xdxfxxb8xx1x4x 3xxxeb5xxxfxa8xd x8fbxxx4xx7xcxxx 1xa2xx3xxxc4xxxx xxxx41xxxaxxbdxc xxx1xdxx3xxx790x 2xe9x8xxxc0bxxxf xbxfxxa0xxex5x2x xxx0xxxb965xe7xx xf6xx39x07dxx2xx bxxxx48afxxxdxcx xxxxx57xxxxxxxx0 */ // The code is now improved to take almost anything, as long as // there are 81 values for a 9x9 or 256 values for a 16x16, // but now the "no entry" character must be a '.', an 'X', an // 'x' or a space (' '), so files like this or similar are valid: /* ..7|...|.9. .6.|.8.|3.. 39.|2.7|..5 ----------- ...|4..|..2 .1.|.9.|.8. 5..|..3|... ----------- 6..|9.2|.71 ..3|.4.|.5. .2.|...|6.. */ // You can obtain as many test puzzles as you want from: // // http://www.websudoku.com/ // If you run the program with the -s (slow) option, after each // time you hit the key, an EPS (Encapsulated PostScript) // file called "puz.eps" is written in the current directory that // displays the current state of the puzzle. If you were, for // example, documenting a solution, you could rename the file after // each press of the key. #include #include #include // only define one of the following: #define SIZE 3 //#define SIZE 4 #define LEN SIZE*SIZE #define UNKNOWN 1000 #define absval(x) ((x) > 0 ? (x) : (-(x))) #define NOSHARE 0 #define ROW 1 #define COLUMN 2 #define BLOCK 4 struct square { int value; int possible[LEN]; }; int find1, find2, find3[LEN], find4, find5, find6, find7, find8, find9, push[100]; int find10, find11, find12, find13, find14, find15; int slow; int jum, randseed = -12345; char *sudokufile; char digitfilter[LEN+1]; int scount; // solution count void printstats() { int i, j; if (find2) printf("single possibility: %d\n", find2); if (find1) printf("one in row/column/block: %d\n", find1); if (find3[2]) printf("naked pairs: %d\n", find3[2]); if (find3[3]) printf("naked triplets: %d\n", find3[3]); if (find3[4]) printf("naked quads: %d\n", find3[4]); if (find3[5]) printf("naked quintuplets: %d\n", find3[5]); if (find4) printf("locked candidates: %d\n", find4); if (find5) printf("hidden pair: %d\n", find5); if (find6) printf("hidden triple: %d\n", find6); if (find7) printf("hidden quad: %d\n", find7); if (find8) printf("x-wing: %d\n", find8); if (find9) printf("xy-wing: %d\n", find9); if (find10) printf("swordfish: %d\n", find10); if (find13) printf("jellyfish: %d\n", find13); if (find11) printf("coloring: %d\n", find11); if (find12) printf("multicoloring: %d\n", find12); if (find14) printf("unique constraint: %d\n", find14); if (find15) printf("forcing chain: %d\n", find15); for (j = 99; j >=0; j--) if (push[j] != 0) break; if (j == 0 && push[0]) printf("push[0] = %d\n", push[0]); else for (i = 0; i <= j; i++) printf("push[%d] = %d\n", i, push[i]); } struct square puzzle[LEN][LEN]; void initsearchvalues() { int i; // initialiaze difficulty counters find1 = find2 = find4 = find5 = find6 = find7 = find8 = 0; find9 = find10 = find11 = find12 = find13 = find14 = find15 = 0; scount = 0; for (i = 0; i < 100; i++) push[i] = 0; for (i = 0; i < LEN; i++) find3[i] = 0; } void initpuzzle() { int i, j, k; initsearchvalues(); // initialize puzzle for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) puzzle[i][j].value = UNKNOWN; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) for (k = 0; k < LEN; k++) puzzle[i][j].possible[k] = 1; } void resetpossible() { int i, j, k, l, v; int iblock, jblock; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { if ((v=puzzle[i][j].value) != UNKNOWN) { for (k = 0; k < LEN; k++) { puzzle[i][k].possible[v]=0; puzzle[k][j].possible[v]=0; } iblock = (i / SIZE) * SIZE; jblock = (j / SIZE) * SIZE; for (k = 0; k < SIZE; k++) for (l = 0; l < SIZE; l++) puzzle[iblock+k][jblock+l].possible[v] = 0; } } } #include void rotatepuz() // rotate 90 degrees { struct square tmp[LEN][LEN]; int i, j; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) tmp[i][j] = puzzle[8-j][i]; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) puzzle[i][j] = tmp[i][j]; } void transposepuz() { int i, j; struct square tmp; for (i = 0; i < LEN-1; i++) for (j = i+1; j < LEN; j++) { tmp = puzzle[i][j]; puzzle[i][j] = puzzle[j][i]; puzzle[j][i] = tmp; } } void permvalues(int perm[LEN]) { int i, j, v; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) if ((v = puzzle[i][j].value) != UNKNOWN) puzzle[i][j].value = perm[v]; } void permrows(int block, int perm[SIZE]) { struct square tmp[SIZE]; int i, j, k; for (i = 0; i < LEN; i++) { for (j = 0; j < SIZE; j++) tmp[j] = puzzle[SIZE*block + perm[j]][i]; for (j = 0; j < SIZE; j++) puzzle[SIZE*block+j][i] = tmp[j]; } } void permcols(int block, int perm[SIZE]) { struct square tmp[SIZE]; int i, j, k; for (i = 0; i < LEN; i++) { for (j = 0; j < SIZE; j++) tmp[j] = puzzle[i][SIZE*block + perm[j]]; for (j = 0; j < SIZE; j++) puzzle[i][SIZE*block+j] = tmp[j]; } } void permrowblocks(int perm[SIZE]) { struct square tmp[SIZE][SIZE]; int i, j, k; for (j = 0; j < LEN; j++) { for (i = 0; i < SIZE; i++) for (k = 0; k < SIZE; k++) tmp[i][k] = puzzle[3*perm[i]+k][j]; for (i = 0; i < SIZE; i++) for (k = 0; k < SIZE; k++) puzzle[3*i+k][j] = tmp[i][k]; } } void permcolblocks(int perm[SIZE]) { struct square tmp[SIZE][SIZE]; int i, j, k; for (j = 0; j < LEN; j++) { for (i = 0; i < SIZE; i++) for (k = 0; k < SIZE; k++) tmp[i][k] = puzzle[j][3*perm[i]+k]; for (i = 0; i < SIZE; i++) for (k = 0; k < SIZE; k++) puzzle[j][3*i+k] = tmp[i][k]; } } void shuffle(int count, int array[]) { int i, k, tmp; for (i = 0; i < count-1; i++) { k = rand() % (count-i); tmp = array[k]; array[k] = array[i]; array[i] = tmp; } } void symjumble() // symmetric jumble { int i, j, perm[LEN]; j = (rand()%4); for (i = 0; i < j; i++) rotatepuz(); if (rand()%2) transposepuz(); for (i = 0; i < LEN; i++) perm[i] = i; for (i = 0; i < SIZE; i++) perm[i] = SIZE - i -1; i = rand()%2; if (i) { // works, but not quite right for SIZE = 4; permrowblocks(perm); permrows(1, perm); } i = rand()%2; if (i) { // works, but not quite right for SIZE = 4; permcolblocks(perm); permcols(1, perm); } // other shuffling here ... XXX shuffle(LEN, perm); permvalues(perm); } void jumble(int symmetric) { int i, j, perm[LEN]; struct tm *newtime; time_t ltime; if (randseed == -12345) { time(<ime); newtime = localtime(<ime); srand(newtime->tm_sec); } else srand(randseed); if (symmetric) { symjumble(); return; } for (i = 0; i < LEN; i++) perm[i] = i; j = (rand()%4); for (i = 0; i < j; i++) rotatepuz(); if (rand()%2) transposepuz(); shuffle(SIZE, perm); permrowblocks(perm); shuffle(SIZE, perm); permcolblocks(perm); for (i = 0; i < SIZE; i++) { shuffle(SIZE, perm); permrows(i, perm); shuffle(SIZE, perm); permcols(i, perm); } shuffle(LEN, perm); permvalues(perm); } int solvable() { int i, j, k, count; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { count = 0; if (puzzle[i][j].value == UNKNOWN) { for (k = 0; k < LEN; k++) if (puzzle[i][j].possible[k]) { count++; break; } if (count == 0) return 0; } } return 1; } int solved() { int i, j; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN) return 0; return 1; } int readpuzzle(FILE *f) { int i, j; int ch, gotentry; initpuzzle(); for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { gotentry = 0; do { ch = fgetc(f); if (ch == EOF) return 0; if (SIZE==3 && isdigit(ch)) { if (ch == '9') ch = '0'; puzzle[i][j].value = ch - '0'; gotentry = 1; } else if (SIZE == 4 && isxdigit(ch)) { if ('0' <= ch && ch <= '9') puzzle[i][j].value = ch - '0'; if ('a' <= ch && ch <= 'f') puzzle[i][j].value = ch - 'a' + 10; if ('A' <= ch && ch <= 'F') puzzle[i][j].value = ch - 'A' + 10; gotentry = 1; } else if (ch == '.' || ch == 'x' || ch == 'X') { puzzle[i][j].value = UNKNOWN; gotentry = 1; } } while (gotentry == 0); } return 1; } // The following is because the 9x9 sudoku uses 1-9 and internally // the program uses 0-8 or 0-15, depending on the puzzle size. int convert(int i) { if (i == 0 && SIZE == 3) return 9; return i; } int findobvious() { int i, j, k, count, last, num; int iblock, jblock, lasti, lastj; last = lastj = lasti = 0; // see if a square can only be filled in one way: for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN) { count = 0; for (k = 0; k < LEN; k++) if (puzzle[i][j].possible[k]) {last = k; count++;} if (count == 1) { puzzle[i][j].value = last; find2++; if (slow == 1 && scount == 0) printf("no other possibility: row: %d column: %d value: %d\n", i+1, j+1, convert(last)); return 1; } } // now go through each row, column, and block to see if // only one of the entries can contain a missing number: for (num = 0; num < LEN; num++) { // check rows: for (i = 0; i < LEN; i++) { count = 0; for (j = 0; j < LEN; j++) { if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[num]) { count++; last = j; } } if (count == 1) { puzzle[i][last].value = num; find1++; if (slow == 1 && scount == 0) printf("row unique missing: row: %d column: %d value: %d\n", i+1, last+1, convert(num)); return 1; } } // check columns: for (i = 0; i < LEN; i++) { count = 0; for (j = 0; j < LEN; j++) { if (puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[num]) { count++; last = j; } } if (count == 1) { puzzle[last][i].value = num; find1++; if (slow == 1 && scount == 0) printf("column unique missing: row: %d column: %d value: %d\n", last+1, i+1, convert(num)); return 1; } } // check blocks: for (iblock = 0; iblock < LEN; iblock += SIZE) for (jblock = 0; jblock < LEN; jblock += SIZE) { count = 0; for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) { if (puzzle[iblock+i][jblock+j].value == UNKNOWN && puzzle[iblock+i][jblock+j].possible[num]) { count++; lasti = iblock+i; lastj = jblock+j; } } if (count == 1) { puzzle[lasti][lastj].value = num; find1++; if (slow == 1 && scount == 0) printf("block unique missing: row: %d column: %d value: %d\n", lasti+1, lastj+1, convert(num)); return 1; } } } return 0; } // findnakedhelp looks on a virtual "line" of LEN squares that need one // of each number in each slot. If there are, for example, exactly // two slots that have to contain exactly the same two numbers, those // numbers can't go in other slots. Same with exactly the same three // numbers in three slots, et cetera. This routine will return 1 if // it finds such a situation and is able to eliminate a possibility from // an additional slot. It returns zero on failure. This routine does // not fill in any slots as solved: it just cuts down the number of // possibilities that can go in other slots. // // The "size" parameter tells how big a set to look for (2 of 2, 3 of // 3, et cetera). int findnakedhelp(struct square line[LEN], int size) { int count; int i, j, k, l, fail, match; int vals[100], vals1[100], squares[100]; int i1, i2, i3, i4, i5; int values[LEN][LEN], counts[LEN]; int naked; count = naked = 0; for (i = 0; i < LEN; i++) if (line[i].value == UNKNOWN) count++; if (count <= size) return 0; for (i = 0; i < LEN; i++) { counts[i] = 0; for (j = 0; j < LEN; j++) if (line[i].value == UNKNOWN && line[i].possible[j]) { values[i][j] = 1; counts[i]++; } else values[i][j] = 0; } if (size == 2) { for (i1 = 0; i1 < LEN-1; i1++) for (i2 = i1+1; i2 < LEN; i2++) { count = 0; for (k = 0; k < LEN; k++) { if (counts[k] == 0 || counts[k] > size) continue; fail = 0; for (i = 0; i < LEN; i++) if (i != i1 && i != i2 && values[k][i]) fail = 1; if (fail) continue; squares[count++] = k; // one of the possible set } if (count != size) continue; for (i = 0; i < LEN; i++) { fail = 0; for (k = 0; k < count; k++) if (i == squares[k]) fail = 1; if (fail) continue; for (k = 0; k < LEN; k++) if ((k == i1 || k == i2) && line[i].value == UNKNOWN && line[i].possible[k]) { line[i].possible[k] = 0; if (slow) printf("naked pair: square %d value %d\n", i+1, convert(k)); naked++; find3[2]++; } } } } if (size == 3) { for (i1 = 0; i1 < LEN-2; i1++) for (i2 = i1+1; i2 < LEN-1; i2++) for (i3 = i2+1; i3 < LEN; i3++) { count = 0; for (k = 0; k < LEN; k++) { if (counts[k] == 0 || counts[k] > size) continue; fail = 0; for (i = 0; i < LEN; i++) if (i != i1 && i != i2 && i != i3 && values[k][i]) fail = 1; if (fail) continue; squares[count++] = k; // one of the possible set } if (count != size) continue; for (i = 0; i < LEN; i++) { fail = 0; for (k = 0; k < count; k++) if (i == squares[k]) fail = 1; if (fail) continue; for (k = 0; k < LEN; k++) if ((k == i1 || k == i2 || k == i3) && line[i].value == UNKNOWN && line[i].possible[k]) { line[i].possible[k] = 0; if (slow) printf("naked triple: square %d value %d\n", i+1, convert(k)); naked++; find3[3]++; } } } } if (size == 4) { for (i1 = 0; i1 < LEN-3; i1++) for (i2 = i1+1; i2 < LEN-2; i2++) for (i3 = i2+1; i3 < LEN-1; i3++) for (i4 = i3+1; i4 < LEN; i4++) { count = 0; for (k = 0; k < LEN; k++) { if (counts[k] == 0 || counts[k] > size) continue; fail = 0; for (i = 0; i < LEN; i++) if (i != i1 && i != i2 && i != i3 && i != i4 && values[k][i]) fail = 1; if (fail) continue; squares[count++] = k; // one of the possible set } if (count != size) continue; for (i = 0; i < LEN; i++) { fail = 0; for (k = 0; k < count; k++) if (i == squares[k]) fail = 1; if (fail) continue; for (k = 0; k < LEN; k++) if ((k == i1 || k == i2 || k == i3 || k == i4) && line[i].value == UNKNOWN && line[i].possible[k]) { line[i].possible[k] = 0; if (slow) printf("naked quad: square %d value %d\n", i+1, convert(k)); naked++; find3[4]++; } } } } if (size == 5) { for (i1 = 0; i1 < LEN-4; i1++) for (i2 = i1+1; i2 < LEN-3; i2++) for (i3 = i2+1; i3 < LEN-2; i3++) for (i4 = i3+1; i4 < LEN-1; i4++) for (i5 = i4+1; i5 < LEN; i5++) { count = 0; for (k = 0; k < LEN; k++) { if (counts[k] == 0 || counts[k] > size) continue; fail = 0; for (i = 0; i < LEN; i++) if (i != i1 && i != i2 && i != i3 && i != i4 && i != i5 && values[k][i]) fail = 1; if (fail) continue; squares[count++] = k; // one of the possible set } if (count != size) continue; for (i = 0; i < LEN; i++) { fail = 0; for (k = 0; k < count; k++) if (i == squares[k]) fail = 1; if (fail) continue; for (k = 0; k < LEN; k++) if ((k == i1 || k == i2 || k == i3 || k == i4 || k == i5) && line[i].value == UNKNOWN && line[i].possible[k]) { line[i].possible[k] = 0; if (slow) printf("naked quintuplet: square %d value %d\n", i+1, convert(k)); naked++; find3[5]++; } } } } if (naked) return 1; return 0; } int findnaked() { int i, j, k; int i1, j1; struct square line[LEN]; for (k = 2; k < 5; k++) { for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) line[j] = puzzle[i][j]; if (findnakedhelp(line, k)) { for (j = 0; j < LEN; j++) puzzle[i][j] = line[j]; if (slow) printf("from row: %d\n", i+1); return 1; } } for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) line[j] = puzzle[j][i]; if (findnakedhelp(line, k)) { for (j = 0; j < LEN; j++) puzzle[j][i] = line[j]; if (slow) printf("from column: %d\n", i+1); return 1; } } for (i1 = 0; i1 < LEN; i1 += SIZE) for (j1 = 0; j1 < LEN; j1 += SIZE) { for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) line[i*SIZE + j] = puzzle[i1+i][j1+j]; if (findnakedhelp(line, k)) { for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) puzzle[i1+i][j1+j] = line[i*SIZE + j]; if (slow) printf("from block: %d %d\n", i1+1, j1+1); return 1; } } } return 0; } int lockedcandidates() { int i1, j1, i, j, k, rows; int found = 0; // Check for type 1: within a box, all possibilities in a row // if so, nuke the rest in the row outside the box... for (i1 = 0; i1 < SIZE; i1++) for (j1 = 0; j1 < SIZE; j1++) { for (k = 0; k < LEN; k++) { // try each value rows = 0; for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) if (puzzle[i1*SIZE+i][j1*SIZE+j].value == UNKNOWN && puzzle[i1*SIZE+i][j1*SIZE+j].possible[k]) rows |= (1 << i); if (rows == 1 || rows == 2 || rows == 4 || rows == 8){ for (i = 0; i < SIZE; i++) if (rows == (1<= (j1+1)*SIZE) && puzzle[i1*SIZE+i][j].possible[k]) { puzzle[i1*SIZE+i][j].possible[k] = 0; if (slow) printf("row-locked candidate1: " "%d invalid in row: %d, column %d\n", convert(k), i1*SIZE+i+1, j+1); found++; } } } rows = 0; for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) if (puzzle[j1*SIZE+j][i1*SIZE+i].value == UNKNOWN && puzzle[j1*SIZE+j][i1*SIZE+i].possible[k]) rows |= (1 << i); if (rows == 1 || rows == 2 || rows == 4 || rows == 8){ for (j = 0; j < SIZE; j++) if (rows == (1<= (j1+1)*SIZE) && puzzle[i][i1*SIZE+j].possible[k]) { puzzle[i][i1*SIZE+j].possible[k] = 0; if (slow) printf("column-locked candidate1: " "%d invalid in row: %d, column %d\n", convert(k), i+1, i1*SIZE+j+1); found++; } } } } } // Now check for type 2: a candidate within a row or column // is restricted to be within a box. Nuke others in the box. int boxval = 0, ibox; for (k = 0; k < LEN; k++) { // check each candidate for (i = 0; i < LEN; i++) { // check each row rows = 0; ibox = i/SIZE; for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[k]) { boxval = j/SIZE; rows |= (1<<(j/SIZE)); } if (rows == 1 || rows == 2 || rows == 4 || rows == 8) { for (i1 = 0; i1 < SIZE; i1++) if (SIZE*ibox + i1 != i) for (j1 = 0; j1 < SIZE; j1++) { if (puzzle[SIZE*ibox+i1][SIZE*boxval+j1].value == UNKNOWN && puzzle[SIZE*ibox+i1][SIZE*boxval+j1].possible[k]) { puzzle[SIZE*ibox+i1][SIZE*boxval+j1].possible[k] = 0; if (slow) printf("row-locked candidate2: " "%d invalid in row %d, column %d\n", convert(k), SIZE*ibox+i1+1, SIZE*boxval+j1); found++; } } } } for (i = 0; i < LEN; i++) { // check each column rows = 0; ibox = i/SIZE; for (j = 0; j < LEN; j++) if (puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[k]) { boxval = j/SIZE; rows |= (1<<(j/SIZE)); } if (rows == 1 || rows == 2 || rows == 4 || rows == 8) { for (i1 = 0; i1 < SIZE; i1++) if (SIZE*ibox + i1 != i) for (j1 = 0; j1 < SIZE; j1++) { if (puzzle[SIZE*boxval+j1][SIZE*ibox+i1].value == UNKNOWN && puzzle[SIZE*boxval+j1][SIZE*ibox+i1].possible[k]) { puzzle[SIZE*boxval+j1][SIZE*ibox+i1].possible[k] = 0; if (slow) printf("column-locked candidate2: " "%d invalid in row %d, column %d\n", convert(k), SIZE*boxval+j1+1, SIZE*ibox+i1+1); found++; } } } } } find4 += found; if (found) return 1; return 0; } dumpline(struct square line[LEN]) // for debugging { int i, j; printf("dumpline:\n"); for (i = 0; i < LEN; i++) { printf("val: %d\n", line[i].value); for (j = 0; j < LEN; j++) printf("%d ", line[i].possible[j]); printf("\n"); } } int hiddenpairhelp(struct square line[LEN], int type, int val) { int i, j, k, l; int count, fail; int klist[LEN]; for (i = 0; i < LEN-1; i++) for (j = i+1; j < LEN; j++) { // count squares that contain i or j fail = 0; for (k = 0; k < LEN; k++) if (line[k].value == i || line[k].value == j) fail = 1; if (fail) continue; count = 0; for (k = 0; k < LEN; k++) if (line[k].value == UNKNOWN && (line[k].possible[i] || line[k].possible[j])) klist[count++] = k; if (count == 2) { // found a pair! //printf("%d %d %d %d\n", i, j, klist[0], klist[1]); //dumpline(line); for (k = 0; k < 2; k++) for (l = 0; l < LEN; l++) { if ((l != i) && (l != j) && line[klist[k]].possible[l]) { line[klist[k]].possible[l] = 0; if (slow) { switch(type) { case ROW: printf("row "); break; case COLUMN: printf("column "); break; case BLOCK: printf("block "); break; } printf("hiddenpair: (%d %d); %d impossible in ", convert(i), convert(j), convert(l), klist[k]+1); switch(type) { case ROW: printf("row %d, column %d\n", val+1, klist[k]+1); break; case COLUMN: printf("row %d, column %d\n", klist[k]+1, val+1); break; case BLOCK: printf("row %d, column %d\n", SIZE*(val/SIZE) + (klist[k]/SIZE)+1, SIZE*(val%SIZE) + (klist[k]%SIZE) +1); break; } } find5++; return 1; } } } } return 0; } int hiddentriplehelp(struct square line[LEN], int type, int val) { int i, j, j1, k, l; int count, fail; int klist[LEN]; for (i = 0; i < LEN-2; i++) for (j = i+1; j < LEN-1; j++) for (j1 = j+1; j1 < LEN; j1++) { // count squares with i,j,j1 fail = 0; for (k = 0; k < LEN; k++) if (line[k].value == i || line[k].value == j || line[k].value == j1) fail = 1; if (fail) continue; count = 0; for (k = 0; k < LEN; k++) if (line[k].value == UNKNOWN && (line[k].possible[i] || line[k].possible[j] || line[k].possible[j1])) klist[count++] = k; if (count == 3) { // found a triplet! //printf("%d %d %d %d\n", i, j, klist[0], klist[1]); //dumpline(line); for (k = 0; k < count; k++) for (l = 0; l < LEN; l++) { if ((l != i) && (l != j) && (l != j1) && line[klist[k]].possible[l]) { line[klist[k]].possible[l] = 0; if (slow) { switch(type) { case ROW: printf("row "); break; case COLUMN: printf("column "); break; case BLOCK: printf("block "); break; } printf("hiddentriple: (%d %d %d); %d impossible in ", convert(i), convert(j), convert(j1), convert(l)); switch(type) { case ROW: printf("row %d, column %d\n", val+1, klist[k]+1); break; case COLUMN: printf("row %d, column %d\n", klist[k]+1, val+1); break; case BLOCK: printf("row %d, column %d\n", SIZE*(val/SIZE) + (klist[k]/SIZE)+1, SIZE*(val%SIZE) + (klist[k]%SIZE) +1); break; } } find6++; return 1; } } } } return 0; } int hiddenquadhelp(struct square line[LEN], int type, int val) { int i, j, j1, j2, k, l; int count, fail; int klist[LEN]; for (i = 0; i < LEN-3; i++) for (j = i+1; j < LEN-2; j++) for (j1 = j+1; j1 < LEN-1; j1++) for (j2 = j1+1; j2 < LEN; j2++) { // count squares with i,j,j1,j2 fail = 0; for (k = 0; k < LEN; k++) if (line[k].value == i || line[k].value == j || line[k].value == j1 || line[k].value == j2) fail = 1; if (fail) continue; count = 0; for (k = 0; k < LEN; k++) if (line[k].value == UNKNOWN && (line[k].possible[i] || line[k].possible[j] || line[k].possible[j1] || line[k].possible[j2])) klist[count++] = k; if (count == 4) { // found a quad! //printf("%d %d %d %d\n", i, j, klist[0], klist[1]); //dumpline(line); for (k = 0; k < count; k++) for (l = 0; l < LEN; l++) { if ((l != i) && (l != j) && (l != j1) && (l != j2) && line[klist[k]].possible[l]) { line[klist[k]].possible[l] = 0; if (slow) { switch(type) { case ROW: printf("row "); break; case COLUMN: printf("column "); break; case BLOCK: printf("block "); break; } printf("hiddenquad: (%d %d %d %d); %d impossible in ", convert(i), convert(j), convert(j1), convert(j2), convert(l)); switch(type) { case ROW: printf("row %d, column %d\n", val+1, klist[k]+1); break; case COLUMN: printf("row %d, column %d\n", klist[k]+1, val+1); break; case BLOCK: printf("row %d, column %d\n", SIZE*(val/SIZE) + (klist[k]/SIZE)+1, SIZE*(val%SIZE) + (klist[k]%SIZE) +1); break; } } find7++; return 1; } } } } return 0; } findhiddenpair() { int i, j, i1, j1; struct square line[LEN]; for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) line[j] = puzzle[i][j]; if (hiddenpairhelp(line, ROW, i)) { for (j = 0; j < LEN; j++) puzzle[i][j] = line[j]; //if (slow) printf("from row: %d\n", i+1); return 1; } if (hiddentriplehelp(line, ROW, i)) { for (j = 0; j < LEN; j++) puzzle[i][j] = line[j]; if (slow) printf("from row: %d\n", i+1); return 1; } if (hiddenquadhelp(line, ROW, i)) { for (j = 0; j < LEN; j++) puzzle[i][j] = line[j]; if (slow) printf("from row: %d\n", i+1); return 1; } } for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) line[j] = puzzle[j][i]; if (hiddenpairhelp(line, COLUMN, i)) { for (j = 0; j < LEN; j++) puzzle[j][i] = line[j]; //if (slow) printf("from column: %d\n", i+1); return 1; } if (hiddentriplehelp(line, COLUMN, i)) { for (j = 0; j < LEN; j++) puzzle[j][i] = line[j]; if (slow) printf("from column: %d\n", i+1); return 1; } if (hiddenquadhelp(line, COLUMN, i)) { for (j = 0; j < LEN; j++) puzzle[j][i] = line[j]; if (slow) printf("from column: %d\n", i+1); return 1; } } for (i1 = 0; i1 < LEN; i1 += SIZE) for (j1 = 0; j1 < LEN; j1 += SIZE) { for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) line[i*SIZE + j] = puzzle[i1+i][j1+j]; if (hiddenpairhelp(line, BLOCK, SIZE*(i1/SIZE)+j1/SIZE)) { for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) puzzle[i1+i][j1+j] = line[i*SIZE + j]; //if (slow) printf("from block: %d %d\n", i1+1, j1+1); return 1; } if (hiddentriplehelp(line, BLOCK, SIZE*(i1/SIZE)+j1/SIZE)) { for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) puzzle[i1+i][j1+j] = line[i*SIZE + j]; //if (slow) printf("from block: %d %d\n", i1+1, j1+1); return 1; } if (hiddenquadhelp(line, BLOCK, SIZE*(i1/SIZE)+j1/SIZE)) { for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) puzzle[i1+i][j1+j] = line[i*SIZE + j]; //if (slow) printf("from block: %d %d\n", i1+1, j1+1); return 1; } } return 0; } int xwing() { int i, j, j1, k, j1save = 0, rcount, i2, j2, printed, count; int val[LEN], val1[LEN]; // first check rows for (k = 0; k < LEN; k++) // k = candidate value for (i = 0; i < LEN; i++) { count = 0; for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[k]) val[count++] = j; if (count == 2) { rcount = 0; for (j1 = 0; j1 < LEN; j1++) { count = 0; for (j = 0; j < LEN; j++) if (puzzle[j1][j].value == UNKNOWN && puzzle[j1][j].possible[k]) val1[count++] = j; if (count == 2 && val[0] == val1[0] && val[1] == val1[1]) { if (j1 != i) j1save = j1; rcount++; } } printed = 0; if (rcount == 2) { // got a possible x-wing! for (i2 = 0; i2 < LEN; i2++) for (j2 = 0; j2 < LEN; j2++) { if (i2 != i && i2 != j1save && (j2 == val[0] || j2 == val[1]) && puzzle[i2][j2].value == UNKNOWN && puzzle[i2][j2].possible[k]) { puzzle[i2][j2].possible[k] = 0; if (slow) printf("x-wing candidate %d in rows: %d %d: " "value %d impossible at (%d %d)\n", convert(k), i+1, j1save+1, convert(k), i2+1, j2+1); find8++; printed = 1; } } } if (printed == 1) return 1; } } // next check columns for (k = 0; k < LEN; k++) // k = candidate value for (i = 0; i < LEN; i++) { count = 0; for (j = 0; j < LEN; j++) if (puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[k]) val[count++] = j; if (count == 2) { rcount = 0; for (j1 = 0; j1 < LEN; j1++) { count = 0; for (j = 0; j < LEN; j++) if (puzzle[j][j1].value == UNKNOWN && puzzle[j][j1].possible[k]) val1[count++] = j; if (count == 2 && val[0] == val1[0] && val[1] == val1[1]) { if (j1 != i) j1save = j1; rcount++; } } printed = 0; if (rcount == 2) { // got a possible x-wing! for (i2 = 0; i2 < LEN; i2++) for (j2 = 0; j2 < LEN; j2++) { if (i2 != i && i2 != j1save && (j2 == val[0] || j2 == val[1]) && puzzle[j2][i2].value == UNKNOWN && puzzle[j2][i2].possible[k]) { puzzle[j2][i2].possible[k] = 0; if (slow) printf("x-wing columns: %d %d: " "value %d impossible at (%d %d)\n", i+1, j1save+1, convert(k), j2+1, i2+1); find8++; printed = 1; } } } if (printed == 1) return 1; } } return 0; } // share compares two pairs of coordinates. It returns: // 1 if in the same row // 2 if in the same column // 4 if in the same block // 5: row+block // 6: column+block // 0 otherwise int share(int x1, int y1, int x2, int y2) { int retval = 0; if (x1 == x2) retval = ROW; if (y1 == y2) retval = COLUMN; if ((x1 / SIZE == x2 / SIZE) && (y1 / SIZE == y2 / SIZE)) retval |= BLOCK; return retval; } debugxywing(int i, int j, int k, int pairlist[LEN*LEN][4]) { printf("xywing:\n"); printf("(%d %d): (%d < %d)\n", pairlist[i][0]+1, pairlist[i][1]+1, convert(pairlist[i][2]), convert(pairlist[i][3])); printf("(%d %d): (%d < %d)\n", pairlist[j][0]+1, pairlist[j][1]+1, convert(pairlist[j][2]), convert(pairlist[j][3])); printf("(%d %d): (%d < %d)\n", pairlist[k][0]+1, pairlist[k][1]+1, convert(pairlist[k][2]), convert(pairlist[k][3])); } int xywing() { int i, j, k, l; int p1, q1, p2, q2, p3, q3; int x1, y1, x2, y2, x3, y3; int s12, s23, s13; int paircount, count; int pairlist[LEN*LEN][4]; // totally paranoid size int save[LEN]; // only need first 2, but can easily overflow int test[LEN]; int fail; // the four entries are: (x, y), (p < q), where (x, y) // are the coordinates of the square and p and q are the values // first, search for squares that have only two candidates: paircount = 0; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { count = 0; for (k = 0; k < LEN; k++) if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[k]) save[count++] = k; if (count == 2) { pairlist[paircount][0] = i; pairlist[paircount][1] = j; pairlist[paircount][2] = save[0]; pairlist[paircount++][3] = save[1]; } } // OK, the pairs are all found if (paircount < 3) return 0; // now look at all sets of three squares: for (i = 0; i < paircount-2; i++) for (j = i+1; j < paircount-1; j++) for (k = j+1; k < paircount; k++) { p1 = pairlist[i][2]; q1 = pairlist[i][3]; p2 = pairlist[j][2]; q2 = pairlist[j][3]; p3 = pairlist[k][2]; q3 = pairlist[k][3]; for (l = 0; l < LEN; l++) test[l] = 0; test[p1]++; test[p2]++; test[p3]++; test[q1]++; test[q2]++; test[q3]++; fail = 0; for (l = 0; l < LEN; l++) if (test[l] != 0 && test[l] != 2) fail=1; if (fail) continue; // possible set! x1 = pairlist[i][0]; y1 = pairlist[i][1]; x2 = pairlist[j][0]; y2 = pairlist[j][1]; x3 = pairlist[k][0]; y3 = pairlist[k][1]; s12 = share(x1, y1, x2, y2); s23 = share(x2, y2, x3, y3); s13 = share(x1, y1, x3, y3); if (s12*s13 + s12*s23 + s13*s23 == 0) continue; // at this point, assume one of s12, s23, s23 is // zero, or all are in the same row, column, or // block and this case is handled elsewhere fail = 1; if (s13 == 0) { int tmp; tmp = x1; x1 = x2; x2 = tmp; tmp = y1; y1 = y2; y2 = tmp; tmp = p1; p1 = p2; p2 = tmp; tmp = q1; q1 = q2; q2 = tmp; tmp = s23; s23 = s13; s13 = tmp; } if (s12 == 0) { int tmp; tmp = x1; x1 = x3; x3 = tmp; tmp = y1; y1 = y3; y3 = tmp; tmp = p1; p1 = p3; p3 = tmp; tmp = q1; q1 = q3; q3 = tmp; tmp = s23; s23 = s12; s12 = tmp; } if (s13 & BLOCK) { int tmp; tmp = x2; x2 = x3; x3 = tmp; tmp = y2; y2 = y3; y3 = tmp; tmp = p2; p2 = p3; p3 = tmp; tmp = q2; q2 = q3; q3 = tmp; tmp = s12; s12 = s13; s13 = tmp; } if (s23 == 0) { int val = 0; // find excluded value: for (l = 0; l < LEN; l++) if (test[l] && l != p1 && l != q1) val = l; if ((s12 & ROW) && (s13 & COLUMN)) { if (puzzle[x3][y2].value == UNKNOWN && puzzle[x3][y2].possible[val]) { puzzle[x3][y2].possible[val] = 0; fail = 0; find9++; if (slow) printf("xy-wing: %d impossible in row: %d col %d\n", convert(val), x3+1, y2+1); } } if ((s12 & COLUMN) && (s13 & ROW)) { if (puzzle[x2][y3].value == UNKNOWN && puzzle[x2][y3].possible[val]) { puzzle[x2][y3].possible[val] = 0; fail = 0; find9++; if (slow) printf("xy-wing: %d impossible in row: %d col %d\n", convert(val), x2+1, y3+1); } } if ((s12 & BLOCK) && (s13 & ROW)) { int xb; xb = (y1/SIZE)*SIZE; for (l = xb; l < xb+SIZE; l++) if (l != y1 && puzzle[x1][l].value == UNKNOWN && puzzle[x1][l].possible[val]) { puzzle[x1][l].possible[val] = 0; fail = 0; find9++; if (slow) printf("xy-wing1: %d impossible in row: %d col %d\n", convert(val), x1+1, l+1); } xb = (y3/SIZE)*SIZE; for (l = xb; l < xb+SIZE; l++) if (puzzle[x2][l].value == UNKNOWN && puzzle[x2][l].possible[val]) { puzzle[x2][l].possible[val] = 0; fail = 0; find9++; if (slow) printf("xy-wing2: %d impossible in row: %d col %d\n", convert(val), x2+1, l+1); } } if ((s12 & BLOCK) && (s13 & COLUMN)) { int xb; xb = (x1/SIZE)*SIZE; for (l = xb; l < xb+SIZE; l++) if (l != x1 && puzzle[l][y1].value == UNKNOWN && puzzle[l][y1].possible[val]) { puzzle[l][y1].possible[val] = 0; fail = 0; find9++; if (slow) printf("xy-wing3: %d impossible in row: %d col %d\n", convert(val), l+1, y1+1); } xb = (x3/SIZE)*SIZE; for (l = xb; l < xb+SIZE; l++) if (puzzle[l][y2].value == UNKNOWN && puzzle[l][y2].possible[val]) { puzzle[l][y2].possible[val] = 0; fail = 0; find9++; if (slow) printf("xy-wing4: %d impossible in row: %d col %d\n", convert(val), l+1, y2+1); } } } if (fail == 0) return 1; } return 0; } int bitcount[65536]; static int bitinited = 0; int sw4() // jellyfish { int i, j, count; int candidate, value; int bitmap[LEN]; int j1, j2, j3, j4; int found = 0; if (bitinited == 0) { bitinited = 1; for (i = 0; i < 65536; i++) { j = i; count = 0; while (j) { if (j&1) count++; j>>= 1; } bitcount[i] = count; } } for (candidate = 0; candidate < LEN; candidate++) { // fill in bitmap array for rows: for (i = 0; i < LEN; i++) { value = 0; for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[candidate]) value |= (1 << j); bitmap[i] = value; } for (j1 = 0; j1 < LEN - 3; j1++) { if (bitcount[bitmap[j1]] < 2 || bitcount[bitmap[j1]] > 4) continue; for (j2 = j1+1; j2 < LEN - 2; j2++) { if (bitcount[bitmap[j2]] < 2 || bitcount[bitmap[j2]] > 4) continue; for (j3 = j2+1; j3 < LEN - 1; j3++) { if (bitcount[bitmap[j3]] < 2 || bitcount[bitmap[j3]] > 4) continue; for (j4 = j3+1; j4 < LEN; j4++) { if (bitcount[bitmap[j4]] < 2 || bitcount[bitmap[j4]] > 4) continue; value = bitmap[j1] | bitmap[j2] | bitmap[j3] | bitmap[j4]; if (bitcount[value] > 4) continue; // possible hit for (i = 0; i < LEN; i++) { if (i == j1 || i == j2 || i == j3 || i == j4) continue; for (j = 0; j < LEN; j++) { if (((1 << j) & value) && puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[candidate]) { if (slow) printf("jellyfish rows: %d %d %d %d\n" "eliminate: candidate: %d from row: %d col: %d\n", j1+1, j2+1, j3+1, j4+1, convert(candidate), i+1, j+1); puzzle[i][j].possible[candidate] = 0; find13++; found = 1; } } } } } } } // now check columns: for (i = 0; i < LEN; i++) { value = 0; for (j = 0; j < LEN; j++) if (puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[candidate]) value |= (1 << j); bitmap[i] = value; } for (j1 = 0; j1 < LEN - 3; j1++) { if (bitcount[bitmap[j1]] < 2 || bitcount[bitmap[j1]] > 4) continue; for (j2 = j1+1; j2 < LEN - 2; j2++) { if (bitcount[bitmap[j2]] < 2 || bitcount[bitmap[j2]] > 4) continue; for (j3 = j2+1; j3 < LEN - 1; j3++) { if (bitcount[bitmap[j3]] < 2 || bitcount[bitmap[j3]] > 4) continue; for (j4 = j3+1; j4 < LEN; j4++) { if (bitcount[bitmap[j4]] < 2 || bitcount[bitmap[j4]] > 4) continue; value = bitmap[j1] | bitmap[j2] | bitmap[j3] | bitmap[j4]; if (bitcount[value] > 4) continue; // possible hit for (i = 0; i < LEN; i++) { if (i == j1 || i == j2 || i == j3 || i == j4) continue; for (j = 0; j < LEN; j++) { if (((1 << j) & value) && puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[candidate]) { if (slow) printf("jellyfish columns: %d %d %d %d\n" "eliminate: candidate: %d from row: %d col: %d\n", j1+1, j2+1, j3+1, j4+1, convert(candidate), j+1, i+1); puzzle[j][i].possible[candidate] = 0; find13++; found = 1; } } } } } } } } return found; } int sw3() // improved swordfish { int i, j, count; int candidate, value; int bitmap[LEN]; int j1, j2, j3; int found = 0; if (bitinited == 0) { bitinited = 1; for (i = 0; i < 65536; i++) { j = i; count = 0; while (j) { if (j&1) count++; j>>= 1; } bitcount[i] = count; } } for (candidate = 0; candidate < LEN; candidate++) { // fill in bitmap array for rows: for (i = 0; i < LEN; i++) { value = 0; for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[candidate]) value |= (1 << j); bitmap[i] = value; } for (j1 = 0; j1 < LEN - 2; j1++) { if (bitcount[bitmap[j1]] < 2 || bitcount[bitmap[j1]] > 3) continue; for (j2 = j1+1; j2 < LEN - 1; j2++) { if (bitcount[bitmap[j2]] < 2 || bitcount[bitmap[j2]] > 3) continue; for (j3 = j2+1; j3 < LEN; j3++) { if (bitcount[bitmap[j3]] < 2 || bitcount[bitmap[j3]] > 3) continue; value = bitmap[j1] | bitmap[j2] | bitmap[j3]; if (bitcount[value] > 3) continue; // possible hit for (i = 0; i < LEN; i++) { if (i == j1 || i == j2 || i == j3) continue; for (j = 0; j < LEN; j++) { if (((1 << j) & value) && puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[candidate]) { if (slow) printf("swordfish rows: %d %d %d\n" "eliminate: candidate: %d from row: %d col: %d\n", j1+1, j2+1, j3+1, convert(candidate), i+1, j+1); puzzle[i][j].possible[candidate] = 0; find10++; found = 1; } } } } } } // now check columns: for (i = 0; i < LEN; i++) { value = 0; for (j = 0; j < LEN; j++) if (puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[candidate]) value |= (1 << j); bitmap[i] = value; } for (j1 = 0; j1 < LEN - 2; j1++) { if (bitcount[bitmap[j1]] < 2 || bitcount[bitmap[j1]] > 3) continue; for (j2 = j1+1; j2 < LEN - 1; j2++) { if (bitcount[bitmap[j2]] < 2 || bitcount[bitmap[j2]] > 3) continue; for (j3 = j2+1; j3 < LEN; j3++) { if (bitcount[bitmap[j3]] < 2 || bitcount[bitmap[j3]] > 3) continue; value = bitmap[j1] | bitmap[j2] | bitmap[j3]; if (bitcount[value] > 3) continue; // possible hit for (i = 0; i < LEN; i++) { if (i == j1 || i == j2 || i == j3) continue; for (j = 0; j < LEN; j++) { if (((1 << j) & value) && puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[candidate]) { if (slow) printf("swordfish columns: %d %d %d\n" "eliminate: candidate: %d from row: %d col: %d\n", j1+1, j2+1, j3+1, convert(candidate), j+1, i+1); puzzle[j][i].possible[candidate] = 0; find10++; found = 1; } } } } } } } return found; } int swordfish() // fucked: need to look at 3 individual columns. { int k, can; int j1; int jj1, jj2, jj3; int count, findfish; int tmpsave[LEN], tmpsave1[LEN], tmpsave2[LEN], tmpsave3[LEN]; findfish = 0; for (can = 0; can < LEN; can++) { // can = candidate for (jj1 = 0; jj1 < LEN-2; jj1++) { count = 0; for (k = 0; k < LEN; k++) { tmpsave1[k] = 0; if (puzzle[jj1][k].value == UNKNOWN && puzzle[jj1][k].possible[can]) { tmpsave1[k] = 1; count++; } } if (count > 3 || count < 2) continue; for (jj2 = jj1+1; jj2 < LEN-1; jj2++) { count = 0; for (k = 0; k < LEN; k++) { tmpsave2[k] = 0; if (puzzle[jj2][k].value == UNKNOWN && puzzle[jj2][k].possible[can]) { tmpsave2[k] = 1; count++; } } if (count > 3 || count < 2) continue; for (jj3 = jj2+1; jj3 < LEN; jj3++) { count = 0; for (k = 0; k < LEN; k++) { tmpsave3[k] = 0; if (puzzle[jj3][k].value == UNKNOWN && puzzle[jj3][k].possible[can]) { tmpsave3[k] = 1; count++; } } if (count > 3 || count < 2) continue; // we've got a possible triple that works count = 0; for (k = 0; k < LEN; k++) if (tmpsave[k] = tmpsave1[k]|tmpsave2[k]|tmpsave3[k]) count++; if (count != 3) continue; // Got one! Let's see if we can nuke some possibilities ... for (k= 0; k < LEN; k++) { if (k != jj1 && k != jj2 && k != jj3) for (j1 = 0; j1 < LEN; j1++) if (tmpsave[j1] == 1 && puzzle[k][j1].value == UNKNOWN && puzzle[k][j1].possible[can]) { puzzle[k][j1].possible[can] = 0; if (slow) { printf("swordfish rows: %d %d %d:\n", jj1+1, jj2+1, jj3+1); printf("eliminate candidate %d " "from row %d column %d\n", convert(can), k+1, j1+1); } findfish++; find10++; } } } } } for (jj1 = 0; jj1 < LEN-2; jj1++) { count = 0; for (k = 0; k < LEN; k++) { tmpsave1[k] = 0; if (puzzle[k][jj1].value == UNKNOWN && puzzle[k][jj1].possible[can]) { tmpsave1[k] = 1; count++; } } if (count > 3 || count < 2) continue; for (jj2 = jj1+1; jj2 < LEN-1; jj2++) { count = 0; for (k = 0; k < LEN; k++) { tmpsave2[k] = 0; if (puzzle[k][jj2].value == UNKNOWN && puzzle[k][jj2].possible[can]) { tmpsave2[k] = 1; count++; } } if (count > 3 || count < 2) continue; for (jj3 = jj2+1; jj3 < LEN; jj3++) { count = 0; for (k = 0; k < LEN; k++) { tmpsave3[k] = 0; if (puzzle[k][jj3].value == UNKNOWN && puzzle[k][jj3].possible[can]) { tmpsave3[k] = 1; count++; } } if (count > 3 || count < 2) continue; // we've got a possible triple that works count = 0; for (k = 0; k < LEN; k++) if (tmpsave[k] = tmpsave1[k]|tmpsave2[k]|tmpsave3[k]) count++; if (count != 3) continue; // Got one! Let's see if we can nuke some possibilities ... for (k= 0; k < LEN; k++) { if (k != jj1 && k != jj2 && k != jj3) for (j1 = 0; j1 < LEN; j1++) if (tmpsave[j1] == 1 && puzzle[j1][k].value == UNKNOWN && puzzle[j1][k].possible[can]) { puzzle[j1][k].possible[can] = 0; if (slow) { printf("swordfish columns: %d %d %d:\n", jj1+1, jj2+1, jj3+1); printf("eliminate candidate %d " "from row %d column %d\n", convert(can), j1+1, k+1); } findfish++; find10++; } } } } } } if (findfish) return 1; return 0; } int doublepair() // bad code! { int i, j, k, l, m, v, c, count, n, o, p, q; int r, c1, c2; int R; int can[LEN][LEN]; if (bitinited == 0) { bitinited = 1; for (i = 0; i < 65536; i++) { j = i; count = 0; while (j) { if (j&1) count++; j>>= 1; } bitcount[i] = count; } } for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { can[i][j] = 0; for (k = 0; k < LEN; k++) if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[k]) can[i][j] |= (1 << k); } count = 0; // check rows first: for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) for (k = 0; k < SIZE; k++) for (l = 0; l < SIZE-1; l++) for (m = l+1; m < SIZE; m++) { r = i*SIZE+k; c1 = j*SIZE+l; c2 = j*SIZE+m; if (can[r][c1] == can[r][c2] && bitcount[can[r][c1]] == 2) { for (n = 0; n < SIZE; n++) { if (n == i) continue; for (o = 0; o < SIZE; o++) { R = n*SIZE + o; if (can[R][c1] == can[R][c2] && ((can[R][c1] & can[r][c1]) == can[r][c1]) && bitcount[can[R][c1]] == 3) { for (c = 0; c < LEN; c++) if ((can[r][c1] ^ can[R][c1]) == (1 << c)) v = c; for (p = 0; p < SIZE; p++) for (q = 0; q < SIZE; q++) if (p != o || (q != l && q != m)) if (puzzle[n*SIZE+p][j*SIZE+q].value == UNKNOWN && puzzle[n*SIZE+p][j*SIZE+q].possible[v]) { printf("row unique constraint: row: %d col: %d, value: %d\n", n*SIZE+p+1, j*SIZE+q+1, convert(v)); puzzle[n*SIZE+p][j*SIZE+q].possible[v] = 0; find14++; count = 1; } } } } } } // now check columns: for (i = 0; i < SIZE; i++) for (j = 0; j < SIZE; j++) for (k = 0; k < SIZE; k++) for (l = 0; l < SIZE-1; l++) for (m = l+1; m < SIZE; m++) { r = i*SIZE+k; c1 = j*SIZE+l; c2 = j*SIZE+m; if (can[c1][r] == can[c2][r] && bitcount[can[c1][r]] == 2) { for (n = 0; n < SIZE; n++) { if (n == i) continue; for (o = 0; o < SIZE; o++) { R = n*SIZE + o; if (can[c1][R] == can[c2][R] && ((can[c1][R] & can[c1][r]) == can[c1][r]) && bitcount[can[c1][R]] == 3) { for (c = 0; c < LEN; c++) if ((can[c1][r] ^ can[c1][R]) == (1 << c)) v = c; for (p = 0; p < SIZE; p++) for (q = 0; q < SIZE; q++) if (p != o || (q != l && q != m)) if (puzzle[j*SIZE+q][n*SIZE+p].value == UNKNOWN && puzzle[j*SIZE+q][n*SIZE+p].possible[v]) { printf("column unique constraint: row: %d col: %d, value: %d\n", j*SIZE+q+1, n*SIZE+p+1, convert(v)); puzzle[j*SIZE+q][n*SIZE+p].possible[v] = 0; find14++; count = 1; } } } } } } return count; } struct pairset { int v1, v2; int val; int p1, p2; // pointer to previous } puz[LEN][LEN], b1[LEN][LEN], b2[LEN][LEN]; void copypairset(struct pairset from[LEN][LEN], struct pairset to[LEN][LEN]) { int i, j; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { to[i][j] = from[i][j]; } } void initpairset(struct pairset b[LEN][LEN]) { int i, j, k, count; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { count = 0; b[i][j].p1 = b[i][j].p2 = UNKNOWN; b[i][j].val = UNKNOWN; if (puzzle[i][j].value == UNKNOWN) { for (k = 0; k < LEN; k++) { if (puzzle[i][j].possible[k]) if (count == 0) { b[i][j].v1 = k; count++; } else if (count == 1) { b[i][j].v2 = k; count++; } else count++; } } if (count != 2) b[i][j].v1 = b[i][j].v2 = UNKNOWN; } } void printback(struct pairset b[LEN][LEN], int i, int j) { int i1, j1; int stack[100][3]; int ptr = 0; stack[ptr][0] = i+1; stack[ptr][1] = j+1; stack[ptr++][2] = convert(b[i][j].val); while (1) { i1 = b[i][j].p1; j1 = b[i][j].p2; i = i1; j = j1; if (i == UNKNOWN) break; stack[ptr][0] = i+1; stack[ptr][1] = j+1; stack[ptr++][2] = convert(b[i][j].val); } ptr--; while (ptr >= 0) { printf("(%d %d)=%d", stack[ptr][0], stack[ptr][1], stack[ptr][2]); if (ptr != 0) printf(" =>\n"); else printf("\n"); ptr--; } } int iterateforce(struct pairset b[LEN][LEN]) { int i, j, k, l, v, c1, c2; int found = 0; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { if ((v = b[i][j].val) == UNKNOWN) continue; for (k = 0; k < LEN; k++) { if (b[k][j].val == UNKNOWN && b[k][j].v1 == v) { b[k][j].val = b[k][j].v2; b[k][j].p1 = i; b[k][j].p2 = j; found = 1; } if (b[k][j].val == UNKNOWN && b[k][j].v2 == v) { b[k][j].val = b[k][j].v1; b[k][j].p1 = i; b[k][j].p2 = j; found = 1; } if (b[i][k].val == UNKNOWN && b[i][k].v1 == v) { b[i][k].val = b[i][k].v2; b[i][k].p1 = i; b[i][k].p2 = j; found = 1; } if (b[i][k].val == UNKNOWN && b[i][k].v2 == v) { b[i][k].val = b[i][k].v1; b[i][k].p1 = i; b[i][k].p2 = j; found = 1; } if (b[c1 = SIZE*(i/SIZE) + k/SIZE][c2 = SIZE*(j/SIZE)+(k%SIZE)].val == UNKNOWN && b[c1][c2].v1 == v) { b[c1][c2].val = b[c1][c2].v2; b[c1][c2].p1 = i; b[c1][c2].p2 = j; found = 1; } if (b[c1 = SIZE*(i/SIZE) + k/SIZE][c2 = SIZE*(j/SIZE)+(k%SIZE)].val == UNKNOWN && b[c1][c2].v2 == v) { b[c1][c2].val = b[c1][c2].v1; b[c1][c2].p1 = i; b[c1][c2].p2 = j; found = 1; } } } return found; } int forcingchains() { int i, j, k, l, v; initpairset(puz); for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { if (puz[i][j].v1 == UNKNOWN) continue; // got a pair: make copies copypairset(puz, b1); b1[i][j].val = puz[i][j].v1; copypairset(puz, b2); b2[i][j].val = puz[i][j].v2; while (iterateforce(b1) || iterateforce(b2)) { // check for forced value for (k = 0; k < LEN; k++) for (l = 0; l < LEN; l++) if ((b1[k][l].val != UNKNOWN) && (b1[k][l].val == b2[k][l].val)) { v = puzzle[k][l].value = b1[k][l].val; if (slow) { printf("forcing chain: row %d column %d takes %d\n", k+1, l+1, convert(v)); printback(b1, k, l); printback(b2, k, l); } find15++; return 1; } } } // apparently we didn't find anything ... return 0; } void debugcolorarray(int can, int colors[LEN][LEN]) { int i, j; printf("Candidate: %d\n----------\n", can); for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) printf("%3d ", colors[i][j]); printf("\n"); } printf("---------\n"); } int fillexclusion(int line[LEN], int exclude[200][2], int excludecount) { int i, j, k; int a, b, tmp, fail; for (i = 0; i < LEN-1; i++) { if (line[i] == 0) continue; for (j = i+1; j < LEN; j++) { if (line[j] == 0) continue; if ((line[i] == -line[j]) || (line[i] == line[j])) continue; a = line[i]; b = line[j]; if (a > b) { tmp = a; a = b; b = tmp; } fail = 0; for (k = 0; k < excludecount; k++) if (exclude[k][0] == a && exclude[k][1] == b) fail = 1; if (fail) continue; exclude[excludecount][0] = a; exclude[excludecount++][1] = b; } } return excludecount; } int addexclude(int exclude[200][2], int a, int b, int excludecount) { int tmp, i, fail; if (a > b) { tmp = a; a = b; b = tmp; } fail = 0; for (i = 0; i < excludecount; i++) if (exclude[i][0] == a && exclude[i][1] == b) { fail = 1; break; } if (fail == 0) { exclude[excludecount][0] = a; exclude[excludecount++][1] = b; } return excludecount; } int colorcheck() { int can, count; int i, j, k; int i1, j1; int freecolor; int colors[LEN][LEN]; int save[LEN], save1[LEN], save2[LEN]; int colorchange = 0; int multichange = 0; for (can = 0; can < LEN; can++) { freecolor = 1; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) colors[i][j] = 0; for (i = 0; i < LEN; i++) { count = 0; for (k = 0; k < LEN; k++) if (puzzle[i][k].value == UNKNOWN && puzzle[i][k].possible[can]) save[count++] = k; if (count == 2) { // found a row-conjugate colors[i][save[0]] = freecolor; colors[i][save[1]] = -freecolor; freecolor++; } } for (i = 0; i < LEN; i++) { count = 0; for (k = 0; k < LEN; k++) if (puzzle[k][i].value == UNKNOWN && puzzle[k][i].possible[can]) save[count++] = k; if (count == 2) { // found a column-conjugate int a1, a2; a1 = colors[save[0]][i]; a2 = colors[save[1]][i]; if ( a1 && a2 ) { for (i1 = 0; i1 < LEN; i1++) for (j1 = 0; j1 < LEN; j1++) { if (colors[i1][j1] == a1) colors[i1][j1] = -a2; if (colors[i1][j1] == -a1) colors[i1][j1] = a2; } } else if (colors[save[0]][i]== 0 && colors[save[1]][i] == 0) { colors[save[0]][i] = freecolor; colors[save[1]][i] = -freecolor; freecolor++; } else if (colors[save[0]][i]) { colors[save[1]][i] = -colors[save[0]][i]; } else { // colors[i][save[1]] != 0 colors[save[0]][i] = - colors[save[1]][i]; } } } for (i = 0; i < LEN; i += SIZE) for (j = 0; j < LEN; j += SIZE) { count = 0; for (i1 = 0; i1 < SIZE; i1++) for (j1 = 0; j1 < SIZE; j1++) if (puzzle[i+i1][j+j1].value == UNKNOWN && puzzle[i+i1][j+j1].possible[can]) { save1[count] = i1; save2[count++] = j1; } if (count == 2) { // found a box-conjugate int a1, a2; a1 = colors[i+save1[0]][j+save2[0]]; a2 = colors[i+save1[1]][j+save2[1]]; if ( a1 && a2 ) { for (i1 = 0; i1 < LEN; i1++) for (j1 = 0; j1 < LEN; j1++) { if (colors[i1][j1] == a1) colors[i1][j1] = -a2; if (colors[i1][j1] == -a1) colors[i1][j1] = a2; } } else if (a1 == 0 && a2 == 0) { colors[i+save1[0]][j+save2[0]] = freecolor; colors[i+save1[1]][j+save2[1]] = -freecolor; freecolor++; } else if (colors[i+save1[0]][j+save2[0]]) { colors[i+save1[1]][j+save2[1]] = -colors[i+save1[0]][j+save2[0]]; } else { // colors[i+save1[1]][j+save2[1]] != 0 colors[i+save1[0]][j+save2[0]] = -colors[i+save1[1]][j+save2[1]]; } } } // OK, now it's completely colored; let's look for stuff... freecolor--; // colors run from -freecolor to freecolor for (k = -freecolor; k <= freecolor; k++) { if (k == 0) continue; for (i = 0; i < LEN; i++) { // look in each row count = 0; for (j = 0; j < LEN; j++) if (colors[i][j] == k) count++; if (count > 1) { for (i1 = 0; i1 < LEN; i1++) for (j = 0; j < LEN; j++) if (colors[i1][j] == k) { puzzle[i1][j].possible[can] = 0; if (slow) printf("coloring: candidate %d" " impossible at row %d, column %d\n", convert(can), i1+1, j+1); colorchange++; } } } for (i = 0; i < LEN; i++) { // look in each column count = 0; for (j = 0; j < LEN; j++) if (colors[j][i] == k) count++; if (count > 1) for (i1 = 0; i1 < LEN; i1++) for (j = 0; j < LEN; j++) if (colors[j][i1] == k) { puzzle[j][i1].possible[can] = 0; if (slow) printf("coloring: candidate %d" " impossible at row %d, column %d\n", convert(can), j+1, i1+1); colorchange++; } } for (i = 0; i < LEN; i += SIZE) for (j = 0; j < LEN; j += SIZE) { count = 0; for (i1 = 0; i1 < SIZE; i1++) for (j1 = 0; j1 < SIZE; j1++) if (colors[i+i1][j+j1] == k) count++; if (count > 1) for (i1 = 0; i1 < LEN; i1++) for (j1 = 0; j1 < LEN; j1++) if (colors[i1][j1] == k) { puzzle[i1][j1].possible[can] = 0; if (slow) printf("coloring: candidate %d" " impossible at row %d, column %d\n", convert(can), i1+1, j1+1); colorchange++; } } } for (k = 1; k <= freecolor; k++) { int plus, minus; // check for k and -k in same row, column, block for (i = 0; i < LEN; i++) { plus = minus = 0; for (j = 0; j < LEN; j++) { if (colors[i][j] == k) plus = j; if (colors[i][j] == -k) minus = j; } if (plus && minus) for (j = 0; j < LEN; j++) { if (j != plus && j != minus) if (puzzle[i][j].value == UNKNOWN && puzzle[i][j].possible[can]) { puzzle[i][j].possible[can] = 0; if (slow) printf("coloringx: candidate %d" " impossible at row %d, column %d\n", convert(can), i+1, j+1); colorchange++; } } } for (i = 0; i < LEN; i++) { plus = minus = 0; for (j = 0; j < LEN; j++) { if (colors[j][i] == k) plus = j; if (colors[j][i] == -k) minus = j; } if (plus && minus) for (j = 0; j < LEN; j++) { if (j != plus && j != minus) if (puzzle[j][i].value == UNKNOWN && puzzle[j][i].possible[can]) { puzzle[j][i].possible[can] = 0; if (slow) printf("coloringy: candidate %d" " impossible at row %d, column %d\n", convert(can), j+1, i+1); colorchange++; } } } for (i = 0; i < LEN; i += SIZE) for (j = 0; j < LEN; j += SIZE) { plus = minus = 0; for (i1 = 0; i1 < SIZE; i1++) for (j1 = 0; j1 < SIZE; j1++) { if (colors[i+i1][j+j1] == k) plus = 3*i1 + j1; if (colors[i+i1][j+j1] == -k) minus = 3*i1 + j1; } if (plus && minus) for (i1 = 0; i1 < LEN; i1++) { if (i1 != plus && i1 != minus) if (puzzle[i + i1/3][j + (i1%3)].value == UNKNOWN && puzzle[i + i1/3][j + (i1%3)].possible[can]) { puzzle[i + i1/3][j + (i1%3)].possible[can] = 0; if (slow) printf("coloringz: candidate %d" " impossible at row %d, column %d\n", convert(can), i + i1/3 + 1, j + (i1%3) +1); colorchange++; } } } for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { if (colors[i][j] != k) continue; for (i1 = 0; i1 < LEN; i1++) for (j1 = 0; j1 < LEN; j1++) { if (colors[i1][j1] != -k) continue; if (i == i1 || j == j1) continue; if (puzzle[i][j1].value == UNKNOWN && puzzle[i][j1].possible[can]) { puzzle[i][j1].possible[can] = 0; if (slow) printf("coloringq: candidate %d" " impossible at row %d, column %d\n", convert(can), i + 1, j1 + 1); colorchange++; } if (puzzle[i1][j].value == UNKNOWN && puzzle[i1][j].possible[can]) { puzzle[i1][j].possible[can] = 0; if (slow) printf("coloringq: candidate %d" " impossible at row %d, column %d\n", convert(can), i1 + 1, j + 1); colorchange++; } } } } // We can use the same color data to check on the multicolor situation. // First, we need to make an exclusion table: int exclude[200][2]; int excludecount, excludesave; int colorlist[200][2]; // 0 => pos; 1 => neg int colorlistcount; int line[LEN]; int a, b, newcount, i2, j2, i3, j3; // Normalize colors excludecount = colorlistcount = 0; // Fill in exclusions: for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) line[j] = colors[i][j]; excludecount = fillexclusion(line, exclude, excludecount); } for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) line[j] = colors[j][i]; excludecount = fillexclusion(line, exclude, excludecount); } for (i = 0; i < LEN; i += SIZE) for (j = 0; j < LEN; j += SIZE) { for (k = 0; k < LEN; k++) line[k] = colors[i + k/3][j + (k%3)]; excludecount = fillexclusion(line, exclude, excludecount); } // Now complete the transitive closure of the exclusion list: do { excludesave = excludecount; for (i = 0; i < excludecount; i++) { a = exclude[i][0]; b = exclude[i][1]; for (j = 0; j < excludecount; j++) { if (a == -exclude[j][0]) excludecount = addexclude(exclude, b, exclude[j][1], excludecount); if (a == -exclude[j][1]) excludecount = addexclude(exclude, b, exclude[j][0], excludecount); if (b == -exclude[j][0]) excludecount = addexclude(exclude, a, exclude[j][1], excludecount); if (b == -exclude[j][1]) excludecount = addexclude(exclude, a, exclude[j][0], excludecount); } } } while (excludesave != excludecount); //if (excludecount > 6) printf("Exclude count: %d\n", excludecount); // Now, for every pair a!b in the exclude list, we need to find // instances at the intersection of A and B in the table and eliminate them if (0 && excludecount >= 2) { debugcolorarray(can, colors); for (i = 0; i < excludecount; i++) printf("%d excludes %d\n", exclude[i][0], exclude[i][1]); printf("excludecount: %d\n", excludecount); } // Look for implications of the exclude list. for (i = 0; i < excludecount; i++) { if (exclude[i][0] == -exclude[i][1]) continue; if (exclude[i][0] == exclude[i][1]) { // number excludes itself! for (i1 = 0; i1 < LEN; i1++) for (j1 = 0; j1 < LEN; j1++) { if (colors[i1][j1] == -exclude[i][0]) { puzzle[i1][j1].value = can; if (slow) printf("multicolor: %d at row %d column %d\n", convert(can), i1+1, j1+1); multichange++; } if (colors[i1][j1] == exclude[i][0]) puzzle[i1][j1].possible[can] = 0; // for good measure... } } else { // different exclude values for (i1 = 0; i1 < LEN; i1++) for (j1 = 0; j1 < LEN; j1++) { if (colors[i1][j1] != -exclude[i][0]) continue; for (i2 = 0; i2 < LEN; i2++) for (j2 = 0; j2 < LEN; j2++) { if (i1 == i2 || j1 == j2) continue; if (colors[i2][j2] != -exclude[i][1]) continue; if (puzzle[i1][j2].value == UNKNOWN && puzzle[i1][j2].possible[can]) { puzzle[i1][j2].possible[can] = 0; if (slow) printf("multicolor: %d impossible " "in row %d column %d\n", convert(can), i1+1, j2+1); multichange++; } if (puzzle[i2][j1].value == UNKNOWN && puzzle[i2][j1].possible[can]) { puzzle[i2][j1].possible[can] = 0; if (slow) printf("multicolor: %d impossible " "in row %d column %d\n", convert(can), i2+1, j1+1); multichange++; } if ((i1/3 == i2/3) && (j1/3 == j2/3)) { // in same block for (i3 = 0; i3 < SIZE; i3++) for (j3 = 0; j3 < SIZE; j3++) { a = 3*(i1/3) + i3; b = 3*(j1/3) + j3; if (((a == i1) && (b == j1)) || ((a == i2) && (b == j2))) continue; if (puzzle[a][b].value == UNKNOWN && puzzle[a][b].possible[can]) { puzzle[a][b].possible[can] = 0; if (slow) printf("multicolor: %d impossible " "in row %d column %d\n", convert(can), a+1, b+1); multichange++; } } } if ((i1/3) == (i2/3)) { for (j3 = 0; j3 < SIZE; j3++) { a = i1; b = 3*(j2/3) + j3; if (puzzle[a][b].value == UNKNOWN && puzzle[a][b].possible[can] && colors[a][b] == 0) { puzzle[a][b].possible[can] = 0; if (slow) printf("multicolora: %d impossible " "in row %d column %d\n", convert(can), a+1, b+1); multichange++; } } } if ((j1/3) == (j2/3)) { for (i3 = 0; i3 < SIZE; i3++) { a = 3*(i2/3) + i3; b = j1; if (puzzle[a][b].value == UNKNOWN && puzzle[a][b].possible[can] && colors[a][b] == 0) { puzzle[a][b].possible[can] = 0; if (slow) printf("multicolorb: %d impossible " "in row %d column %d\n", convert(can), a+1, b+1); multichange++; } } } if ((i1/3) == (i2/3)) { for (j3 = 0; j3 < SIZE; j3++) { a = i2; b = 3*(j1/3) + j3; if (puzzle[a][b].value == UNKNOWN && puzzle[a][b].possible[can] && colors[a][b] == 0) { puzzle[a][b].possible[can] = 0; if (slow) printf("multicolorc: %d impossible " "in row %d column %d\n", convert(can), a+1, b+1); multichange++; } } } if ((j1/3) == (j2/3)) { for (i3 = 0; i3 < SIZE; i3++) { a = 3*(i1/3) + i3; b = j2; if (puzzle[a][b].value == UNKNOWN && puzzle[a][b].possible[can] && colors[a][b] == 0) { puzzle[a][b].possible[can] = 0; if (slow) printf("multicolord: %d impossible " "in row %d column %d\n", convert(can), a+1, b+1); multichange++; } } } } } } } } find11 += colorchange; find12 += multichange; if (colorchange || multichange) return 1; return 0; } void printpuzzle() { int i, j, v; if (SIZE == 3) { printf("+---+---+---+\n"); for (i = 0; i < LEN; i++) { printf("|"); for (j = 0; j < LEN; j++) { v = puzzle[i][j].value; if (v == 0) v = 9; if (v == UNKNOWN) printf(" "); else printf("%c", '0' + v); if(j % SIZE == 2) printf("|"); } if (i % SIZE == 2) printf("\n+---+---+---+\n"); else printf("\n"); } } if (SIZE == 4) { printf("+----+----+----+----+\n"); for (i = 0; i < LEN; i++) { printf("|"); for (j = 0; j < LEN; j++) { v = puzzle[i][j].value; if (v == UNKNOWN) printf(" "); else { if (v <= 9) printf("%c", '0' + v); else printf("%c", 'A' + v - 10); } if(j % SIZE == 3) printf("|"); } if (i % SIZE == 3) printf("\n+----+----+----+----+\n"); else printf("\n"); } } } int countpossibles(); struct square puzzlestack[100][LEN][LEN]; int guessstack[100][3]; int stackpointer = 0; void pushpuzzle(int ival, int jval, int guess) { int i, j; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) puzzlestack[stackpointer][i][j] = puzzle[i][j]; guessstack[stackpointer][0] = ival; guessstack[stackpointer][1] = jval; guessstack[stackpointer][2] = guess; push[stackpointer]++; //printf("push: %d: %d\n", stackpointer, countpossibles()); stackpointer++; } int poppuzzle() { int i, j, ival, jval, guess; stackpointer--; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) puzzle[i][j] = puzzlestack[stackpointer][i][j]; ival = guessstack[stackpointer][0]; jval = guessstack[stackpointer][1]; guess = guessstack[stackpointer][2]; //if (puzzle[ival][jval].possible[guess] == 0) // printf("poppuzzle: fuckup!\n"); puzzle[ival][jval].possible[guess] = 0; //printf("pop: %d: %d\n", stackpointer, countpossibles()); } int makeguess() { int i, j, k; int ibest, jbest, count = 0, countbest, ksave, kbest = 0; ibest = jbest = ksave = -1; countbest = 1000; resetpossible(); // first, find the best slot: for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN) { count = 0; for (k = 0; k < LEN; k++) { if (puzzle[i][j].possible[k]) { count++; ksave = k; } } if (count == 1) { printf("makeguess: gross fuckup!!!\n"); exit(1); } if (count > 0 && count < countbest) { countbest = count; ibest = i; jbest = j; kbest = ksave; } } if ((countbest == 1000 || count == 0) && stackpointer <= 0) // impossible to solve return 0; pushpuzzle(ibest, jbest, kbest); puzzle[ibest][jbest].value = kbest; return 1; #ifdef CRUDEGUESS for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) if (puzzle[i][j].value == UNKNOWN) for (k = 0; k < LEN; k++) if (puzzle[i][j].possible[k]) { pushpuzzle(i, j, k); puzzle[i][j].value = k; return 1; } return 0; // puzzle impossible to solve #endif } void dumpepshead(FILE *f) { fprintf(f, "%%!PS-Adobe-2.0 EPSF-1.2\n"); fprintf(f, "%%%%Creator: Sudoku\n"); fprintf(f, "%%%%BoundingBox: 0 0 1000 1000\n"); fprintf(f, "%%%%EndComments\n"); fprintf(f, "\n"); fprintf(f, "%% (5) 4 5 shownum shows a '5' in row 4, column 5\n"); fprintf(f, "\n"); fprintf(f, "/shownum {\n"); fprintf(f, "1 sub exch 1 sub exch\n"); fprintf(f, "100 mul 30 add exch\n"); fprintf(f, "8 exch sub 100 mul 23 add\n"); fprintf(f, "moveto\n"); fprintf(f, "/Times-Roman findfont 80 scalefont setfont\n"); fprintf(f, "show\n"); fprintf(f, "} bind def\n"); fprintf(f, "\n"); fprintf(f, "%% 3 5 showring emphasizes (3, 5)\n"); fprintf(f, "/showring {\n"); fprintf(f, "newpath\n"); fprintf(f, "100 mul 50 sub exch\n"); fprintf(f, "8 exch sub 100 mul 150 add\n"); fprintf(f, "60 0 360 arc stroke\n"); fprintf(f, "} bind def\n"); fprintf(f, "\n"); fprintf(f, "%% (1278) 3 6 shows a possible grid at (3, 6)\n"); fprintf(f, "\n"); fprintf(f, "/showgrid {\n"); fprintf(f, "/Times-Roman findfont 30 scalefont setfont\n"); fprintf(f, "1 sub exch 1 sub exch\n"); fprintf(f, "100 mul exch\n"); fprintf(f, "8 exch sub 100 mul\n"); fprintf(f, "gsave\n"); fprintf(f, "translate\n"); fprintf(f, "-3 -5 translate\n"); fprintf(f, "dup (1) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "15 75 moveto (1) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (2) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "45 75 moveto (2) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (3) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "75 75 moveto (3) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (4) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "15 45 moveto (4) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (5) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "45 45 moveto (5) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (6) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "75 45 moveto (6) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (7) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "15 15 moveto (7) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (8) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "45 15 moveto (8) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "dup (9) search\n"); fprintf(f, "{pop pop pop\n"); fprintf(f, "75 15 moveto (9) show}\n"); fprintf(f, "{pop} ifelse\n"); fprintf(f, "pop\n"); fprintf(f, "grestore\n"); fprintf(f, "} bind def\n"); fprintf(f, "\n"); fprintf(f, "75 25 translate\n"); fprintf(f, "1 setlinecap\n"); fprintf(f, "1 setlinejoin\n"); fprintf(f, "\n"); fprintf(f, "%% draw the grid:\n"); fprintf(f, "\n"); fprintf(f, "0 100 900 {\n"); fprintf(f, "dup dup dup\n"); fprintf(f, "dup 100 idiv 3 mod 0 eq\n"); fprintf(f, "newpath\n"); fprintf(f, "{ 3 setlinewidth} {1 setlinewidth} ifelse\n"); fprintf(f, "0 moveto 900 lineto stroke\n"); fprintf(f, "newpath\n"); fprintf(f, "0 exch moveto 900 exch lineto stroke\n"); fprintf(f, "} for\n"); fprintf(f, "\n"); fprintf(f, "%% label the grid\n"); fprintf(f, "\n"); fprintf(f, "/Times-Italic findfont 50 scalefont setfont\n"); fprintf(f, "-50 35 moveto (i) show\n"); fprintf(f, "-50 135 moveto (h) show\n"); fprintf(f, "-50 235 moveto (g) show\n"); fprintf(f, "-50 335 moveto (f) show\n"); fprintf(f, "-50 435 moveto (e) show\n"); fprintf(f, "-50 535 moveto (d) show\n"); fprintf(f, "-50 635 moveto (c) show\n"); fprintf(f, "-50 735 moveto (b) show\n"); fprintf(f, "-50 835 moveto (a) show\n"); fprintf(f, "35 925 moveto (1) show\n"); fprintf(f, "135 925 moveto (2) show\n"); fprintf(f, "235 925 moveto (3) show\n"); fprintf(f, "335 925 moveto (4) show\n"); fprintf(f, "435 925 moveto (5) show\n"); fprintf(f, "535 925 moveto (6) show\n"); fprintf(f, "635 925 moveto (7) show\n"); fprintf(f, "735 925 moveto (8) show\n"); fprintf(f, "835 925 moveto (9) show\n"); fprintf(f, "\n"); } void dumpepsvalue(FILE *f, int row, int col, int value) { fprintf(f, "(%d) %d %d shownum\n", convert(value), row, col); } void dumpepspossible(FILE *f, int row, int col, int *possible) { int i; fprintf(f, "("); for (i = 0; i < 9; i++) if (possible[i] && digitfilter[i]) fprintf(f, "%d", convert(i)); fprintf(f, ") %d %d showgrid\n", row, col); } void dumpepstail(FILE *f) { fprintf(f, "showpage\n"); fprintf(f, "\n"); } void dumpepspuzzle() { FILE *f; int i, j; if (slow == 0) return; if (SIZE == 4) return; // Later... much later... f = fopen("puz.eps", "w"); dumpepshead(f); for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) { if (puzzle[i][j].value != UNKNOWN) dumpepsvalue(f, i+1, j+1, puzzle[i][j].value); else dumpepspossible(f, i+1, j+1, &(puzzle[i][j].possible[0])); } dumpepstail(f); fclose(f); } void debugdump() { int i, j, k; for (i = 0; i < LEN; i++) { for (j = 0; j < LEN; j++) if (puzzle[i][j].value != UNKNOWN) printf("[[[[%d]]]] ", puzzle[i][j].value); else { for (k=0; k < LEN; k++) if (puzzle[i][j].possible[k]) printf("%d", k); else printf("."); printf(" "); } printf("\n"); } } int countpossibles() { int i, j, k; int count = 0; for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) for (k = 0; k < LEN; k++) if (puzzle[i][j].possible[k]) count++; return count; } struct square savepuzzle[LEN][LEN]; // checkpuzzle returns 0 if the puzzle's OK; 1 if at // least one solution exists int checkpuzzle() { int i, j, saveslow, issolved=0; // save the original puzzle for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) savepuzzle[i][j] = puzzle[i][j]; saveslow = slow; slow = 0; resetpossible(); while (issolved == 0) { if (solved()) issolved = 1; else { if (0 == solvable() && stackpointer) { poppuzzle(); } resetpossible(); if (findobvious()) { } else { // need to use recursion if (0 == makeguess()) { for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) puzzle[i][j] = savepuzzle[i][j]; slow = saveslow; return 1; } } } } // restore the original puzzle for (i = 0; i < LEN; i++) for (j = 0; j < LEN; j++) puzzle[i][j] = savepuzzle[i][j]; slow = saveslow; stackpointer = 0; resetpossible(); initsearchvalues(); return 0; } void printhelp() { printf("-f: filter: filter lists the series of digits\n" " to be printed in the dumped eps files. For\n" " now they are 1-9. A 0 (zero) prints none.\n"); printf("-h: help: print this message\n"); printf("-j: jumble: jumble the given puzzle to look different\n"); printf("-J: jumble, but using the seed given as a parameter\n"); printf("-x: jumble, but retain symmetry\n"); printf("-s: step mode; type for each step\n"); } int symmetric = 0; int parseoptions(int argc, char **argv) { int i = 1, j; if (argc == 1) { fprintf(stderr, "Usage: %s [-hjsx] [-f filter] [-J value] " "(%s -h for help)\n", argv[0], argv[0]); exit(1); } do { if (argv[i][0] == '-') { // read option(s) char *p = &(argv[i][1]), *q; while (*p) { switch (*p) { case 's': slow = 1; break; case 'h': printhelp(); exit(0); break; case 'j': jum = 1; break; case 'x': symmetric = jum = 1; break; case 'f': i++; q = &(argv[i][0]); for (j = 0; j < LEN; j++) digitfilter[j] = 0; while (*q) { if ('1' <= *q && *q <= '9') { if (*q == '9') *q = '0'; digitfilter[*q - '0'] = 1; } q++; } break; case 'J': i++; jum = 1; randseed = atoi(argv[i]); break; } p++; } } else { sudokufile = argv[i]; } i++; } while (i < argc); } int main(int argc, char **argv) { FILE *f; int i; char cmd[1000]; slow = jum = 0; for (i = 0; i < LEN; i++) digitfilter[i] = 1; parseoptions(argc, argv); if (sudokufile == 0) { fprintf(stderr, "Missing file name"); exit(1); } f = fopen(sudokufile, "r"); if (f == 0) { fprintf(stderr, "Couldn't open %s\n", sudokufile); exit(1); } if (slow) printf("Type to see each step\nin the solution.\n\n"); if (0 == readpuzzle(f)) { fprintf(stderr, "Invalid puzzle: no solution\n"); exit(1); } fclose(f); if (jum) jumble(symmetric); printf("Initial puzzle (%s):\n\n", sudokufile); if (slow == 0) printpuzzle(); if (checkpuzzle() == 1) { printpuzzle(); if (slow) dumpepspuzzle(); printf("no solution\n"); exit(1); } resetpossible(); if (slow) dumpepspuzzle(); while (1) { if (solved()) { if (scount == 1) { printf("There are multiple solutions. Here's another:\n"); printpuzzle(); return; } printf("Solved!\n"); if (scount == 0) printpuzzle(); //printf("slow: %d\n", slow); if (slow) dumpepspuzzle(); scount++; printstats(); printf("--------------------------------\n"); if (stackpointer) { poppuzzle(); } else return; } if (0 == solvable()) { if (slow == 1 && scount == 0) printf("Backtrack!\n"); if (stackpointer) poppuzzle(); } resetpossible(); if (slow) dumpepspuzzle(); if (slow) { if (scount == 0) printpuzzle(); if (scount == 0) { char *p; fgets(cmd, 999, stdin); p = cmd; if (*p != '\n') { int row, col, val; row = *p++ - '0' - 1; col = *p++ - '0' - 1; val = *p - '0'; if (val == 9) val = 0; puzzle[row][col].value = val; continue; } } } if (findobvious()) { } else if (lockedcandidates()) { } else if (findnaked()) { } else if (findhiddenpair()) { } else if (forcingchains()) { } else if (xwing()) { } else if (sw3()) { } else if (sw4()) { } else if (xywing()) { } else if (colorcheck()) { } else if (doublepair()) { } else { // need to use recursion if (0 == makeguess()) { printstats(); if (scount == 0) printf("Puzzle can't be solved\n"); exit(0); } if (slow == 1) { if (scount == 0) printf("Make a guess: row: %d column: %d value: %d\n", guessstack[stackpointer-1][0]+1, guessstack[stackpointer-1][1]+1, convert(guessstack[stackpointer-1][2])); } } } }