Dump my code into a repo!

This commit is contained in:
Pyrex 2024-05-25 16:08:47 -07:00
commit 7e84d09250
24 changed files with 2387 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
cmake-build-debug
cmake-build-release
bazel-bin
bazel-out
bazel-sudoku_bat
bazel-testlogs

7
MODULE.bazel Normal file
View File

@ -0,0 +1,7 @@
"""
sudoku_bat: A pretty ordinary sudoku solver in C.
"""
module(
name="sudoku_bat",
version="0.0"
)

1613
MODULE.bazel.lock Normal file

File diff suppressed because it is too large Load Diff

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# Sudoku Bat!
A tiny sudoku solver in commented C.
Uses a pretty fast backtracking algorithm I came up with off the top of my head.
Have fun!
## Commands
### Run demo
```
> scripts\run_demo.bat
```

0
WORKSPACE Normal file
View File

3
scripts/run_demo.bat Normal file
View File

@ -0,0 +1,3 @@
@REM TODO: Port to bash too
@bazel build solver
@.\bazel-bin\solver\solver.exe scripts\sudoku_puzzles.txt

128
scripts/sudoku_puzzles.txt Normal file
View File

@ -0,0 +1,128 @@
.17369825632158947958724316825437169791586432346912758289643571573291684164875293
4.7369825632158947958724316825437169791586432346912758289643571573291684164875293
..7369825632158947958724316825437169791586432346912758289643571573291684164875293
.1736982563215894795872431682543716979158643234691275828964357157329168416487529.
81497653265912347873.854169948265317275341896163798245391682754587439621426517983
814976532659123478.3.854169948265317275341896163798245391682754587439621426517983
81497653265.123478.3.854169948265317275341896163798245391682754587439621426517983
814976532.5.123478.3.854169948265317275341896163798245391682754587439621426517983
81.976532.5.123478.3.854169948265317275341896163798245391682754587439621426517983
8..976532.5.123478.3.854169948265317275341896163798245391682754587439621426517983
...976532.5.123478.3.854169948265317275341896163798245391682754587439621426517983
..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3..
2...8.3...6..7..84.3.5..2.9...1.54.8.........4.27.6...3.1..7.4.72..4..6...4.1...3
......9.7...42.18....7.5.261..9.4....5.....4....5.7..992.1.8....34.59...5.7......
.3..5..4...8.1.5..46.....12.7.5.2.8....6.3....4.1.9.3.25.....98..1.2.6...8..6..2.
.2.81.74.7....31...9...28.5..9.4..874..2.8..316..3.2..3.27...6...56....8.76.51.9.
1..92....524.1...........7..5...81.2.........4.27...9..6...........3.945....71..6
.43.8.25.6.............1.949....4.7....6.8....1.2....382.5.............5.34.9.71.
48...69.2..2..8..19..37..6.84..1.2....37.41....1.6..49.2..85..77..9..6..6.92...18
...9....2.5.1234...3....16.9.8.......7.....9.......2.5.91....5...7439.2.4....7...
..19....39..7..16..3...5..7.5......9..43.26..2......7.6..1...3..42..7..65....68..
...1254....84.....42.8......3.....95.6.9.2.1.51.....6......3.49.....72....1298...
.6234.75.1....56..57.....4.....948..4.......6..583.....3.....91..64....7.59.8326.
3..........5..9...2..5.4....2....7..16.....587.431.6.....89.1......67.8......5437
63..........5....8..5674.......2......34.1.2.......345.....7..4.8.3..9.29471...8.
....2..4...8.35.......7.6.2.31.4697.2...........5.12.3.49...73........1.8....4...
361.259...8.96..1.4......57..8...471...6.3...259...8..74......5.2..18.6...547.329
.5.8.7.2.6...1..9.7.254...6.7..2.3.15.4...9.81.3.8..7.9...762.5.6..9...3.8.1.3.4.
.8...5........3457....7.8.9.6.4..9.3..7.1.5..4.8..7.2.9.1.2....8423........1...8.
..35.29......4....1.6...3.59..251..8.7.4.8.3.8..763..13.8...1.4....2......51.48..
...........98.51...519.742.29.4.1.65.........14.5.8.93.267.958...51.36...........
.2..3..9....9.7...9..2.8..5..48.65..6.7...2.8..31.29..8..6.5..7...3.9....3..2..5.
..5.....6.7...9.2....5..1.78.415.......8.3.......928.59.7..6....3.4...1.2.....6..
.4.....5...19436....9...3..6...5...21.3...5.68...2...7..5...2....24367...3.....4.
..4..........3...239.7...8.4....9..12.98.13.76..2....8.1...8.539...4..........8..
36..2..89...361............8.3...6.24..6.3..76.7...1.8............418...97..3..14
5..4...6...9...8..64..2.........1..82.8...5.17..5.........9..84..3...6...6...3..2
..72564..4.......5.1..3..6....5.8.....8.6.2.....1.7....3..7..9.2.......4..63127..
..........79.5.18.8.......7..73.68..45.7.8.96..35.27..7.......5.16.3.42..........
.3.....8...9...5....75.92..7..1.5..8.2..9..3.9..4.2..1..42.71....2...8...7.....9.
2..17.6.3.5....1.......6.79....4.7.....8.1.....9.5....31.4.......5....6.9.6.37..2
.......8.8..7.1.4..4..2..3.374...9......3......5...321.1..6..5..5.8.2..6.8.......
.......85...21...996..8.1..5..8...16.........89...6..7..9.7..523...54...48.......
6.8.7.5.2.5.6.8.7...2...3..5...9...6.4.3.2.5.8...5...3..5...2...1.7.4.9.4.9.6.7.1
.5..1..4.1.7...6.2...9.5...2.8.3.5.1.4..7..2.9.1.8.4.6...4.1...3.4...7.9.2..6..1.
.53...79...97534..1.......2.9..8..1....9.7....8..3..7.5.......3..76412...61...94.
..6.8.3...49.7.25....4.5...6..317..4..7...8..1..826..9...7.2....75.4.19...3.9.6..
..5.8.7..7..2.4..532.....84.6.1.5.4...8...5...7.8.3.1.45.....916..5.8..7..3.1.6..
...9..8..128..64...7.8...6.8..43...75.......96...79..8.9...4.1...36..284..1..7...
....8....27.....54.95...81...98.64...2.4.3.6...69.51...17...62.46.....38....9....
...6.2...4...5...1.85.1.62..382.671...........194.735..26.4.53.9...2...7...8.9...
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
52...6.........7.13...........4..8..6......5...........418.........3..2...87.....
6.....8.3.4.7.................5.4.7.3..2.....1.6.......2.....5.....8.6......1....
48.3............71.2.......7.5....6....2..8.............1.76...3.....4......5....
....14....3....2...7..........9...3.6.1.............8.2.....1.4....5.6.....7.8...
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
6.2.5.........3.4..........43...8....1....2........7..5..27...........81...6.....
.524.........7.1..............8.2...3.....6...9.5.....1.6.3...........897........
6.2.5.........4.3..........43...8....1....2........7..5..27...........81...6.....
.923.........8.1...........1.7.4...........658.........6.5.2...4.....7.....9.....
6..3.2....5.....1..........7.26............543.........8.15........4.2........7..
.6.5.1.9.1...9..539....7....4.8...7.......5.8.817.5.3.....5.2............76..8...
..5...987.4..5...1..7......2...48....9.1.....6..2.....3..6..2.......9.7.......5..
3.6.7...........518.........1.4.5...7.....6.....2......2.....4.....8.3.....5.....
1.....3.8.7.4..............2.3.1...........958.........5.6...7.....8.2...4.......
6..3.2....4.....1..........7.26............543.........8.15........4.2........7..
....3..9....2....1.5.9..............1.2.8.4.6.8.5...2..75......4.1..6..3.....4.6.
45.....3....8.1....9...........5..9.2..7.....8.........1..4..........7.2...6..8..
.237....68...6.59.9.....7......4.97.3.7.96..2.........5..47.........2....8.......
..84...3....3.....9....157479...8........7..514.....2...9.6...2.5....4......9..56
.98.1....2......6.............3.2.5..84.........6.........4.8.93..5...........1..
..247..58..............1.4.....2...9528.9.4....9...1.........3.3....75..685..2...
4.....8.5.3..........7......2.....6.....5.4......1.......6.3.7.5..2.....1.9......
.2.3......63.....58.......15....9.3....7........1....8.879..26......6.7...6..7..4
1.....7.9.4...72..8.........7..1..6.3.......5.6..4..2.........8..53...7.7.2....46
4.....3.....8.2......7........1...8734.......6........5...6........1.4...82......
.......71.2.8........4.3...7...6..5....2..3..9........6...7.....8....4......5....
6..3.2....4.....8..........7.26............543.........8.15........8.2........7..
.47.8...1............6..7..6....357......5....1..6....28..4.....9.1...4.....2.69.
......8.17..2........5.6......7...5..1....3...8.......5......2..4..8....6...3....
38.6.......9.......2..3.51......5....3..1..6....4......17.5..8.......9.......7.32
...5...........5.697.....2...48.2...25.1...3..8..3.........4.7..13.5..9..2...31..
.2.......3.5.62..9.68...3...5..........64.8.2..47..9....3.....1.....6...17.43....
.8..4....3......1........2...5...4.69..1..8..2...........3.9....6....5.....2.....
..8.9.1...6.5...2......6....3.1.7.5.........9..4...3...5....2...7...3.8.2..7....4
4.....5.8.3..........7......2.....6.....5.8......1.......6.3.7.5..2.....1.8......
1.....3.8.6.4..............2.3.1...........958.........5.6...7.....8.2...4.......
1....6.8..64..........4...7....9.6...7.4..5..5...7.1...5....32.3....8...4........
249.6...3.3....2..8.......5.....6......2......1..4.82..9.5..7....4.....1.7...3...
...8....9.873...4.6..7.......85..97...........43..75.......3....3...145.4....2..1
...5.1....9....8...6.......4.1..........7..9........3.8.....1.5...2..4.....36....
......8.16..2........7.5......6...2..1....3...8.......2......7..3..8....5...4....
.476...5.8.3.....2.....9......8.5..6...1.....6.24......78...51...6....4..9...4..7
.....7.95.....1...86..2.....2..73..85......6...3..49..3.5...41724................
.4.5.....8...9..3..76.2.....146..........9..7.....36....1..4.5..6......3..71..2..
.834.........7..5...........4.1.8..........27...3.....2.6.5....5.....8........1..
..9.....3.....9...7.....5.6..65..4.....3......28......3..75.6..6...........12.3.8
.26.39......6....19.....7.......4..9.5....2....85.....3..2..9..4....762.........4
2.3.8....8..7...........1...6.5.7...4......3....1............82.5....6...1.......
6..3.2....1.....5..........7.26............843.........8.15........8.2........7..
1.....9...64..1.7..7..4.......3.....3.89..5....7....2.....6.7.9.....4.1....129.3.
.........9......84.623...5....6...453...1...6...9...7....1.....4.5..2....3.8....9
.2....5938..5..46.94..6...8..2.3.....6..8.73.7..2.........4.38..7....6..........5
9.4..5...25.6..1..31......8.7...9...4..26......147....7.......2...3..8.6.4.....9.
...52.....9...3..4......7...1.....4..8..453..6...1...87.2........8....32.4..8..1.
53..2.9...24.3..5...9..........1.827...7.........981.............64....91.2.5.43.
1....786...7..8.1.8..2....9........24...1......9..5...6.8..........5.9.......93.4
....5...11......7..6.....8......4.....9.1.3.....596.2..8..62..7..7......3.5.7.2..
.47.2....8....1....3....9.2.....5...6..81..5.....4.....7....3.4...9...1.4..27.8..
......94.....9...53....5.7..8.4..1..463...........7.8.8..7.....7......28.5.26....
.2......6....41.....78....1......7....37.....6..412....1..74..5..8.5..7......39..
1.....3.8.6.4..............2.3.1...........758.........7.5...6.....8.2...4.......
2....1.9..1..3.7..9..8...2.......85..6.4.........7...3.2.3...6....5.....1.9...2.5
..7..8.....6.2.3...3......9.1..5..6.....1.....7.9....2........4.83..4...26....51.
...36....85.......9.4..8........68.........17..9..45...1.5...6.4....9..2.....3...
34.6.......7.......2..8.57......5....7..1..2....4......36.2..1.......9.......7.82
......4.18..2........6.7......8...6..4....3...1.......6......2..5..1....7...3....
.4..5..67...1...4....2.....1..8..3........2...6...........4..5.3.....8..2........
.......4...2..4..1.7..5..9...3..7....4..6....6..1..8...2....1..85.9...6.....8...3
8..7....4.5....6............3.97...8....43..5....2.9....6......2...6...7.71..83.2
.8...4.5....7..3............1..85...6.....2......4....3.26............417........
1....7.9..3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..
..53.....8......2..7..1.5..4....53...1..7...6..32...8..6.5....9..4....3......97..
..........1.62..9...2..931...4..6.8...87.21...3.8..5...691..4...8..73.5..........
.21.7...63.9...5.......1...13...8....84...9....6...34......6.87.....36..7...8..9.
85...24..72......9..4.........1.7..23.5...9...4...........8..7..17..........36.4.
..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9

