From 3b453ec26e5fc56463200701e25948ec66ad15b5 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 3 Jan 2021 22:00:04 +0100 Subject: [PATCH] rewrite in go; added multi-threading support; added support for variable grid sizes --- main.c | 379 ---------------------------------------------- main.go | 454 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 454 insertions(+), 379 deletions(-) delete mode 100644 main.c create mode 100644 main.go diff --git a/main.c b/main.c deleted file mode 100644 index ef78894..0000000 --- a/main.c +++ /dev/null @@ -1,379 +0,0 @@ -#include -#include -#include -#include - -#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; -} diff --git a/main.go b/main.go new file mode 100644 index 0000000..282e484 --- /dev/null +++ b/main.go @@ -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) +} \ No newline at end of file