Initial commit

This commit is contained in:
2026-07-17 22:35:11 +02:00
parent 3121a7d818
commit 1ff0efb557
31 changed files with 895046 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
package board
// ────────────────────────────────────────────────────────────────────────────── //
// NOTES STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Notes struct {
numbers []int
}
func (n *Notes) Add(field *Field, cell *Cell, note int, trigger string, marks []Mark) {
for _, existing := range n.numbers {
if existing == note {
return // Note already exists
}
}
field.changes = append(field.changes, Change{
Cell: cell,
marks: marks,
action: ActionSetNote,
value: note,
from: 0,
triggerdBy: trigger,
})
n.numbers = append(n.numbers, note)
}
func (n *Notes) Remove(field *Field, cell *Cell, note int, trigger string, marks []Mark) {
for i, existing := range n.numbers {
if existing == note {
field.changes = append(field.changes, Change{
Cell: cell,
marks: marks,
action: ActionRemoveNote,
value: 0,
from: note,
triggerdBy: trigger,
})
n.numbers = append(n.numbers[:i], n.numbers[i+1:]...)
return
}
}
}
// ────────────────────────────────────────────────────────────────────────────── //
// CELL STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Cell struct {
number int
Notes *Notes
Pos *Position
}
func (c *Cell) SetNumber(field *Field, n int, trigger string, marks []Mark) {
if c.number == n {
return
}
if field == nil {
return
}
if n <= 0 || n > field.rows || n > field.columns {
return
}
field.changes = append(field.changes, Change{
Cell: c,
marks: marks,
action: ActionSetNumber,
value: n,
from: c.number,
triggerdBy: trigger,
})
c.number = n
}
func (c *Cell) RemoveNumber(field *Field, trigger string, marks []Mark) {
if c.number == 0 {
return
}
if field == nil {
return
}
field.changes = append(field.changes, Change{
action: ActionSetNumber,
Cell: c,
value: 0,
from: c.number,
marks: marks,
triggerdBy: trigger,
})
c.number = 0
}
+93
View File
@@ -0,0 +1,93 @@
package board
import "image/color"
// ────────────────────────────────────────────────────────────────────────────── //
// MARK STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Mark struct {
Cell *Cell
Change *Change
color color.Color
}
func (m *Mark) GetColor() color.Color {
return m.color
}
// ────────────────────────────────────────────────────────────────────────────── //
// CHANGE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type ChangeAction int
const (
ActionSetNumber = iota
ActionSetNote
ActionRemoveNumber
ActionRemoveNote
)
// ────────────────────────────────────────────────────────────────────────────── //
// CHANGE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Change struct {
Cell *Cell
marks []Mark
action ChangeAction
value int
from int
triggerdBy string //strategy or manuell
}
// ────────────────────────────────────────────────────────────────────────────── //
// GETTER //
// ────────────────────────────────────────────────────────────────────────────── //
func (c *Change) GetMarks() []Mark {
return c.marks
}
func (c *Change) GetAction() ChangeAction {
return c.action
}
func (c *Change) GetTo() int {
return c.value
}
func (c *Change) GetFrom() int {
return c.from
}
func (c *Change) GetTriggeredBy() string {
return c.triggerdBy
}
// ────────────────────────────────────────────────────────────────────────────── //
// ??? //
// ────────────────────────────────────────────────────────────────────────────── //
func (c *Change) do() {
switch c.action {
case ActionSetNumber:
c.Cell.number = c.value
case ActionSetNote:
c.Cell.Notes.numbers = append(c.Cell.Notes.numbers, c.value)
case ActionRemoveNumber:
c.Cell.number = 0
case ActionRemoveNote:
for i, existing := range c.Cell.Notes.numbers {
if existing == c.from {
c.Cell.Notes.numbers = append(c.Cell.Notes.numbers[:i], c.Cell.Notes.numbers[i+1:]...)
return
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
package board
import "errors"
var (
outOfBound = errors.New("invalid coordinates")
invalidField = errors.New("can not parse Field")
)
+161
View File
@@ -0,0 +1,161 @@
package board
import (
"fmt"
)
// ────────────────────────────────────────────────────────────────────────────── //
// FIELD STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Field struct {
rows int
columns int
blockRows int
blockColumns int
blockSizeRow int
blockSizeColumn int
cells [][]Cell
changes []Change
sha1 []byte
rating float64
}
// ────────────────────────────────────────────────────────────────────────────── //
// GETTER //
// ────────────────────────────────────────────────────────────────────────────── //
func (f *Field) GetRow(r int) (*Line, error) {
if r >= f.rows || r < 0 {
return nil, outOfBound
}
return &Line{cells: append([]Cell(nil), f.cells[r]...)}, nil
}
func (f *Field) GetColumn(c int) (*Line, error) {
if c >= f.columns || c < 0 {
return nil, outOfBound
}
// Performance
result := &Line{
cells: make([]Cell, 0, f.rows),
}
// Copying
for _, row := range f.cells {
result.cells = append(result.cells, row[c])
}
return result, nil
}
func (f *Field) GetBlock(r, c int) (*Block, error) {
if r < 0 || r >= f.blockRows || c < 0 || c >= f.blockColumns {
return nil, outOfBound
}
startRow := r * f.blockSizeRow
startCol := c * f.blockSizeColumn
block := &Block{
cells: make([][]Cell, f.blockSizeRow),
}
for row := range block.cells {
// Effizientes Kopieren der Zeile
block.cells[row] = make([]Cell, f.blockSizeColumn)
copy(block.cells[row], f.cells[startRow+row][startCol:startCol+f.blockSizeColumn])
}
return block, nil
}
func (f *Field) GetCell(r, c int) (*Cell, error) {
if r > f.rows || c > f.columns {
return nil, outOfBound
}
return &f.cells[r][c], nil
}
// ────────────────────────────────────────────────────────────────────────────── //
// FOREACH FUNCTIONS //
// ────────────────────────────────────────────────────────────────────────────── //
func (f *Field) ForEachPart(fn func(part Part)) {
f.ForEachRow(func(line *Line) {
fn(line)
})
f.ForEachColumn(func(line *Line) {
fn(line)
})
f.ForEachBlock(func(block *Block) {
fn(block)
})
}
func (f *Field) ForEachRow(fn func(line *Line)) {
for i := range f.rows {
line, err := f.GetRow(i)
if err != nil {
fmt.Println(err.Error())
return
}
fn(line)
}
}
func (f *Field) ForEachColumn(fn func(line *Line)) {
for i := range f.columns {
column, err := f.GetColumn(i)
if err != nil {
fmt.Println(err.Error())
return
}
fn(column)
}
}
func (f *Field) ForEachBlock(fn func(block *Block)) {
for r := range f.blockRows {
for c := range f.blockColumns {
block, err := f.GetBlock(r, c)
if err != nil {
fmt.Println(err.Error())
return
}
fn(block)
}
}
}
func (f *Field) ForEachCell(fn func(cell *Cell)) {
for _, row := range f.cells {
for _, cell := range row {
fn(&cell)
}
}
}
// ────────────────────────────────────────────────────────────────────────────── //
// MODIFIER //
// ────────────────────────────────────────────────────────────────────────────── //
func (f *Field) AddChange(cell *Cell, action ChangeAction, value, from int, trigger string, marks []Mark) {
change := Change{
Cell: cell,
marks: marks,
action: action,
value: value,
from: from,
triggerdBy: trigger,
}
change.do()
f.changes = append(f.changes, change)
}
+97
View File
@@ -0,0 +1,97 @@
package board
import (
"bytes"
"errors"
"fmt"
"strconv"
)
type Parser interface {
parse(data []byte) error
getField(i int) *Field
getAllFields() []Field
}
type parserHelper struct {
fields []Field
}
func (ph *parserHelper) getField(i int) *Field {
if i < 0 || i >= len(ph.fields) {
return &Field{}
}
return &ph.fields[i]
}
func (ph *parserHelper) getAllFields() []Field {
if len(ph.fields) == 0 {
return nil
}
return ph.fields
}
// https://github.com/grantm/sudoku-exchange-puzzle-bank/tree/master
type PuzzleBank struct {
parserHelper
}
func (pb *PuzzleBank) parse(data []byte) error {
lines := bytes.Split(data, []byte("\n"))
for _, line := range lines {
if len(line) == 0 { // Überspringe leere Zeile
continue
}
if len(line) != 99 {
return fmt.Errorf("Zeile hat die falsche länge. soll: 100, ist: %d", len(line))
}
//siehe Readme in github repo
sha1Hash := bytes.TrimSpace(line[0:12])
sudokuStr := string(line[13:94])
ratingStr := string(line[96:99])
//fmt.Println(sudokuStr)
var rating float64
_, err := fmt.Sscanf(ratingStr, "%f", &rating)
if err != nil {
return errors.New("can nor parse raiting")
}
field := Field{
rows: 9,
columns: 9,
blockRows: 3,
blockColumns: 3,
blockSizeRow: 3,
blockSizeColumn: 3,
rating: rating,
sha1: sha1Hash,
}
field.cells = make([][]Cell, field.rows)
for i := range field.cells {
field.cells[i] = make([]Cell, field.columns)
for j := range field.cells[i] {
number, err := strconv.Atoi(string(sudokuStr[i*9+j]))
if err != nil {
return fmt.Errorf("Kann Zeichen nicht in Zahl konvertieren")
}
field.cells[i][j].number = number
field.cells[i][j].Pos = &Position{
row: i,
column: j,
blockRow: i / field.blockRows,
blockColumn: j / field.blockColumns,
inBlockRow: i % field.blockSizeRow,
inBlockColumn: j % field.blockSizeColumn,
}
}
}
//fmt.Println(field.String())
pb.fields = append(pb.fields, field)
}
return nil
}
+91
View File
@@ -0,0 +1,91 @@
package board
type Part interface {
IsSolved() bool
ForEachCell(fn func(cell *Cell))
GetMissingNumbers() []int
RemoveNote(field *Field, note int, trigger string, marks []Mark)
}
// ────────────────────────────────────────────────────────────────────────────── //
// LINE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Line struct {
cells []Cell
}
func (l *Line) IsSolved() bool {
//TODO
return false
}
func (l *Line) ForEachCell(fn func(cell *Cell)) {
for _, cell := range l.cells {
fn(&cell)
}
}
func (l *Line) GetMissingNumbers() []int {
return getMissingNumbersHelper(len(l.cells), l.ForEachCell)
}
func (l *Line) RemoveNote(field *Field, note int, trigger string, marks []Mark) {
l.ForEachCell(func(cell *Cell) {
cell.Notes.Remove(field, cell, note, trigger, marks)
})
}
// ────────────────────────────────────────────────────────────────────────────── //
// BLOCK STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Block struct {
cells [][]Cell
}
func (b *Block) IsSolved() bool {
//TODO
return false
}
func (b *Block) ForEachCell(fn func(cell *Cell)) {
for _, row := range b.cells {
for _, cell := range row {
fn(&cell)
}
}
}
func (b *Block) GetMissingNumbers() []int {
max := len(b.cells) * len(b.cells[0])
return getMissingNumbersHelper(max, b.ForEachCell)
}
func (b *Block) RemoveNote(field *Field, note int, trigger string, marks []Mark) {
b.ForEachCell(func(cell *Cell) {
cell.Notes.Remove(field, cell, note, trigger, marks)
})
}
// ────────────────────────────────────────────────────────────────────────────── //
// HELPER //
// ────────────────────────────────────────────────────────────────────────────── //
func getMissingNumbersHelper(max int, iterate func(fn func(cell *Cell))) []int {
present := make(map[int]bool, max)
iterate(func(cell *Cell) {
if cell.number != 0 {
present[cell.number] = true
}
})
var missing []int
for i := 1; i <= max; i++ {
if !present[i] {
missing = append(missing, i)
}
}
return missing
}
+46
View File
@@ -0,0 +1,46 @@
package board
type Position struct {
row int
column int
blockRow int
blockColumn int
inBlockRow int
inBlockColumn int
}
func (p *Position) GetRow() int {
return p.row
}
func (p *Position) GetColumn() int {
return p.column
}
func (p *Position) GetBlockRow() int {
return p.blockRow
}
func (p *Position) GetBlockColumn() int {
return p.blockColumn
}
func (p *Position) GetInBlockRow() int {
return p.inBlockRow
}
func (p *Position) GetInBlockColumn() int {
return p.inBlockColumn
}
func (p *Position) GetCoords() (int, int) {
return p.row, p.column
}
func (p *Position) GetBlockCoords() (int, int) {
return p.blockRow, p.blockColumn
}
func (p *Position) GetInBlockCoords() (int, int) {
return p.inBlockRow, p.inBlockColumn
}
+67
View File
@@ -0,0 +1,67 @@
package board
import "strings"
func (f *Field) String() string {
//TODO: auf beliebige größen anpassen
var sb strings.Builder
// Oberer Rahmen
sb.WriteString("╔═══════╤═══════╤═══════╗\n")
for i := range f.cells {
sb.WriteString("║ ") // Linke Rahmenseite
for j, cell := range f.cells[i] {
// Wert ausgeben oder Punkt für 0
val := cell.number
if val == 0 {
sb.WriteString("·")
} else {
sb.WriteString(string('0' + val))
}
// Trennlinien zwischen Blöcken und Zellen
if (j+1)%3 == 0 {
if j < 8 {
sb.WriteString(" │ ")
} else {
sb.WriteString(" ║\n") // Rechte Rahmenseite + Zeilenumbruch
}
} else {
sb.WriteString(" ")
}
}
// Horizontale Trennlinien nach jeder 3. Zeile
if (i+1)%3 == 0 && i < 8 {
sb.WriteString("╟───────┼───────┼───────╢\n")
}
}
// Unterer Rahmen
sb.WriteString("╚═══════╧═══════╧═══════╝")
return sb.String()
}
func (f *Field) IsSolved() bool {
result := true
f.ForEachCell(func(cell *Cell) {
if cell.number == 0 {
result = false
}
})
/* TODO:
if !f.isValid() {
return false
}
*/
return result
}
func (f *Field) IsValid() bool {
return false
}
@@ -0,0 +1,23 @@
This is a free and unencumbered data set released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this data, either in source form or transformed, for any
purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
@@ -0,0 +1,37 @@
# Sudoku Exchange "Puzzle Bank"
This repository contains several hundred thousand Sudoku puzzles that were
computer generated for use by the [Sudoku Exchange](https://sudokuexchange.com/)
web site.
The puzzles were generated using the
[QQWing Sudoku](https://github.com/stephenostermiller/qqwing) software, which
ensure that each puzzle has a unique solution.
The generated puzzles were then graded using
[Sukaku Explainer](https://github.com/SudokuMonster/SukakuExplainer) and
sorted into four 'buckets':
| Filename | Difficulty Rating |
| ------------------- | ----------------- |
| [easy.txt][1] | < 1.5 |
| [medium.txt][2] | < 2.5 |
| [hard.txt][3] | < 5.0 |
| [diabolical.txt][4] | ≥ 5.0 |
Each text file has one puzzle per line, represented as three space-separated
fields and a Unix-style line-ending, for a total of 100 bytes per record:
12 bytes of SHA1 hash of the digits string (for randomising order)
81 bytes of puzzle digits
4 bytes of rating (nn.n)
3 bytes of white-space (including the linefeed);
100 bytes total
### License
The data set is dedicated to the [public domain](LICENSE.txt).
[1]: https://github.com/grantm/sudoku-exchange-puzzle-bank/raw/master/easy.txt
[2]: https://github.com/grantm/sudoku-exchange-puzzle-bank/raw/master/medium.txt
[3]: https://github.com/grantm/sudoku-exchange-puzzle-bank/raw/master/hard.txt
[4]: https://github.com/grantm/sudoku-exchange-puzzle-bank/raw/master/diabolical.txt
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
0000183b305c 451723860007000800000816000000030000005000100730040086906000204840572093000409000 1.2
0001d5d6314e 302401809001000300000000000040708010780502036000090000200609003900000008800070005 1.2
000212406270 000823001003000400070000052300960010000102000010038006830000040002000900600789000 1.2
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
module git.kleiax.de/homepage
go 1.25.10
+4
View File
@@ -0,0 +1,4 @@
package logic
type Change struct {
}
+1
View File
@@ -0,0 +1 @@
package logic
+43
View File
@@ -0,0 +1,43 @@
package logic
import "git.kleiax.de/homepage/libs/sudoku/board"
// ────────────────────────────────────────────────────────────────────────────── //
// SOLVER STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Solver struct {
strategies []Strategy
conf struct {
all bool
repeat bool
}
}
func (s *Solver) Add(strategy Strategy) {
s.strategies = append(s.strategies, strategy)
}
func (s *Solver) initStragies(field *board.Field) {
for _, strategy := range s.strategies {
strategy.Init(field)
}
}
func (s *Solver) run(i int) bool {
//TODO: clean und Fehlerauffangen
s.strategies[i].SearchProgressableCells()
numberOfChanges := s.strategies[i].ApplyAll()
if numberOfChanges == 0 {
return false
}
return true
}
func (s *Solver) search() {
}
func (s *Solver) getSolutionPath() []board.Change {
return nil
}
+15
View File
@@ -0,0 +1,15 @@
package strategies
type XChain struct {
Base
}
type XYChain struct {
Base
}
type XChainLoop struct {
Base
}
type XYChainLoop struct {
Base
}
+53
View File
@@ -0,0 +1,53 @@
package strategies
import "git.kleiax.de/homepage/libs/sudoku/board"
// ────────────────────────────────────────────────────────────────────────────── //
// LAST_DIGIT STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type LastDigit struct {
Base
}
func (ld *LastDigit) SearchProgressableCells() int {
ld.field.ForEachPart(func(part board.Part) {
missingNumbers := part.GetMissingNumbers()
if len(missingNumbers) == 1 {
// var emptyCell *Cell
// part.forEachCell(func(cell *Cell) {
// if cell.number == 0 {
// emptyCell = cell
// }
// })
// change := Change{
// cell: emptyCell,
// action: ActionSetNumber,
// value: missingNumbers[0],
// }
// ld.changes = append(ld.changes, change)
}
})
//fmt.Printf("LastDigit Changes %d", len(ld.changes))
return len(ld.changes)
}
func (ld *LastDigit) getName() string {
return "Last Digit - Letzte Zahl"
}
// ────────────────────────────────────────────────────────────────────────────── //
// CRP STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type CRP struct {
Base
}
// ────────────────────────────────────────────────────────────────────────────── //
// SIMPLE_COLORING_T1 STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type SimpleColoringT1 struct {
Base
}
+25
View File
@@ -0,0 +1,25 @@
package strategies
// ────────────────────────────────────────────────────────────────────────────── //
// HIDDEN_SINGLE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type HiddenSingle struct {
Base
}
// ────────────────────────────────────────────────────────────────────────────── //
// HIDDEN_PAIR STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type HiddenPair struct {
Base
}
// ────────────────────────────────────────────────────────────────────────────── //
// HIDDEN_TRIPLE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type HiddenTriple struct {
Base
}
+12
View File
@@ -0,0 +1,12 @@
package strategies
type LockedPair struct {
Base
}
type LockedCandidateT1 struct {
Base
}
type LockedCandidateT2 struct {
Base
}
+44
View File
@@ -0,0 +1,44 @@
package strategies
import "git.kleiax.de/homepage/libs/sudoku/board"
// ────────────────────────────────────────────────────────────────────────────── //
// NAKED_SINGLE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type NakedSingle struct {
Base
}
func (ns *NakedSingle) SearchProgressableCells() int {
ns.field.ForEachPart(func(part board.Part) {
part.ForEachCell(func(cell *board.Cell) {
// candidates := cell.notes.numbers
// if len(candidates) == 1 {
// change := Change{
// cell: cell,
// action: ActionSetNumber,
// value: candidates[0],
// }
// ns.changes = append(ns.changes, change)
// }
})
})
return len(ns.changes)
}
// ────────────────────────────────────────────────────────────────────────────── //
// NAKED_DOUBLE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type NakedPair struct {
Base
}
// ────────────────────────────────────────────────────────────────────────────── //
// NAKED_TRIPLE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type NakedTriple struct {
Base
}
+14
View File
@@ -0,0 +1,14 @@
package strategies
type EmptyRectangle struct {
Base
}
type UniqueRectangleT1 struct {
Base
}
type UniqueRectangleT4 struct {
Base
}
type UniqueRectangleT7 struct {
Base
}
+67
View File
@@ -0,0 +1,67 @@
package strategies
import (
"fmt"
"git.kleiax.de/homepage/libs/sudoku/board"
)
// ────────────────────────────────────────────────────────────────────────────── //
// STRATEGY INTERFACE //
// ────────────────────────────────────────────────────────────────────────────── //
type Strategy interface {
Init(f *board.Field)
ApplyAll() int
ApplyNext() bool
ApplyOne(n int) bool
Name() string
SearchProgressableCells() int
}
// ────────────────────────────────────────────────────────────────────────────── //
// BASE STRUCTURE //
// ────────────────────────────────────────────────────────────────────────────── //
type Base struct {
name string
field *board.Field
changes []board.Change //eigener Typ muss her
}
func (b *Base) Init(f *board.Field) {
b.field = f
}
func (b *Base) ApplyAll() int {
// for _, change := range sb.changes {
// //change.do()
// }
return len(b.changes)
}
func (b *Base) ApplyNext() bool {
if len(b.changes) < 1 {
return false
}
//sb.changes[0].do()
b.changes = b.changes[1:]
return true
}
func (b *Base) ApplyOne(n int) bool {
if len(b.changes) <= n || n < 0 {
return false
}
//sb.changes[n].do()
b.changes = append(b.changes[:n], b.changes[n+1:]...)
return true
}
func (b *Base) getName() string {
return "Unkown"
}
func (b *Base) Name() string {
return fmt.Sprintf("Die Strategie heißt: %s", b.getName())
}
+8
View File
@@ -0,0 +1,8 @@
package strategies
type Turbot2StringKite struct {
Base
}
type TurbotSkyscraper struct {
Base
}
+14
View File
@@ -0,0 +1,14 @@
package strategies
type XWing struct {
Base
}
type XYWing struct {
Base
}
type XYZWing struct {
Base
}
type WXYZWingBasic struct {
Base
}
+48
View File
@@ -0,0 +1,48 @@
package main
import (
"fmt"
"io"
"os"
"git.kleiax.de/homepage/libs/sudoku"
)
func main() {
solver := sudoku.Solver{}
solver.Add(&sudoku.LastDigit{})
solver.Add(&sudoku.NakedSingle{})
game, err := sudoku.New(&sudoku.PuzzleBank{}, solver, openFile())
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
field := game.GetField()
fmt.Println(field.String())
err = game.Solve()
if err != nil {
fmt.Println(err.Error())
}
field = game.GetField()
fmt.Println(field.String())
}
func openFile() []byte {
file, err := os.Open("libs/sudoku/data/sudoku-exchange-puzzle-bank/easy3.txt")
if err != nil {
fmt.Println("Kann datei nicht öffnen")
os.Exit(3)
}
defer file.Close()
content, err := io.ReadAll(file)
if err != nil {
fmt.Println("Kann Daten nicht extrahieren")
os.Exit(4)
}
return content
}
+48
View File
@@ -0,0 +1,48 @@
package sudoku
import (
"errors"
"fmt"
"git.kleiax.de/homepage/libs/sudoku/board"
"git.kleiax.de/homepage/libs/sudoku/logic"
)
type Game struct {
field *board.Field
solver logic.Solver
}
func New(parser Parser, solver Solver, data []byte) (*Game, error) {
err := parser.parse(data)
if err != nil {
return &Game{}, fmt.Errorf("can not create game: %w", err)
}
return &Game{solver: solver, field: parser.getField(0)}, nil
}
func (g *Game) Solve() error {
g.solver.initStragies(g.field)
for !g.isFinished() {
if !g.nextSolveStep() {
return errors.New("no strategy can solve the puzzle")
}
}
return nil
}
func (g *Game) nextSolveStep() bool {
return g.solver.run(0)
}
func (g *Game) prevSolveStep() {
// far far in the future
}
func (g *Game) isFinished() bool {
return g.field.isSolved()
}
func (g *Game) GetField() Field {
return *g.field
}
+5
View File
@@ -0,0 +1,5 @@
package sudoku
// type StrategyVisualization interface {
// pointOut(f *Field) []Mark
// }