4
solver/BUILD Normal file
View File

@ -0,0 +1,4 @@
cc_binary(
name = "solver",
srcs = glob(["*.c", "*.h"])
)

31
solver/cellset.c Normal file
View File

@ -0,0 +1,31 @@
#include "cellset.h"
void cellset_init(cellset_t* row) {
row->count = 0;
}
void cellset_add(
cellset_t* row,
uint8_t other
) {
for (uint8_t i = 0; i < row->count; i++) {
if (row->cells[i] == other) {
return;
}
}
if (row->count >= N_CELLS) {
crash("too many interference items");
}
row->cells[row->count++] = other;
}
void cellset_add_exclude(
cellset_t* row,
uint8_t exclude,
uint8_t other
) {
if (other == exclude) { return; }
cellset_add(row, other);
}

39
solver/cellset.h Normal file
View File

@ -0,0 +1,39 @@
// A cellset_t is a list of cells. (without duplicates)
//
// This is a very thin wrapper over an array of cells.
//
// You're encouraged to use the underlying representation directly by accessing
// the cells field.
#ifndef CELLSET_H
#define CELLSET_H
#include "shared.h"
#include <stdint.h>
typedef struct {
uint8_t count;
uint8_t cells[N_CELLS];
} cellset_t;
// Create an empty cellset_t.
void cellset_init(cellset_t* cells);
// Add a cell to the cellset_t.
void cellset_add(
cellset_t* row,
uint8_t cell
);
// Add a cell to the cellset_t, unless the new value equals exclude, in which
// case nothing is done.
//
// (This operation is specifically useful in building the interference map.
// See interference.h.)
void cellset_add_exclude(
cellset_t* row,
uint8_t exclude,
uint8_t cell
);
#endif

