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.