From 512580ab2773603621652a7a0e769a416fd0fef7 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Mon, 21 Nov 2022 13:01:39 +0000 Subject: [PATCH] Implement basic generation and printing I expect this will probably generate some impossible puzzles, but I'll deal with that later. --- Makefile | 5 ++- main.c | 14 +++++++ sud.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ sud.h | 61 +++++++++++++++++++++++++++ 4 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 sud.c create mode 100644 sud.h diff --git a/Makefile b/Makefile index ed7d98a..ecb8727 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ CFLAGS += -std=c11 -pedantic -Wall -Wextra CFLAGS += -O2 -flto -SRC = main.c +SRC = main.c sud.c OBJ = $(SRC:.c=.o) sudoku: $(OBJ) @@ -26,4 +26,7 @@ sudoku: $(OBJ) clean: rm -f sudoku $(OBJ) +main.o: sud.h +sud.o: sud.h + .PHONY: clean diff --git a/main.c b/main.c index 7ec51fa..8987058 100644 --- a/main.c +++ b/main.c @@ -16,7 +16,21 @@ * . */ +#include "sud.h" + +#include +#include +#include + int main(void) { + unsigned seed = time(NULL); + printf("Seed: %u\n\n", seed); + srand(seed); + + struct sudoku sud; + gen(&sud); + print(&sud); + return 0; } diff --git a/sud.c b/sud.c new file mode 100644 index 0000000..2723a2d --- /dev/null +++ b/sud.c @@ -0,0 +1,125 @@ +/* + * 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 +#include +#include + +#define FILLP_RCP 3 + +static void initposs(struct sudoku *sud) +{ + for (unsigned r = 0; r < SIDELEN; ++r) { + for (unsigned c = 0; c < SIDELEN; ++c) { + sud->cells[r][c].det = false; + for (unsigned i = 0; i < NDIGITS; ++i) + sud->cells[r][c].pvals[i] = true; + } + } +} + +void gen(struct sudoku *sud) +{ + initposs(sud); + + for (unsigned r = 0; r < SIDELEN; ++r) { + for (unsigned c = 0; c < SIDELEN; ++c) { + if (rand() % FILLP_RCP == 0) { + assert(!sud->cells[r][c].det); + + unsigned val, n = 0; + do { + val = (unsigned)(rand() % NDIGITS); + ++n; + } while (update(sud, r, c, val) == NOT_ALLOWED); + } + } + } +} + +enum update_res update(struct sudoku *sud, unsigned r, unsigned c, unsigned val) +{ + unsigned tr, tc; + assert(val < NDIGITS); + + /* Check that the cell is undetermined and the value is allowed. */ + if (sud->cells[r][c].det) + return ALREADY_DET; + if (!sud->cells[r][c].pvals[val]) + return NOT_ALLOWED; + + /* Update possible values of cells in same column. */ + for (tr = 0; tr < SIDELEN; ++tr) { + if (tr == r || sud->cells[tr][c].det) + continue; + sud->cells[tr][c].pvals[val] = false; + } + + /* Update possible values of cells in same row. */ + for (tc = 0; tc < SIDELEN; ++tc) { + if (tc == c || sud->cells[r][tc].det) + continue; + sud->cells[r][tc].pvals[val] = false; + } + + /* 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) { + if ((tr == r && tc == c) || sud->cells[tr][tc].det) + continue; + sud->cells[tr][tc].pvals[val] = false; + } + } + + /* Set the cell's value. */ + sud->cells[r][c].det = true; + sud->cells[r][c].val = val; + + return OK; +} + +void print(const struct sudoku *sud) +{ + for (unsigned r = 0; r < SIDELEN; ++r) { + /* + * Print horizontal divider if on a segment boundary (but not + * at the start). + */ + if (r != 0 && r % SEGLEN == 0) + puts("------+-------+------"); + + for (unsigned c = 0; c < SIDELEN; ++c) { + /* + * Print vertical divider if on a segment boundary (but + * not at the start). + */ + if (c != 0 && c % SEGLEN == 0) + fputs("| ", stdout); + + if (sud->cells[r][c].det) + printf("%u ", sud->cells[r][c].val + 1); + else + fputs(" ", stdout); + } + putchar('\n'); + } +} diff --git a/sud.h b/sud.h new file mode 100644 index 0000000..ec976e1 --- /dev/null +++ b/sud.h @@ -0,0 +1,61 @@ +/* + * 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 SUD_H +#define SUD_H + +#include + +#define SIDELEN 9 +#define SEGLEN 3 +#define NDIGITS 9 + +struct cellstate { + bool det; + union { + unsigned val; + bool pvals[NDIGITS]; + }; +}; + +struct sudoku { + struct cellstate cells[SIDELEN][SIDELEN]; +}; + +enum update_res { NOT_ALLOWED, ALREADY_DET, OK }; + +/** + * Populate the sudoku with some random values. + */ +void gen(struct sudoku *sud); + +/** + * 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. + */ +enum update_res +update(struct sudoku *sud, unsigned r, unsigned c, unsigned val); + +/** + * Print a string representation of the sudoku to stdout. + */ +void print(const struct sudoku *sud); + +#endif