36
solver/interference.c Normal file
View File

@ -0,0 +1,36 @@
#include "interference.h"
// Convert a two-dimensional coordinate into a one-dimensional cell index.
#define PACK(x, y) ((y * SZ) + x)
interference_t interference;
void interference_init() {
for (uint8_t y = 0; y < SZ; y++) {
for (uint8_t x = 0; x < SZ; x++) {
uint8_t cell = PACK(x, y);
cellset_t *row = &interference.rows[cell];
cellset_init(row);
// rows
for (uint8_t ox = 0; ox < SZ; ox++) {
cellset_add_exclude(row, cell, PACK(ox, y));
}
// columns
for (uint8_t oy = 0; oy < SZ; oy++) {
cellset_add_exclude(row, cell, PACK(x, oy));
}
// boxes
uint8_t box_x = (x / 3) * 3;
uint8_t box_y = (y / 3) * 3;
for (uint8_t oy = box_y; oy < box_y + 3; oy++) {
for (uint8_t ox = box_x; ox < box_x + 3; ox++) {
cellset_add_exclude(row, cell, PACK(ox, oy));
}
}
}
}
}

24
solver/interference.h Normal file
View File

@ -0,0 +1,24 @@
// The interference table is a list of cellsets.
//
// The interference set for cell X is the set of cells that can't have the same
// value as X.
//
// Again, you're encouraged to reach in and use this directly: it's just a
// jagged array of arrays!
#ifndef INTERFERENCE_H
#define INTERFERENCE_H
#include "cellset.h"
#include "shared.h"
typedef struct {
cellset_t rows[N_CELLS];
} interference_t;
extern interference_t interference;
// Initialize the global interference table.
void interference_init();
#endif

