Refactor Sudoku field and solver implementation

This commit is contained in:
2026-09-16 22:52:44 +02:00
parent 8a5ae8c640
commit f7f9c16184
24 changed files with 1574 additions and 603 deletions
+82
View File
@@ -0,0 +1,82 @@
package strategies
import (
"strings"
"testing"
"git.kleiax.de/homepage/field"
)
const solvedGrid = "123456789456789123789123456234567891567891234891234567345678912678912345912345678"
func strategyField(t *testing.T, digits string) *field.Field {
t.Helper()
props := field.Properties{Rows: 9, Columns: 9, BlockRows: 3, BlockColumns: 3, BlockSizeRow: 3, BlockSizeColumn: 3}
cells := make([][]field.Cell, 9)
for row := range cells {
cells[row] = make([]field.Cell, 9)
for column := range cells[row] {
pos := field.NewPosition(row, column, row/3, column/3, row%3, column%3)
cells[row][column] = *field.NewCell(int(digits[row*9+column]-'0'), pos)
}
}
result, err := field.New(props, cells)
if err != nil {
t.Fatal(err)
}
return result
}
func TestLastDigitDeduplicatesOverlappingParts(t *testing.T) {
f := strategyField(t, "0"+solvedGrid[1:])
strategy := &LastDigit{}
strategy.Init(f)
if got := strategy.SearchProgressableCells(); got != 1 {
t.Fatalf("SearchProgressableCells() = %d, want 1", got)
}
changes, err := strategy.ApplyAll()
if err != nil {
t.Fatalf("ApplyAll() error = %v", err)
}
if len(changes) != 1 || !f.IsSolved() {
t.Fatalf("changes = %d, solved = %v", len(changes), f.IsSolved())
}
}
func TestHiddenSingleUsesCandidateValuesNotSliceIndexes(t *testing.T) {
f := strategyField(t, strings.Repeat("0", 81))
first, _ := f.GetCell(0, 0)
second, _ := f.GetCell(0, 1)
third, _ := f.GetCell(0, 2)
for _, setup := range []struct {
cell *field.Cell
note int
}{{first, 9}, {second, 8}, {third, 8}} {
if err := setup.cell.GetNotes().Add(f, setup.cell, setup.note, "test", nil); err != nil {
t.Fatal(err)
}
}
strategy := &HiddenSingle{}
strategy.Init(f)
strategy.SearchProgressableCells()
found := false
for _, change := range strategy.changes {
if change.Cell == first && change.Value == 9 {
found = true
}
}
if !found {
t.Fatal("hidden single for candidate value 9 was not found")
}
}
func TestIntersection3IsSorted(t *testing.T) {
got := intersection3([]int{9, 1, 5}, []int{5, 9, 1}, []int{9, 5, 1})
want := []int{1, 5, 9}
for i := range want {
if got[i] != want[i] {
t.Fatalf("intersection3() = %v, want %v", got, want)
}
}
}