From 4feaedf1a3e821716121405ebbf87a71bdda61a5 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Mon, 21 Nov 2022 19:23:57 +0000 Subject: [PATCH] Create basic, rule-based solver --- Makefile | 5 ++- main.c | 11 +++++ solve.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ solve.h | 30 +++++++++++++ 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 solve.c create mode 100644 solve.h diff --git a/Makefile b/Makefile index ecb8727..b004aaf 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ CFLAGS += -std=c11 -pedantic -Wall -Wextra CFLAGS += -O2 -flto -SRC = main.c sud.c +SRC = main.c sud.c solve.c OBJ = $(SRC:.c=.o) sudoku: $(OBJ) @@ -26,7 +26,8 @@ sudoku: $(OBJ) clean: rm -f sudoku $(OBJ) -main.o: sud.h +main.o: sud.h solve.h sud.o: sud.h +solve.o: solve.h .PHONY: clean diff --git a/main.c b/main.c index 143f2bb..8816d2c 100644 --- a/main.c +++ b/main.c @@ -16,6 +16,7 @@ * . */ +#include "solve.h" #include "sud.h" #include @@ -45,7 +46,17 @@ int main(void) struct sudoku sud; gen(&sud); + puts("Start:"); print(&sud); + putchar('\n'); + + bool res = solve(&sud); + if (!res) { + puts("Solver encountered an error\n"); + } else { + puts("End:"); + print(&sud); + } return 0; } diff --git a/solve.c b/solve.c new file mode 100644 index 0000000..ed9fb61 --- /dev/null +++ b/solve.c @@ -0,0 +1,129 @@ +/* + * 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 "solve.h" + +#include + +struct match { + unsigned row; + unsigned col; + unsigned val; +}; + +struct cellref { + unsigned row, col; + struct cellstate *state; +}; + +enum apply_res { ERROR, NO_MATCH, MATCH }; + +typedef struct cellref cellgroup[NDIGITS]; +typedef bool (*rulefn)(cellgroup *group, struct match *match_out); + +static bool one_place_for_digit(cellgroup *group, struct match *match_out) +{ + for (unsigned val = 0; val < NDIGITS; ++val) { + unsigned n = 0, row = NDIGITS, col = NDIGITS; + for (unsigned i = 0; i < NDIGITS; ++i) { + if (!(*group)[i].state->det && (*group)[i].state->pvals[val]) { + row = (*group)[i].row; + col = (*group)[i].col; + ++n; + if (n > 1) + break; + } + } + + if (n == 1) { + match_out->row = row; + match_out->col = col; + match_out->val = val; + return true; + } + } + + return false; +} + +static const rulefn rules[] = { + &one_place_for_digit, +}; +static const unsigned nrules = sizeof(rules) / sizeof(rules[0]); + +static enum apply_res apply_rules(struct sudoku *sud, cellgroup *group) +{ + struct match m; + bool matched = false; + for (unsigned i = 0; i < nrules; ++i) { + if (!rules[i](group, &m)) + continue; + if (!update(sud, m.row, m.col, m.val)) + return ERROR; + matched = true; + } + return matched ? MATCH : NO_MATCH; +} + +bool solve(struct sudoku *sud) +{ + cellgroup group = { 0 }; + unsigned r, c; + bool match; + enum apply_res res; + + for (unsigned i = 0;; ++i) { + match = false; + + /* Apply rules to each row. */ + for (r = 0; r < NDIGITS; ++r) { + for (c = 0; c < NDIGITS; ++c) { + group[c].row = r; + group[c].col = c; + group[c].state = &sud->cells[r][c]; + } + + res = apply_rules(sud, &group); + if (res == ERROR) + return false; + else if (res == MATCH) + match = true; + } + + /* Apply rules to each column. */ + for (c = 0; c < NDIGITS; ++c) { + for (r = 0; r < NDIGITS; ++r) { + group[r].row = r; + group[r].col = c; + group[r].state = &sud->cells[r][c]; + } + + res = apply_rules(sud, &group); + if (res == ERROR) + return false; + else if (res == MATCH) + match = true; + } + + /* TODO: Apply rules to each segment. */ + + /* Exit if no matches. */ + if (!match) + return true; + } +} diff --git a/solve.h b/solve.h new file mode 100644 index 0000000..1f65e7b --- /dev/null +++ b/solve.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 SOLVE_H +#define SOLVE_H + +#include "sud.h" + +/** + * Attempt to solve the given sudoku. Returns when no rules match for + * an entire pass over the puzzle. + */ +bool solve(struct sudoku *sud); + +#endif