54
solver/main.c Normal file
View File

@ -0,0 +1,54 @@
#include <stdio.h>
#include "interference.h"
#include "puzzle.h"
#include "puzzle_io.h"
// run_puzzle solves a single puzzle and prints the output
void run_puzzle(char* puzzle_text);
int main(int argc, char* argv[]) {
char *filename;
if (argc < 2) {
filename = "sudoku_puzzles.txt";
} else {
filename = argv[1];
}
FILE *file = fopen(filename, "r");
if (file == NULL) {
perror("Failed to open file");
return -1;
}
char line[N_CELLS + 2];
// we expect a line of exactly N_CELLS+1 chars, where the last one is \n
// we replace the \n with a \0 for compatibility with puzzle_init_string
while (fgets(line, sizeof(line), file)) {
bool too_short = strlen(line) != N_CELLS + 1;
bool too_long = line[N_CELLS] != '\n';
if (too_short || too_long) {
crash("wrong number of characters in a puzzle");
}
line[N_CELLS] = '\0';
run_puzzle(line);
}
return 0;
}
void run_puzzle(char* puzzle_text) {
// this only needs to be done once
// but it's harmless to do it more than once
interference_init();
// load and display info about the puzzle before and after solving
puzzle_t puzzle;
puzzle_init_string(&puzzle, puzzle_text);
printf("Initial state:\n\n");
puzzle_display(&puzzle);
printf("\nSolving...\n\n");
puzzle_solve(&puzzle);
puzzle_display(&puzzle);
printf("\n");
}

