mirror of
https://github.com/sigmasternchen/sudoku-solver
synced 2025-03-15 07:48:56 +00:00
rewrite in go; added multi-threading support; added support for variable grid sizes
This commit is contained in:
parent
c3a9124036
commit
3b453ec26e
2 changed files with 454 additions and 379 deletions
379
main.c
379
main.c
|
@ -1,379 +0,0 @@
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define MAX_LIST_LENGTH 9
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int length;
|
|
||||||
int list[MAX_LIST_LENGTH];
|
|
||||||
} list_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
list_t field[9][9];
|
|
||||||
} field_t;
|
|
||||||
|
|
||||||
void setAll(list_t* list) {
|
|
||||||
list->length = MAX_LIST_LENGTH;
|
|
||||||
for(int i = 0; i < MAX_LIST_LENGTH; i++) {
|
|
||||||
list->list[i] = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void output(field_t field) {
|
|
||||||
for (int y = 0; y < 9; y++) {
|
|
||||||
for (int x = 0; x < 9; x++) {
|
|
||||||
if (field.field[x][y].length == 0) {
|
|
||||||
putchar('#');
|
|
||||||
} else if (field.field[x][y].length == 1) {
|
|
||||||
putchar(field.field[x][y].list[0] + '0');
|
|
||||||
} else {
|
|
||||||
putchar('?');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
putchar('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
field_t readField(const char* input) {
|
|
||||||
int x = 0, y = 0;
|
|
||||||
|
|
||||||
field_t field;
|
|
||||||
|
|
||||||
size_t length = strlen(input);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < length; i++) {
|
|
||||||
char c = input[i];
|
|
||||||
bool inc = false;
|
|
||||||
|
|
||||||
if (c == ' ') {
|
|
||||||
setAll(&(field.field[x][y]));
|
|
||||||
inc = true;
|
|
||||||
} else if (c >= '1' && c <= '9') {
|
|
||||||
field.field[x][y].length = 1;
|
|
||||||
field.field[x][y].list[0] = c - '0';
|
|
||||||
|
|
||||||
inc = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inc) {
|
|
||||||
x++;
|
|
||||||
if (x >= 9) {
|
|
||||||
x = 0;
|
|
||||||
y++;
|
|
||||||
}
|
|
||||||
if (y >= 9) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void removeFromList(list_t* list, int i) {
|
|
||||||
for (int li = i + 1; li < list->length; li++) {
|
|
||||||
list->list[li - 1] = list->list[li];
|
|
||||||
}
|
|
||||||
|
|
||||||
list->length--;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int eliminate(field_t *field) {
|
|
||||||
int eliminated = 0;
|
|
||||||
|
|
||||||
// columns
|
|
||||||
for (int x = 0; x < 9; x++) {
|
|
||||||
unsigned long bitfield = 0x1ff;
|
|
||||||
|
|
||||||
for (int y = 0; y < 9; y++) {
|
|
||||||
list_t* list = &(field->field[x][y]);
|
|
||||||
|
|
||||||
if (list->length == 1) {
|
|
||||||
bitfield &= ~(1 << (list->list[0] - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = 0; y < 9; y++) {
|
|
||||||
list_t* list = &(field->field[x][y]);
|
|
||||||
|
|
||||||
if (list->length == 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < list->length; i++) {
|
|
||||||
if (!(bitfield & (1 << (list->list[i] - 1)))) {
|
|
||||||
eliminated++;
|
|
||||||
removeFromList(list, i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rows
|
|
||||||
for (int y = 0; y < 9; y++) {
|
|
||||||
unsigned long bitfield = 0x1ff;
|
|
||||||
|
|
||||||
for (int x = 0; x < 9; x++) {
|
|
||||||
list_t* list = &(field->field[x][y]);
|
|
||||||
|
|
||||||
if (list->length == 1) {
|
|
||||||
bitfield &= ~(1 << (list->list[0] - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int x = 0; x < 9; x++) {
|
|
||||||
list_t* list = &(field->field[x][y]);
|
|
||||||
|
|
||||||
if (list->length == 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < list->length; i++) {
|
|
||||||
if (!(bitfield & (1 << (list->list[i] - 1)))) {
|
|
||||||
eliminated++;
|
|
||||||
removeFromList(list, i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int xs = 0; xs < 3; xs++) {
|
|
||||||
for (int ys = 0; ys < 3; ys++) {
|
|
||||||
unsigned long bitfield = 0x1ff;
|
|
||||||
|
|
||||||
for (int x = xs * 3; x < (xs + 1) * 3; x++) {
|
|
||||||
for (int y = ys * 3; y < (ys + 1) * 3; y++) {
|
|
||||||
list_t* list = &(field->field[x][y]);
|
|
||||||
|
|
||||||
if (list->length == 1) {
|
|
||||||
bitfield &= ~(1 << (list->list[0] - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int x = xs * 3; x < (xs + 1) * 3; x++) {
|
|
||||||
for (int y = ys * 3; y < (ys + 1) * 3; y++) {
|
|
||||||
list_t* list = &(field->field[x][y]);
|
|
||||||
|
|
||||||
if (list->length == 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < list->length; i++) {
|
|
||||||
if (!(bitfield & (1 << (list->list[i] - 1)))) {
|
|
||||||
eliminated++;
|
|
||||||
removeFromList(list, i);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return eliminated;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
solved, unsolved, error
|
|
||||||
} state_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
state_t state;
|
|
||||||
field_t field;
|
|
||||||
} solution_t;
|
|
||||||
|
|
||||||
static inline state_t getState(field_t field) {
|
|
||||||
for (int x = 0; x < 9; x++) {
|
|
||||||
for (int y = 0; y < 9; y++) {
|
|
||||||
int l = field.field[x][y].length;
|
|
||||||
switch(l) {
|
|
||||||
case 0:
|
|
||||||
return error;
|
|
||||||
case 1:
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
return unsolved;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return solved;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int length;
|
|
||||||
struct guess {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int n;
|
|
||||||
}* guesses;
|
|
||||||
} guesslist_t;
|
|
||||||
|
|
||||||
static inline guesslist_t possibleGuesses(field_t field) {
|
|
||||||
guesslist_t list;
|
|
||||||
list.guesses = NULL;
|
|
||||||
list.length = 0;
|
|
||||||
|
|
||||||
for (int x = 0; x < 9; x++) {
|
|
||||||
for (int y = 0; y < 9; y++) {
|
|
||||||
int l = field.field[x][y].length;
|
|
||||||
|
|
||||||
if (l > 1) {
|
|
||||||
void* tmp = realloc(list.guesses, (list.length + l) * sizeof(struct guess));
|
|
||||||
if (tmp == NULL) {
|
|
||||||
fprintf(stderr, "panic: malloc failed\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
list.guesses = tmp;
|
|
||||||
for (int i = 0; i < l; i++) {
|
|
||||||
list.guesses[list.length++] = (struct guess) {
|
|
||||||
x, y, field.field[x][y].list[i]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void freeGuesses(guesslist_t guesses) {
|
|
||||||
free(guesses.guesses);
|
|
||||||
}
|
|
||||||
|
|
||||||
solution_t solve(field_t field) {
|
|
||||||
int n;
|
|
||||||
|
|
||||||
while ((n = eliminate(&field))) {
|
|
||||||
printf("possibilities eliminated: %d\n", n);
|
|
||||||
}
|
|
||||||
|
|
||||||
state_t state = getState(field);
|
|
||||||
|
|
||||||
if (state == solved || state == error) {
|
|
||||||
return (solution_t) {
|
|
||||||
state, field
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
printf("no solution found using basic rules\n");
|
|
||||||
|
|
||||||
guesslist_t list = possibleGuesses(field);
|
|
||||||
|
|
||||||
for (int i = 0; i < list.length; i++) {
|
|
||||||
field_t field_ = field;
|
|
||||||
struct guess guess = list.guesses[i];
|
|
||||||
|
|
||||||
printf("guessing: %d,%d = %d\n", guess.x + 1, guess.y + 1, guess.n);
|
|
||||||
|
|
||||||
field_.field[guess.x][guess.y].length = 1;
|
|
||||||
field_.field[guess.x][guess.y].list[0] = guess.n;
|
|
||||||
|
|
||||||
solution_t solution = solve(field_);
|
|
||||||
|
|
||||||
if (solution.state == solved) {
|
|
||||||
freeGuesses(list);
|
|
||||||
|
|
||||||
return solution;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freeGuesses(list);
|
|
||||||
|
|
||||||
return (solution_t) {
|
|
||||||
error,
|
|
||||||
field
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* wikipedia = "\
|
|
||||||
53 7 \
|
|
||||||
6 195 \
|
|
||||||
98 6 \
|
|
||||||
8 6 3\
|
|
||||||
4 8 3 1\
|
|
||||||
7 2 6\
|
|
||||||
6 28 \
|
|
||||||
419 5\
|
|
||||||
8 79\
|
|
||||||
";
|
|
||||||
|
|
||||||
const char* medium = "\
|
|
||||||
47 53 \
|
|
||||||
9 8 \
|
|
||||||
1 2 5 \
|
|
||||||
1 7 5 4\
|
|
||||||
39 1 \
|
|
||||||
65 9 3\
|
|
||||||
95 172 6\
|
|
||||||
28 1\
|
|
||||||
7 64 9 \
|
|
||||||
";
|
|
||||||
|
|
||||||
const char* hard = "\
|
|
||||||
29 4 \
|
|
||||||
31 5 26\
|
|
||||||
96 \
|
|
||||||
2 83 \
|
|
||||||
1 98 5\
|
|
||||||
57 \
|
|
||||||
768 4\
|
|
||||||
6 2 9\
|
|
||||||
4 3\
|
|
||||||
";
|
|
||||||
|
|
||||||
const char* very_difficult = "\
|
|
||||||
21 6 4 \
|
|
||||||
5 9 \
|
|
||||||
4 2 1\
|
|
||||||
84 5 \
|
|
||||||
1 2\
|
|
||||||
4 75\
|
|
||||||
7 6 4\
|
|
||||||
3 9 \
|
|
||||||
8 3 61 \
|
|
||||||
";
|
|
||||||
|
|
||||||
const char* hardest = "\
|
|
||||||
8 \
|
|
||||||
36 \
|
|
||||||
7 9 2 \
|
|
||||||
5 7 \
|
|
||||||
457 \
|
|
||||||
1 3 \
|
|
||||||
1 68\
|
|
||||||
85 1 \
|
|
||||||
9 4 \
|
|
||||||
";
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
field_t field = readField(hardest);
|
|
||||||
output(field);
|
|
||||||
|
|
||||||
solution_t solution = solve(field);
|
|
||||||
|
|
||||||
printf("\n\n");
|
|
||||||
|
|
||||||
switch(solution.state) {
|
|
||||||
case error:
|
|
||||||
printf("error\n");
|
|
||||||
break;
|
|
||||||
case solved:
|
|
||||||
printf("solved\n");
|
|
||||||
break;
|
|
||||||
case unsolved:
|
|
||||||
printf("unsolved\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
output(solution.field);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
454
main.go
Normal file
454
main.go
Normal file
|
@ -0,0 +1,454 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*const size = 9
|
||||||
|
const blocks = 3
|
||||||
|
const blockSize = size / blocks*/
|
||||||
|
|
||||||
|
const size = 16
|
||||||
|
const blocks = 4
|
||||||
|
const blockSize = size / blocks
|
||||||
|
|
||||||
|
const maskZero = (1 << size) - 1
|
||||||
|
|
||||||
|
const guessChanSize = 1<<20
|
||||||
|
const eliminatedChanSize = 1<<15
|
||||||
|
|
||||||
|
type field [size][size][]int
|
||||||
|
|
||||||
|
var guesses = make(chan field, guessChanSize)
|
||||||
|
var solution = make(chan field, 1)
|
||||||
|
|
||||||
|
var eliminatedChan = make(chan int, eliminatedChanSize)
|
||||||
|
|
||||||
|
// 1 because of the first guess = starting point
|
||||||
|
var possibilities int64 = 1
|
||||||
|
var globalEliminated int64 = 0
|
||||||
|
|
||||||
|
func eliminate(field *field) *int {
|
||||||
|
eliminated := 0
|
||||||
|
|
||||||
|
// columns
|
||||||
|
for x := 0; x < size; x++ {
|
||||||
|
set := 0
|
||||||
|
bitfield := maskZero
|
||||||
|
|
||||||
|
for y := 0; y < size; y++ {
|
||||||
|
if len(field[x][y]) == 1 {
|
||||||
|
set++
|
||||||
|
mask := 1 << (field[x][y][0] - 1)
|
||||||
|
|
||||||
|
if bitfield & mask == 0 {
|
||||||
|
// duplicate number
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield &= mask ^ maskZero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 0; y < size; y++ {
|
||||||
|
if len(field[x][y]) != 1 {
|
||||||
|
length := len(field[x][y])
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
if (bitfield & (1 << (field[x][y][i] - 1))) == 0 {
|
||||||
|
eliminated++
|
||||||
|
field[x][y][i] = field[x][y][length - 1]
|
||||||
|
field[x][y] = field[x][y][:length - 1]
|
||||||
|
length--
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rows
|
||||||
|
for y := 0; y < size; y++ {
|
||||||
|
set := 0
|
||||||
|
bitfield := maskZero
|
||||||
|
|
||||||
|
for x := 0; x < size; x++ {
|
||||||
|
if len(field[x][y]) == 1 {
|
||||||
|
set++
|
||||||
|
mask := 1 << (field[x][y][0] - 1)
|
||||||
|
|
||||||
|
if bitfield & mask == 0 {
|
||||||
|
// duplicate number
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield &= mask ^ maskZero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := 0; x < size; x++ {
|
||||||
|
if len(field[x][y]) != 1 {
|
||||||
|
length := len(field[x][y])
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
if (bitfield & (1 << (field[x][y][i] - 1))) == 0 {
|
||||||
|
eliminated++
|
||||||
|
field[x][y][i] = field[x][y][length - 1]
|
||||||
|
field[x][y] = field[x][y][:length - 1]
|
||||||
|
length--
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// blocks
|
||||||
|
for sx := 0; sx < blocks; sx++ {
|
||||||
|
for sy := 0; sy < blocks; sy++ {
|
||||||
|
set := 0
|
||||||
|
bitfield := maskZero
|
||||||
|
|
||||||
|
for x := sx * blockSize; x < (sx + 1) * blockSize; x++ {
|
||||||
|
for y := sy * blockSize; y < (sy + 1) * blockSize; y++ {
|
||||||
|
if len(field[x][y]) == 1 {
|
||||||
|
set++
|
||||||
|
mask := 1 << (field[x][y][0] - 1)
|
||||||
|
|
||||||
|
if bitfield & mask == 0 {
|
||||||
|
// duplicate number
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield &= mask ^ maskZero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := sx * blockSize; x < (sx + 1) * blockSize; x++ {
|
||||||
|
for y := sy * blockSize; y < (sy + 1) * blockSize; y++ {
|
||||||
|
if len(field[x][y]) != 1 {
|
||||||
|
length := len(field[x][y])
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
if (bitfield & (1 << (field[x][y][i] - 1))) == 0 {
|
||||||
|
eliminated++
|
||||||
|
field[x][y][i] = field[x][y][length-1]
|
||||||
|
field[x][y] = field[x][y][:length-1]
|
||||||
|
length--
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &eliminated
|
||||||
|
}
|
||||||
|
|
||||||
|
type result int
|
||||||
|
const (
|
||||||
|
unsolved result = iota
|
||||||
|
solved
|
||||||
|
wrong
|
||||||
|
)
|
||||||
|
|
||||||
|
func check(field *field) result {
|
||||||
|
for _, column := range field {
|
||||||
|
for _, row := range column {
|
||||||
|
switch len(row) {
|
||||||
|
case 0:
|
||||||
|
return wrong
|
||||||
|
case 1:
|
||||||
|
// check next
|
||||||
|
default:
|
||||||
|
return unsolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return solved
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyField(fieldToCopy field) field {
|
||||||
|
var fieldCopy field
|
||||||
|
for x := 0; x < size; x++ {
|
||||||
|
for y := 0; y < size; y++ {
|
||||||
|
fieldCopy[x][y] = make([]int, len(fieldToCopy[x][y]))
|
||||||
|
copy(fieldCopy[x][y], fieldToCopy[x][y])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
func addGuesses(field field) {
|
||||||
|
for x := 0; x < size; x++ {
|
||||||
|
for y := 0; y < size; y++ {
|
||||||
|
if len(field[x][y]) > 1 {
|
||||||
|
for _, value := range field[x][y] {
|
||||||
|
newField := copyField(field)
|
||||||
|
newField[x][y] = []int{value}
|
||||||
|
|
||||||
|
atomic.AddInt64(&possibilities, 1)
|
||||||
|
guesses <- newField
|
||||||
|
}
|
||||||
|
// we don't need to add more
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removePossibility() {
|
||||||
|
if atomic.AddInt64(&possibilities, -1) <= 0 {
|
||||||
|
fmt.Println("no solution found")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func worker() {
|
||||||
|
workerLoop:
|
||||||
|
|
||||||
|
for {
|
||||||
|
field := <- guesses
|
||||||
|
eliminateLoop:
|
||||||
|
for {
|
||||||
|
eliminated := eliminate(&field)
|
||||||
|
if eliminated == nil {
|
||||||
|
removePossibility()
|
||||||
|
continue workerLoop
|
||||||
|
} else if *eliminated == 0 {
|
||||||
|
break eliminateLoop
|
||||||
|
} else {
|
||||||
|
eliminatedChan <- *eliminated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch check(&field) {
|
||||||
|
case wrong:
|
||||||
|
removePossibility()
|
||||||
|
continue workerLoop
|
||||||
|
case solved:
|
||||||
|
solution <- field
|
||||||
|
// we don't need the worker anymore
|
||||||
|
break workerLoop
|
||||||
|
case unsolved:
|
||||||
|
addGuesses(field)
|
||||||
|
removePossibility()
|
||||||
|
continue workerLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func all() []int {
|
||||||
|
var list []int
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
list = append(list, i + 1)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func readField(input string) field {
|
||||||
|
var field field
|
||||||
|
x := 0
|
||||||
|
y := 0
|
||||||
|
|
||||||
|
for _, c := range input {
|
||||||
|
inc := false
|
||||||
|
if c == ' ' || c == '-' {
|
||||||
|
field[x][y] = all()
|
||||||
|
inc = true
|
||||||
|
} else if c >= '0' && c <= '9' {
|
||||||
|
field[x][y] = []int{int(c - '0')}
|
||||||
|
inc = true
|
||||||
|
} else if c >= 'a' && c <= 'z' {
|
||||||
|
field[x][y] = []int{int(c - 'a' + 10)}
|
||||||
|
inc = true
|
||||||
|
} else {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if inc {
|
||||||
|
x++
|
||||||
|
if x >= size {
|
||||||
|
x = 0
|
||||||
|
y++
|
||||||
|
}
|
||||||
|
if y >= size {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for x = 0; x < size; x++ {
|
||||||
|
for y = 0; y < size; y++ {
|
||||||
|
if field[x][y] == nil {
|
||||||
|
log.Fatal("not all cells filled")
|
||||||
|
} else {
|
||||||
|
for _, value := range field[x][y] {
|
||||||
|
if value < 1 || value > size {
|
||||||
|
log.Fatal("cell range exceeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
func printField(field field) {
|
||||||
|
for y := 0; y < size; y++ {
|
||||||
|
for x := 0; x < size; x++ {
|
||||||
|
if len(field[x][y]) == 0 {
|
||||||
|
fmt.Print("#")
|
||||||
|
} else if len(field[x][y]) == 1 {
|
||||||
|
value := field[x][y][0]
|
||||||
|
if value < 10 {
|
||||||
|
fmt.Printf("%c", rune(value + '0'))
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%c", rune(value - 10 + 'a'))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Print(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Print("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func eliminiatedCounter() {
|
||||||
|
for {
|
||||||
|
eliminiated := <- eliminatedChan
|
||||||
|
globalEliminated += int64(eliminiated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*const wikipedia = `
|
||||||
|
53--7----
|
||||||
|
6--195---
|
||||||
|
-98----6-
|
||||||
|
8---6---3
|
||||||
|
4--8-3--1
|
||||||
|
7---2---6
|
||||||
|
-6----28-
|
||||||
|
---419--5
|
||||||
|
----8--79
|
||||||
|
`
|
||||||
|
|
||||||
|
const medium = `
|
||||||
|
47-53----
|
||||||
|
-9-----8-
|
||||||
|
-1--2--5-
|
||||||
|
1--7--5-4
|
||||||
|
--39--1--
|
||||||
|
---65-9-3
|
||||||
|
95--172-6
|
||||||
|
28------1
|
||||||
|
7---64-9-
|
||||||
|
`
|
||||||
|
|
||||||
|
const hard = `
|
||||||
|
---29--4-
|
||||||
|
--31-5-26
|
||||||
|
--96-----
|
||||||
|
2----83--
|
||||||
|
1----98-5
|
||||||
|
-57------
|
||||||
|
768-----4
|
||||||
|
----6-2-9
|
||||||
|
----4---3
|
||||||
|
`
|
||||||
|
|
||||||
|
const veryDifficult = `
|
||||||
|
-21-6-4--
|
||||||
|
---5---9-
|
||||||
|
4----2--1
|
||||||
|
84--5----
|
||||||
|
1-------2
|
||||||
|
----4--75
|
||||||
|
7--6----4
|
||||||
|
-3---9---
|
||||||
|
--8-3-61-
|
||||||
|
`
|
||||||
|
|
||||||
|
const hardest = `
|
||||||
|
8--------
|
||||||
|
--36-----
|
||||||
|
-7--9-2--
|
||||||
|
-5---7---
|
||||||
|
----457--
|
||||||
|
---1---3-
|
||||||
|
--1----68
|
||||||
|
--85---1-
|
||||||
|
-9----4--
|
||||||
|
`*/
|
||||||
|
|
||||||
|
const test16x16 = `
|
||||||
|
c3-8---g7f---4-e
|
||||||
|
---ac-3-----g2-7
|
||||||
|
-b-d--8-5-9---f-
|
||||||
|
-9-4--2---gdb-53
|
||||||
|
--f-----27b---4-
|
||||||
|
4--------5-g9-7c
|
||||||
|
---e3----c--85--
|
||||||
|
-c951--e8-a4f---
|
||||||
|
---g48-56--a1ed-
|
||||||
|
--d9--b----c5---
|
||||||
|
5a-6f-e--------b
|
||||||
|
-e---7d2-----8--
|
||||||
|
a2-f85---g--4-e-
|
||||||
|
-4---a-3-2--6-1-
|
||||||
|
g-5c-----4-e2---
|
||||||
|
6-e---gfa---3-c5
|
||||||
|
`
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
field := readField(test16x16)
|
||||||
|
printField(field)
|
||||||
|
guesses <- field
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
threads := runtime.GOMAXPROCS(0)
|
||||||
|
fmt.Printf("number of threads: %d\n", threads)
|
||||||
|
for x := 0; x < threads; x++ {
|
||||||
|
go worker()
|
||||||
|
}
|
||||||
|
|
||||||
|
go eliminiatedCounter()
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Printf("queued: %10d, remaining: %10d, eliminated: %d (+ %d) \r", len(guesses), possibilities, globalEliminated, len(eliminatedChan))
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
|
if len(solution) > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
solution := <- solution
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("solved")
|
||||||
|
|
||||||
|
printField(solution)
|
||||||
|
}
|
Loading…
Reference in a new issue