Dump my code into a repo!
This commit is contained in:
commit
7e84d09250
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal 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
7
MODULE.bazel
Normal 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
1613
MODULE.bazel.lock
Normal file
File diff suppressed because it is too large
Load Diff
14
README.md
Normal file
14
README.md
Normal 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
|
||||
```
|
3
scripts/run_demo.bat
Normal file
3
scripts/run_demo.bat
Normal 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
128
scripts/sudoku_puzzles.txt
Normal 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
4
solver/BUILD
Normal file
@ -0,0 +1,4 @@
|
||||
cc_binary(
|
||||
name = "solver",
|
||||
srcs = glob(["*.c", "*.h"])
|
||||
)
|
31
solver/cellset.c
Normal file
31
solver/cellset.c
Normal 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
39
solver/cellset.h
Normal 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
36
solver/interference.c
Normal 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
24
solver/interference.h
Normal 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
54
solver/main.c
Normal 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
193
solver/puzzle.c
Normal 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
31
solver/puzzle.h
Normal 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
25
solver/puzzle_io.c
Normal 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
19
solver/puzzle_io.h
Normal 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
47
solver/puzzle_options.c
Normal 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
40
solver/puzzle_options.h
Normal 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
|
19
solver/puzzle_propagate_result.h
Normal file
19
solver/puzzle_propagate_result.h
Normal 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
8
solver/shared.c
Normal 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
12
solver/shared.h
Normal 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
16
solver/tile.c
Normal 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
18
solver/tile.h
Normal 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
|
Loading…
Reference in New Issue
Block a user