193
solver/puzzle.c Normal file
View File

@ -0,0 +1,193 @@
#include <stdio.h>
#include "puzzle.h"
#include "puzzle_propagate_result.h"
#include "interference.h"
// Crash the program if the solution is bad
// (read: if any assertion is violated)
//
// assumes that the interference graph is correct
void puzzle_check_solution(puzzle_t* puzzle);
// Solve the puzzle, returning false if a backtrack is needed.
//
// (unlike puzzle_solve, which crashes the program)
bool puzzle_solve1(puzzle_t* puzzle);
// Propagate the constraints corresponding to cell=tile.
//
// Anything connected to cell on the interference graph won't be set to tile.
void puzzle_propagate_constraints(
puzzle_t* puzzle,
uint8_t cell,
tile_t tile,
puzzle_propagate_result_t* result
);
// Undo propagating the constraints corresponding to cell=tile.
void puzzle_undo_propagate_constraints(
puzzle_t* puzzle,
tile_t tile,
puzzle_propagate_result_t* result
);
// Return true if cell is bound to something other than '.'.
bool puzzle_is_complete(puzzle_t* puzzle, uint8_t cell);
// Bind cell to some value, including '.'
void puzzle_set_cell(puzzle_t* puzzle, uint8_t cell, tile_t tile);
void puzzle_init(puzzle_t* puzzle, const tile_t board[N_CELLS]) {
// save the original board
for (uint8_t cell = 0; cell < N_CELLS; cell++) {
puzzle->input_board[cell] = board[cell];
}
// create a blank board
for (uint8_t cell = 0; cell < N_CELLS; cell++) {
puzzle->solved_board[cell] = tile_new('.');
puzzle_options_init(&puzzle->options[cell]);
}
// add every item from the input board to the new board
for (uint8_t cell = 0; cell < N_CELLS; cell++) {
tile_t tile = puzzle->input_board[cell];
if (tile.value != '.') {
puzzle_propagate_result_t propagate_result;
puzzle_propagate_constraints(puzzle, cell, tile, &propagate_result);
if (!propagate_result.viable) {
crash("puzzle contains a contradiction");
}
puzzle_set_cell(puzzle, cell, tile);
}
}
}
void puzzle_solve(puzzle_t* puzzle) {
bool success = puzzle_solve1(puzzle);
if (!success) {
crash("couldn't solve! horrible.");
}
puzzle_check_solution(puzzle);
}
bool puzzle_solve1(puzzle_t* puzzle) {
// find best cell to start from
uint8_t best_cell_noptions = 255;
uint8_t best_cell = 255;
for (uint8_t cell = 0; cell < N_CELLS; cell++) {
if (puzzle_is_complete(puzzle, cell)) {
continue;
}
puzzle_options_t *options = &puzzle->options[cell];
uint8_t cell_noptions = puzzle_options_count(options);
if (cell_noptions < best_cell_noptions) {
best_cell_noptions = cell_noptions;
best_cell = cell;
}
}
if (best_cell == 255) {
return true; // solved!
}
{
uint8_t cell = best_cell;
puzzle_options_t *options = &puzzle->options[cell];
for (char digit = '1'; digit <= (char) '9'; digit++) {
tile_t tile = tile_new(digit);
if (!puzzle_options_contains(options, tile)) {
continue;
}
puzzle_propagate_result_t result;
puzzle_propagate_constraints(puzzle, cell, tile, &result);
if (result.viable) {
puzzle_set_cell(puzzle, cell, tile);
if (puzzle_solve1(puzzle)) {
return true;
}
}
puzzle_undo_propagate_constraints(puzzle, tile, &result);
}
puzzle_set_cell(puzzle, cell, tile_new('.'));
}
return false;
}
void puzzle_check_solution(puzzle_t* puzzle) {
for (uint8_t cell = 0; cell < N_CELLS; cell++) {
tile_t original = puzzle->input_board[cell];
tile_t solved = puzzle->solved_board[cell];
if (solved.value == '.') {
crash("invalid solution (empty cell)");
}
if (original.value != '.' && original.value != solved.value) {
crash("invalid solution (changed original cell)");
}
cellset_t *interference_row = &interference.rows[cell];
for (uint8_t other_i = 0; other_i < interference_row->count; other_i++) {
uint8_t other = interference_row->cells[other_i];
if (puzzle->solved_board[other].value == solved.value) {
crash("invalid solution (sudoku constraints violated)");
}
}
}
}
void puzzle_propagate_constraints(
puzzle_t* puzzle,
uint8_t cell,
tile_t tile,
puzzle_propagate_result_t* result
) {
cellset_init(&result->cells);
result->viable = true;
cellset_t *interference_row = &interference.rows[cell];
for (uint8_t other_i = 0; other_i < interference_row->count; other_i++) {
uint8_t other = interference_row->cells[other_i];
if (puzzle_is_complete(puzzle, other)) {
continue;
}
puzzle_options_t *options = &puzzle->options[other];
if (puzzle_options_remove(options, tile)) {
cellset_add(&result->cells, other);
if (puzzle_options_is_empty(options)) {
result->viable = false;
break;
}
}
}
}
void puzzle_undo_propagate_constraints(
puzzle_t* puzzle,
tile_t tile,
puzzle_propagate_result_t* result
) {
cellset_t *cells = &result->cells;
for (uint8_t cell_i = 0; cell_i < cells->count; cell_i++) {
uint8_t cell = cells->cells[cell_i];
puzzle_options_add(&puzzle->options[cell], tile);
}
}
bool puzzle_is_complete(puzzle_t* puzzle, uint8_t cell) {
return puzzle->solved_board[cell].value != '.';
}
void puzzle_set_cell(puzzle_t* puzzle, uint8_t cell, tile_t tile) {
puzzle->solved_board[cell] = tile;
}

