diff --git a/.gitignore b/.gitignore index d392cc8..74e1b75 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.o sudoku eval +genlut # Test data sudoku.csv @@ -15,3 +16,6 @@ compile_commands.json # Profiling results gmon.out + +# Generated code +lut.c diff --git a/Makefile b/Makefile index 47b33ea..17817e2 100644 --- a/Makefile +++ b/Makefile @@ -24,10 +24,7 @@ CFLAGS += -static LDFLAGS += -lpthread -# For profiling -# CFLAGS += -pg - -SRC = main.c sud.c solve.c +SRC = main.c sud.c solve.c lut.c OBJ = $(SRC:.c=.o) default: sudoku eval @@ -35,16 +32,16 @@ default: sudoku eval sudoku: $(OBJ) $(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $@ -eval: eval.o - $(CC) $(CFLAGS) $(LDFLAGS) eval.o -o $@ - clean: - rm -f sudoku *.o + rm -f sudoku eval genlut *.o lut.c + +lut.c: genlut + ./genlut > $@ eval.o: ds.h main.o: sud.h solve.h ds.h -sud.o: sud.h -solve.o: solve.h +sud.o: sud.h lut.h +solve.o: solve.h lut.h puzzles: sudoku.csv pv sudoku.csv | tail -n+2 | cut -d, -f1 | tr -d '\n' > $@ diff --git a/genlut.c b/genlut.c new file mode 100644 index 0000000..968203f --- /dev/null +++ b/genlut.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 Camden Dixie O'Brien + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see + * . + */ + +#include "sud.h" + +#include + +#define Q(x) #x +#define PRLUT(x) prlut(Q(x), x) + +static void prlut(const char *name, unsigned lut[NCELLS][NDIGITS - 1]) +{ + printf("const unsigned %s[NCELLS][NDIGITS - 1] = {\n", name); + for (unsigned i = 0; i < NCELLS; ++i) { + printf("\t[%2u] = { ", i); + for (unsigned j = 0; j < NDIGITS - 1; ++j) { + printf("%2u", lut[i][j]); + if (j != NDIGITS - 2) + putchar(','); + putchar(' '); + } + fputs("},\n", stdout); + } + fputs("};\n", stdout); +} + +int main(void) +{ + unsigned rowidx_lut[NCELLS][NDIGITS - 1]; + unsigned colidx_lut[NCELLS][NDIGITS - 1]; + unsigned segidx_lut[NCELLS][NDIGITS - 1]; + + /* Populate tables. */ + unsigned rowi, coli, segi; + for (unsigned r = 0; r < NDIGITS; ++r) { + for (unsigned c = 0; c < NDIGITS; ++c) { + /* Calculate row and column indices. */ + rowi = coli = 0; + for (unsigned i = 0; i < NDIGITS; ++i) { + if (i != c) + rowidx_lut[r * NDIGITS + c][rowi++] = r * NDIGITS + i; + if (i != r) + colidx_lut[r * NDIGITS + c][coli++] = i * NDIGITS + c; + } + + /* Calculate segment indices. */ + segi = 0; + const unsigned sr = SEGLEN * (r / SEGLEN); + const unsigned sc = SEGLEN * (c / SEGLEN); + for (unsigned i = sr; i < sr + SEGLEN; ++i) { + for (unsigned j = sc; j < sc + SEGLEN; ++j) { + if (i == r && j == c) + continue; + segidx_lut[r * NDIGITS + c][segi++] = i * NDIGITS + j; + } + } + } + } + + printf("/*\n * This file is auto-generated; do not edit.\n */\n\n"); + printf("#include \"lut.h\"\n\n"); + PRLUT(rowidx_lut); + putchar('\n'); + PRLUT(colidx_lut); + putchar('\n'); + PRLUT(segidx_lut); + + return 0; +} diff --git a/lut.h b/lut.h new file mode 100644 index 0000000..603fdeb --- /dev/null +++ b/lut.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 Camden Dixie O'Brien + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see + * . + */ + +#ifndef LUT_H +#define LUT_H + +#include "sud.h" + +#define NGROUP (NDIGITS - 1) + +extern const unsigned rowidx_lut[NCELLS][NGROUP]; +extern const unsigned colidx_lut[NCELLS][NGROUP]; +extern const unsigned segidx_lut[NCELLS][NGROUP]; + +#endif diff --git a/solve.c b/solve.c index f17ae60..b5cf04d 100644 --- a/solve.c +++ b/solve.c @@ -17,14 +17,15 @@ */ #include "solve.h" +#include "lut.h" #include -static void setpval(struct sudoku *sud, unsigned r, unsigned c, uint16_t pval) +static void setpval(struct sudoku *sud, unsigned i, uint16_t pval) { for (unsigned val = 0; val < NDIGITS; ++val) { if (pval & 1) { - update(sud, r, c, val); + update(sud, i, val); return; } pval >>= 1; @@ -33,86 +34,80 @@ static void setpval(struct sudoku *sud, unsigned r, unsigned c, uint16_t pval) int solve(struct sudoku *sud) { - unsigned r, c, n, i, j, val, sr, sc; + unsigned n, i, j, val; bool match; uint16_t valmask, pvals; for (n = 0;; ++n) { match = false; - for (r = 0; r < NDIGITS; ++r) { - for (c = 0; c < NDIGITS; ++c) { - if (DET(sud->cells[r][c])) - continue; + for (i = 0; i < NCELLS; ++i) { + if (DET(sud->cells[i])) + continue; - /* - * Check if there's only one possible value this cell - * can have. - */ - pvals = sud->cells[r][c] & PVALSMASK; - for (val = 0; val < NDIGITS; ++val) { - if (pvals & 1) { - if (pvals == 1) { - update(sud, r, c, val); - match = true; - goto next_cell; - } - break; + /* + * Check if there's only one possible value this cell + * can have. + */ + pvals = sud->cells[i] & PVALSMASK; + for (val = 0; val < NDIGITS; ++val) { + if (pvals & 1) { + if (pvals == 1) { + update(sud, i, val); + match = true; + goto next_cell; } - pvals >>= 1; + break; } - - /* - * Check if there's a possible value unique to this - * cell in its row. - */ - valmask = 0; - for (i = 0; i < NDIGITS; ++i) { - if (i != r && !DET(sud->cells[i][c])) - valmask |= sud->cells[i][c]; - } - if ((pvals = sud->cells[r][c] & ~valmask)) { - setpval(sud, r, c, pvals); - match = true; - continue; - } - - /* - * Check if there's a possible value unique to this - * cell in its column. - */ - valmask = 0; - for (i = 0; i < NDIGITS; ++i) { - if (i != c && !DET(sud->cells[r][i])) - valmask |= sud->cells[r][i]; - } - if ((pvals = sud->cells[r][c] & ~valmask)) { - setpval(sud, r, c, pvals); - match = true; - continue; - } - - /* - * Check if there's a possible value unique to this - * cell in its segment. - */ - sr = SEGLEN * (r / SEGLEN); - sc = SEGLEN * (c / SEGLEN); - valmask = 0; - for (i = sr; i < sr + SEGLEN; ++i) { - for (j = sc; j < sc + SEGLEN; ++j) { - if ((i != r || j != c) && !DET(sud->cells[i][j])) - valmask |= sud->cells[i][j]; - } - } - if ((pvals = sud->cells[r][c] & ~valmask)) { - setpval(sud, r, c, pvals); - match = true; - continue; - } - - next_cell:; + pvals >>= 1; } + + /* + * Check if there's a possible value unique to this + * cell in its row. + */ + valmask = 0; + for (j = 0; j < NGROUP; ++j) { + if (!DET(sud->cells[rowidx_lut[i][j]])) + valmask |= sud->cells[rowidx_lut[i][j]]; + } + if ((pvals = sud->cells[i] & ~valmask)) { + setpval(sud, i, pvals); + match = true; + continue; + } + + /* + * Check if there's a possible value unique to this + * cell in its column. + */ + valmask = 0; + for (j = 0; j < NGROUP; ++j) { + if (!DET(sud->cells[colidx_lut[i][j]])) + valmask |= sud->cells[colidx_lut[i][j]]; + } + if ((pvals = sud->cells[i] & ~valmask)) { + setpval(sud, i, pvals); + match = true; + continue; + } + + /* + * Check if there's a possible value unique to this + * cell in its segment. + */ + valmask = 0; + for (j = 0; j < NGROUP; ++j) { + if (!DET(sud->cells[segidx_lut[i][j]])) + valmask |= sud->cells[segidx_lut[i][j]]; + } + if ((pvals = sud->cells[i] & ~valmask)) { + setpval(sud, i, pvals); + match = true; + continue; + } + + next_cell:; } /* Exit if no matches. */ diff --git a/sud.c b/sud.c index be0ac41..51c8a21 100644 --- a/sud.c +++ b/sud.c @@ -16,6 +16,7 @@ * . */ +#include "lut.h" #include "sud.h" #include @@ -27,10 +28,8 @@ static void init(struct sudoku *sud) { - for (unsigned r = 0; r < NDIGITS; ++r) { - for (unsigned c = 0; c < NDIGITS; ++c) - sud->cells[r][c] = CELLINIT; - } + for (unsigned i = 0; i < NCELLS; ++i) + sud->cells[i] = CELLINIT; } bool load(struct sudoku *sud, const char *ptr) @@ -41,7 +40,7 @@ bool load(struct sudoku *sud, const char *ptr) for (unsigned i = 0; i < NCELLS; ++i) { if (ptr[i] == '0') continue; - if (update(sud, i / NDIGITS, i % NDIGITS, ptr[i] - '1') != OK) + if (update(sud, i, ptr[i] - '1') != OK) return false; } @@ -50,40 +49,29 @@ bool load(struct sudoku *sud, const char *ptr) void save(struct sudoku *sud, char *ptr) { - for (unsigned r = 0; r < NDIGITS; ++r) { - for (unsigned c = 0; c < NDIGITS; ++c) { - if (DET(sud->cells[r][c])) - *ptr++ = '1' + VAL(sud->cells[r][c]); - else - *ptr++ = '0'; - } + for (unsigned i = 0; i < NCELLS; ++i) { + if (DET(sud->cells[i])) + *ptr++ = '1' + VAL(sud->cells[i]); + else + *ptr++ = '0'; } } -enum update_res update(struct sudoku *sud, unsigned r, unsigned c, unsigned val) +enum update_res update(struct sudoku *sud, unsigned i, unsigned val) { - unsigned tr, tc; const uint16_t clearmask = ~(1 << val); - /* Update possible values of cells in same row. */ - for (tc = 0; tc < NDIGITS; ++tc) - sud->cells[r][tc] &= clearmask; - - /* Update possible values of cells in same column. */ - for (tr = 0; tr < NDIGITS; ++tr) - sud->cells[tr][c] &= clearmask; - - /* Update possible values of cells in same segment. */ - const unsigned segr0 = SEGLEN * (r / SEGLEN); - const unsigned segc0 = SEGLEN * (c / SEGLEN); - for (tr = segr0; tr < segr0 + SEGLEN; ++tr) { - for (tc = segc0; tc < segc0 + SEGLEN; ++tc) - sud->cells[tr][tc] &= clearmask; + /* Update possible values of cells in same row, column and + * segment. */ + for (unsigned j = 0; j < NGROUP; ++j) { + sud->cells[rowidx_lut[i][j]] &= clearmask; + sud->cells[colidx_lut[i][j]] &= clearmask; + sud->cells[segidx_lut[i][j]] &= clearmask; } /* Set the cell's value. */ - sud->cells[r][c] |= DETMASK; - sud->cells[r][c] |= val << VALSHIFT; + sud->cells[i] |= DETMASK; + sud->cells[i] |= val << VALSHIFT; return OK; } @@ -106,8 +94,8 @@ void print(const struct sudoku *sud) if (c != 0 && c % SEGLEN == 0) fputs("| ", stdout); - if (DET(sud->cells[r][c])) - printf("%u ", VAL(sud->cells[r][c]) + 1); + if (DET(sud->cells[IDX(r, c)])) + printf("%u ", VAL(sud->cells[IDX(r, c)]) + 1); else fputs(" ", stdout); } @@ -138,9 +126,9 @@ enum check_res check(const struct sudoku *sud) for (r = 0; r < NDIGITS; ++r) { zerocounts(digitcounts); for (c = 0; c < NDIGITS; ++c) { - if (!DET(sud->cells[r][c])) + if (!DET(sud->cells[IDX(r, c)])) return INCOMPLETE; - ++digitcounts[VAL(sud->cells[r][c])]; + ++digitcounts[VAL(sud->cells[IDX(r, c)])]; } if (!checkcounts(digitcounts)) return INCORRECT; @@ -150,9 +138,9 @@ enum check_res check(const struct sudoku *sud) for (c = 0; c < NDIGITS; ++c) { zerocounts(digitcounts); for (r = 0; r < NDIGITS; ++r) { - if (!DET(sud->cells[r][c])) + if (!DET(sud->cells[IDX(r, c)])) return INCOMPLETE; - ++digitcounts[VAL(sud->cells[r][c])]; + ++digitcounts[VAL(sud->cells[IDX(r, c)])]; } if (!checkcounts(digitcounts)) return INCORRECT; @@ -164,9 +152,9 @@ enum check_res check(const struct sudoku *sud) for (j = 0; j < NDIGITS; ++j) { r = 3 * (i / 3) + j / 3; c = 3 * (i % 3) + j % 3; - if (!DET(sud->cells[r][c])) + if (!DET(sud->cells[IDX(r, c)])) return INCOMPLETE; - ++digitcounts[VAL(sud->cells[r][c])]; + ++digitcounts[VAL(sud->cells[IDX(r, c)])]; } if (!checkcounts(digitcounts)) return INCORRECT; @@ -180,7 +168,7 @@ bool filled(const struct sudoku *sud) { for (unsigned r = 0; r < NDIGITS; ++r) { for (unsigned c = 0; c < NDIGITS; ++c) { - if (!(DET(sud->cells[r][c]))) + if (!(DET(sud->cells[IDX(r, c)]))) return false; } } diff --git a/sud.h b/sud.h index 3ced19d..d11f411 100644 --- a/sud.h +++ b/sud.h @@ -34,8 +34,10 @@ #define DET(x) (x & DETMASK) #define VAL(x) ((x & VALMASK) >> VALSHIFT) +#define IDX(r, c) (NDIGITS * (r) + (c)) + struct sudoku { - uint16_t cells[NDIGITS][NDIGITS]; + uint16_t cells[NCELLS]; }; enum update_res { NOT_ALLOWED, ALREADY_DET, OK }; @@ -54,13 +56,10 @@ bool load(struct sudoku *sud, const char *ptr); void save(struct sudoku *sud, char *ptr); /** - * Attempt to update the cell at row `r`, column `c` to have the value - * `val`. Returns `OK` on success, `ALREADY_DET` if the cell is - * already determined or `NOT_ALLOWED` if the cell being `val` would - * violate the sudoku rules. + * Attempt to update the cell at index `i` to have the value + * `val`. Returns `OK`. */ -enum update_res -update(struct sudoku *sud, unsigned r, unsigned c, unsigned val); +enum update_res update(struct sudoku *sud, unsigned i, unsigned val); /** * Print a string representation of the sudoku to stdout.