Dump my code into a repo!
This commit is contained in:
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
|
Reference in New Issue
Block a user