31
solver/puzzle.h Normal file
View File

@ -0,0 +1,31 @@
// The puzzle represents a Sudoku board where some cells may still be empty.
//
// All solve failures and inconsistencies are handled by crashing the program
// -- if puzzle_solve completes, then solved_board contains a copy of
// input_board where every blank tile is now set to some digit value.
#ifndef PUZZLE_H
#define PUZZLE_H
#include <stdint.h>
#include "shared.h"
#include "puzzle_options.h"
typedef struct {
tile_t input_board[N_CELLS];
tile_t solved_board[N_CELLS];
puzzle_options_t options[N_CELLS];
} puzzle_t;
// Create a puzzle whose input board is as given.
//
// You probably want puzzle_init_string from puzzle_io.h instead..
void puzzle_init(puzzle_t* puzzle, const tile_t board[N_CELLS]);
// Solve the puzzle.
//
// If this returns, the puzzle is solved. (Otherwise, we crash the process.)
void puzzle_solve(puzzle_t* puzzle);
#endif

25
solver/puzzle_io.c Normal file
View File

@ -0,0 +1,25 @@
#include <stdio.h>
#include <string.h>
#include "puzzle_io.h"
void puzzle_init_string(puzzle_t* puzzle, const char* text) {
if (strlen(text) != N_CELLS) {
crash("puzzles should be N_CELLS characters long");
}
tile_t tiles[N_CELLS];
for (uint8_t cell = 0; cell < N_CELLS; cell++) {
tiles[cell] = tile_new(text[cell]);
}
puzzle_init(puzzle, tiles);
}
void puzzle_display(puzzle_t* puzzle) {
for (uint8_t y = 0; y < SZ; y++) {
for (uint8_t x = 0; x < SZ; x++) {
putchar(puzzle->solved_board[y * SZ + x].value);
}
putchar('\n');
}
}

