first successful run to solve a simple puzzle
This commit is contained in:
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4,6 +4,8 @@ package board
|
||||
// NOTES STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
import "slices"
|
||||
|
||||
type Notes struct {
|
||||
numbers []int
|
||||
}
|
||||
@@ -49,6 +51,10 @@ func (n *Notes) Get() []int {
|
||||
return copySlice
|
||||
}
|
||||
|
||||
func (n *Notes) Has(i int) bool {
|
||||
return slices.Contains(n.numbers, i)
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// CELL STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
+26
-1
@@ -82,10 +82,28 @@ func (f *Field) GetBlock(r, c int) (*Block, error) {
|
||||
block.cells[row][column] = &f.cells[startRow+row][startCol+column]
|
||||
}
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (f *Field) GetEachPartAtPos(pos *Position) []Part {
|
||||
row, err := f.GetRow(pos.row)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
column, err := f.GetColumn(pos.column)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
block, err := f.GetBlock(pos.blockRow, pos.blockColumn)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return append([]Part{}, row, column, block)
|
||||
}
|
||||
|
||||
func (f *Field) GetCell(r, c int) (*Cell, error) {
|
||||
if r > f.rows || c > f.columns {
|
||||
return nil, outOfBound
|
||||
@@ -111,6 +129,13 @@ func (f *Field) ForEachPart(fn func(part Part)) {
|
||||
})
|
||||
}
|
||||
|
||||
func (f *Field) ForEachPartAtPos(pos *Position, fn func(part Part)) {
|
||||
parts := f.GetEachPartAtPos(pos)
|
||||
for _, part := range parts {
|
||||
fn(part)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Field) ForEachRow(fn func(row *Row)) {
|
||||
for i := range f.rows {
|
||||
row, err := f.GetRow(i)
|
||||
|
||||
@@ -80,6 +80,7 @@ func (pb *PuzzleBank) Parse(data []byte) error {
|
||||
return fmt.Errorf("Kann Zeichen nicht in Zahl konvertieren")
|
||||
}
|
||||
field.cells[i][j].number = number
|
||||
field.cells[i][j].Notes = &Notes{}
|
||||
field.cells[i][j].Pos = &Position{
|
||||
row: i,
|
||||
column: j,
|
||||
|
||||
+17
-1
@@ -1,6 +1,9 @@
|
||||
package board
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (f *Field) String() string {
|
||||
//TODO: auf beliebige größen anpassen
|
||||
@@ -45,6 +48,19 @@ func (f *Field) String() string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (f *Field) StringNotesForNumber(n int) string {
|
||||
return "StringNotesForNumber is not implementet"
|
||||
}
|
||||
|
||||
func (f *Field) StringNotes() string {
|
||||
var str strings.Builder
|
||||
fmt.Fprintf(&str, "Notes:\n")
|
||||
f.ForEachCell(func(cell *Cell) {
|
||||
fmt.Fprintf(&str, "Pos: %d/%d - Notes: %v\n", cell.Pos.row, cell.Pos.column, cell.Notes.numbers)
|
||||
})
|
||||
return str.String()
|
||||
}
|
||||
|
||||
func (f *Field) IsSolved() bool {
|
||||
result := true
|
||||
f.ForEachCell(func(cell *Cell) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
0000183b305c 451723860007000800000816000000030000005000100730040086906000204840572093000409000 1.2
|
||||
0000183b305c 050703060007000800000816000000030000005000100730040086906000204840572093000409000 1.2
|
||||
0001d5d6314e 302401809001000300000000000040708010780502036000090000200609003900000008800070005 1.2
|
||||
000212406270 000823001003000400070000052300960010000102000010038006830000040002000900600789000 1.2
|
||||
@@ -1,4 +0,0 @@
|
||||
package logic
|
||||
|
||||
type Change struct {
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package logic
|
||||
+26
-6
@@ -11,6 +11,8 @@ import (
|
||||
|
||||
type Solver struct {
|
||||
strategies []strategies.Strategy
|
||||
returnTo int
|
||||
field *board.Field
|
||||
conf struct {
|
||||
all bool
|
||||
repeat bool
|
||||
@@ -22,19 +24,37 @@ func (s *Solver) Add(strategy strategies.Strategy) {
|
||||
}
|
||||
|
||||
func (s *Solver) InitStragies(field *board.Field) {
|
||||
s.returnTo = 1
|
||||
s.field = 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
|
||||
for j := 0; j < len(s.strategies); j++ {
|
||||
if s.strategies[j].SearchProgressableCells() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, change := range s.strategies[j].ApplyAll() {
|
||||
if change.Action != board.ActionSetNumber {
|
||||
continue
|
||||
}
|
||||
s.field.ForEachPartAtPos(change.Cell.Pos, func(part board.Part) {
|
||||
part.ForEachCell(func(cell *board.Cell) {
|
||||
cell.Notes.Remove(s.field, cell, change.Value, "remove note after insert of a number", nil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
j = s.returnTo - 1
|
||||
}
|
||||
return true
|
||||
|
||||
if s.field.IsSolved() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Solver) Search() {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kleiax.de/homepage/board"
|
||||
)
|
||||
|
||||
@@ -24,6 +22,8 @@ func (ld *LastDigit) SearchProgressableCells() int {
|
||||
emptyCell = cell
|
||||
}
|
||||
})
|
||||
// fmt.Println(part)
|
||||
// fmt.Printf("Gefundene Zelle: %d/%d - %d, missungNumber: %v, Typ: %T\n", emptyCell.Pos.GetRow(), emptyCell.Pos.GetColumn(), emptyCell.GetNumber(), missingNumbers, part)
|
||||
change := board.ExternalChange{
|
||||
Cell: emptyCell,
|
||||
Action: board.ActionSetNumber,
|
||||
@@ -34,12 +34,12 @@ func (ld *LastDigit) SearchProgressableCells() int {
|
||||
ld.changes = append(ld.changes, change)
|
||||
}
|
||||
})
|
||||
fmt.Printf("LastDigit Changes %d\n", len(ld.changes))
|
||||
// fmt.Printf("LastDigit Changes %d\n", len(ld.changes))
|
||||
return len(ld.changes)
|
||||
}
|
||||
|
||||
func (ld *LastDigit) getName() string {
|
||||
return "Last Digit - Letzte Zahl"
|
||||
return "Last Digit"
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
@@ -14,7 +14,7 @@ func (ns *NakedSingle) SearchProgressableCells() int {
|
||||
ns.field.ForEachPart(func(part board.Part) {
|
||||
part.ForEachCell(func(cell *board.Cell) {
|
||||
candidates := cell.Notes.Get()
|
||||
if len(candidates) == 1 {
|
||||
if len(candidates) == 1 && cell.GetNumber() == 0 {
|
||||
change := board.ExternalChange{
|
||||
Cell: cell,
|
||||
Action: board.ActionSetNumber,
|
||||
@@ -29,6 +29,10 @@ func (ns *NakedSingle) SearchProgressableCells() int {
|
||||
return len(ns.changes)
|
||||
}
|
||||
|
||||
func (ns *NakedSingle) getName() string {
|
||||
return "Naked Single"
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// NAKED_DOUBLE STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package strategies
|
||||
|
||||
import (
|
||||
"git.kleiax.de/homepage/board"
|
||||
)
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// NOTES STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Notes struct {
|
||||
Base
|
||||
}
|
||||
|
||||
func (n *Notes) SearchProgressableCells() int {
|
||||
n.field.ForEachRow(func(row *board.Row) {
|
||||
row.ForEachCell(func(cell *board.Cell) {
|
||||
column, _ := n.field.GetColumn(cell.Pos.GetColumn())
|
||||
block, _ := n.field.GetBlock(cell.Pos.GetBlockRow(), cell.Pos.GetBlockColumn())
|
||||
candidates := intersection3(row.GetMissingNumbers(), column.GetMissingNumbers(), block.GetMissingNumbers())
|
||||
for _, note := range candidates {
|
||||
if cell.Notes.Has(note) || cell.GetNumber() != 0 {
|
||||
continue
|
||||
}
|
||||
ch := board.ExternalChange{
|
||||
Cell: cell,
|
||||
Action: board.ActionSetNote,
|
||||
Value: note,
|
||||
TriggerdBy: n.getName(),
|
||||
Marks: nil,
|
||||
From: 0,
|
||||
}
|
||||
n.changes = append(n.changes, ch)
|
||||
}
|
||||
})
|
||||
})
|
||||
return len(n.changes)
|
||||
}
|
||||
|
||||
func (n *Notes) getName() string {
|
||||
return "Make Notes"
|
||||
}
|
||||
|
||||
func intersection3(a, b, c []int) []int {
|
||||
set := make(map[int]bool)
|
||||
|
||||
for _, v := range a {
|
||||
set[v] = true
|
||||
}
|
||||
|
||||
// Nur Werte behalten, die auch in b vorkommen
|
||||
inB := make(map[int]bool)
|
||||
for _, v := range b {
|
||||
inB[v] = true
|
||||
}
|
||||
|
||||
for v := range set {
|
||||
if !inB[v] {
|
||||
delete(set, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Nur Werte behalten, die auch in c vorkommen
|
||||
inC := make(map[int]bool)
|
||||
for _, v := range c {
|
||||
inC[v] = true
|
||||
}
|
||||
|
||||
for v := range set {
|
||||
if !inC[v] {
|
||||
delete(set, v)
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]int, 0, len(set))
|
||||
for v := range set {
|
||||
result = append(result, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
type Strategy interface {
|
||||
Init(f *board.Field)
|
||||
ApplyAll() int
|
||||
ApplyNext() bool
|
||||
ApplyAll() []board.ExternalChange
|
||||
ApplyNext() board.ExternalChange
|
||||
Name() string
|
||||
SearchProgressableCells() int
|
||||
}
|
||||
@@ -40,23 +40,31 @@ func (b *Base) Init(f *board.Field) {
|
||||
b.field = f
|
||||
}
|
||||
|
||||
func (b *Base) ApplyAll() int {
|
||||
func (b *Base) ApplyAll() []board.ExternalChange {
|
||||
changesCopy := make([]board.ExternalChange, len(b.changes))
|
||||
copy(changesCopy, b.changes)
|
||||
|
||||
for _, change := range b.changes {
|
||||
b.field.AddChange(&change)
|
||||
}
|
||||
len := len(b.changes)
|
||||
|
||||
b.changes = b.changes[:0]
|
||||
|
||||
return len
|
||||
return changesCopy
|
||||
}
|
||||
|
||||
func (b *Base) ApplyNext() bool {
|
||||
func (b *Base) ApplyNext() board.ExternalChange {
|
||||
if len(b.changes) < 1 {
|
||||
return false
|
||||
return board.ExternalChange{}
|
||||
}
|
||||
|
||||
changeCopy := b.changes[0]
|
||||
|
||||
b.field.AddChange(&b.changes[0])
|
||||
|
||||
b.changes = b.changes[1:]
|
||||
return true
|
||||
|
||||
return changeCopy
|
||||
}
|
||||
|
||||
func (b *Base) getName() string {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
func main() {
|
||||
solver := logic.Solver{}
|
||||
solver.Add(&strategies.Notes{})
|
||||
solver.Add(&strategies.LastDigit{})
|
||||
solver.Add(&strategies.NakedSingle{})
|
||||
game, err := sudoku.New(&board.PuzzleBank{}, solver, openFile())
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
- Parser kann eigenes Package sein nur das Interface und die Helfer funktionen im Board lassen, jeder kann einen Parser für sseine Quelle schreiben. Vielleicht ein Standardparser in Board für gängige Typen oder Hilfsfunktionen
|
||||
- Feld immer wieder auf Validität checken (keine doppelten Zahlen in einem Part) damit keine Fehler bei den Strategien auftauchen können
|
||||
Reference in New Issue
Block a user