19
solver/puzzle_io.h Normal file
View File

@ -0,0 +1,19 @@
// It's possible to convert the puzzle to a terse string, and to display the
// puzzle with newlines between its rows for the person viewing.
//
// This functionality depends on stdio.h, which is gross, so I broke it out.
#ifndef PUZZLE_IO_H
#define PUZZLE_IO_H
#include "puzzle.h"
// Initialize a puzzle from an 81-character string containing its contents.
//
// (The characters are read from left to right, top to bottom.)
void puzzle_init_string(puzzle_t* puzzle, const char* text);
// Display a puzzle, writing it to stdout.
void puzzle_display(puzzle_t* puzzle);
#endif

47
solver/puzzle_options.c Normal file
View File

@ -0,0 +1,47 @@
#include "puzzle_options.h"
// this macro just does the validation that is always the same
#define TILE_TO_IX(bail) \
if (tile.value < '1' || tile.value > '9') { bail; } \
uint8_t ix = tile.value - '1'
// all of these are set operations for a bitfield with 9 bits!
// (done in the most obvious way possible)
void puzzle_options_init(puzzle_options_t* puzzle_options) {
puzzle_options->bitfield = 0x1FF; // 9 set bits
}
void puzzle_options_add(puzzle_options_t* puzzle_options, tile_t tile) {
TILE_TO_IX(return);
puzzle_options->bitfield |= 1 << ix;
}
bool puzzle_options_remove(puzzle_options_t* puzzle_options, tile_t tile) {
TILE_TO_IX(return false);
uint16_t old = puzzle_options->bitfield;
puzzle_options->bitfield &= ~(1 << ix);
uint16_t new = puzzle_options->bitfield;
return old != new;
}
bool puzzle_options_contains(puzzle_options_t* puzzle_options, tile_t tile) {
TILE_TO_IX(return false);
return (puzzle_options->bitfield & (1 << ix)) != 0;
}
uint8_t puzzle_options_count(puzzle_options_t* puzzle_options) {
uint8_t count = 0;
for (uint8_t ix = 0; ix < 9; ix++) {
if ((puzzle_options->bitfield & (1 << ix)) != 0) {
count++;
}
}
return count;
}
bool puzzle_options_is_empty(puzzle_options_t* puzzle_options) {
return puzzle_options->bitfield == 0;
}

40
solver/puzzle_options.h Normal file
View File

@ -0,0 +1,40 @@
// A puzzle_options_t is a set of possible tile values that can live somewhere
// in the puzzle.
//
// It has the interface of Set<tile_t>.
//
// Only values '1' through '9' are representable. Trying to add any other value
// to the set will silently fail.
#ifndef PUZZLE_OPTIONS_H
#define PUZZLE_OPTIONS_H
#include <stdint.h>
#include <stdbool.h>
#include "tile.h"
typedef struct {
uint16_t bitfield;
} puzzle_options_t;
// Create a blank puzzle options set.
void puzzle_options_init(puzzle_options_t* puzzle_options);
// Add a tile to a puzzle options set.
void puzzle_options_add(puzzle_options_t* puzzle_options, tile_t tile);
// Remove a tile from a puzzle options set.
bool puzzle_options_remove(puzzle_options_t* puzzle_options, tile_t tile);
// Return true if a puzzle options set contains this tile
bool puzzle_options_contains(puzzle_options_t* puzzle_options, tile_t tile);
// Count the tiles in this puzzle options set.
uint8_t puzzle_options_count(puzzle_options_t* puzzle_options);
// Check if a puzzle options set is empty.
//
// Equivalent to puzzle_options_count(...) == 0.
bool puzzle_options_is_empty(puzzle_options_t* puzzle_options);
#endif

View File

@ -0,0 +1,19 @@
// A constraint propagation operation generates a propagation result.
//
// Such a result is a list of cells that were touched and a "viable" flag.
//
// If "viable" is false, then at least one cell became impossible to fill
// during the propagation operation. (That is, the propagate operation found
// a contradiction.)
#ifndef PUZZLE_PROPAGATE_RESULT_H
#define PUZZLE_PROPAGATE_RESULT_H
#include <stdbool.h>
#include "cellset.h"
typedef struct {
bool viable;
cellset_t cells;
} puzzle_propagate_result_t;
#endif

8
solver/shared.c Normal file
View File

@ -0,0 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
#include "shared.h"
void crash(const char* msg) {
fprintf(stderr, "%s\n", msg);
exit(-1);
}

12
solver/shared.h Normal file
View File

@ -0,0 +1,12 @@
// These shared values apply to the whole program.
#ifndef SHARED_H
#define SHARED_H
#define SZ 9
#define N_CELLS (SZ * SZ)
// Unceremoniously crash the process. RIP!
void crash(const char* msg);
#endif

16
solver/tile.c Normal file
View File

@ -0,0 +1,16 @@
#include <stdio.h>
#include "tile.h"
#include "shared.h"
tile_t tile_new(char c) {
tile_t tile;
tile.value = 0;
if (c == '.' || (c >= '1' && c <= '9')) {
tile.value = c;
return tile;
}
printf("tile value: %d", c);
crash("invalid tile value");
return tile;
}

18
solver/tile.h Normal file
View File

@ -0,0 +1,18 @@
// A tile_t is a char in "123456789." (including the dot)
//
// tile_new checks this.
//
// The digits represent the values of filled cells, while the dot represents an
// empty cell.
#ifndef TILE_H
#define TILE_H
typedef struct { char value; } tile_t;
// Create a tile from a character, validating that the character is in
// "123456789."
tile_t tile_new(char c);
#endif