Refactor Sudoku field and solver implementation
This commit is contained in:
+79
-67
@@ -7,83 +7,72 @@ import (
|
||||
|
||||
type Part interface {
|
||||
IsSolved() bool
|
||||
ForEachCell(fn func(cell *Cell))
|
||||
ForEachCell(func(*Cell))
|
||||
GetMissingNumbers() []int
|
||||
RemoveNote(field *Field, note int, trigger string, marks []Mark)
|
||||
RemoveNote(*Field, int, string, []Mark) error
|
||||
String() string
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// LINE STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Line struct {
|
||||
cells []*Cell
|
||||
}
|
||||
|
||||
func (l *Line) IsSolved() bool {
|
||||
//TODO
|
||||
return false
|
||||
return partIsSolved(l)
|
||||
}
|
||||
|
||||
func (l *Line) ForEachCell(fn func(cell *Cell)) {
|
||||
func (l *Line) ForEachCell(fn func(*Cell)) {
|
||||
if l == nil || fn == nil {
|
||||
return
|
||||
}
|
||||
for _, cell := range l.cells {
|
||||
fn(cell)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Line) GetMissingNumbers() []int {
|
||||
return getMissingNumbersHelper(len(l.cells), l.ForEachCell)
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
return getMissingNumbers(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)
|
||||
})
|
||||
func (l *Line) RemoveNote(f *Field, note int, trigger string, marks []Mark) error {
|
||||
return removeNoteFromPart(l, f, note, trigger, marks)
|
||||
}
|
||||
|
||||
func (l *Line) String() string {
|
||||
return lineString(l, "Line")
|
||||
return partString(l, "Line")
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// ROW STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Row struct {
|
||||
Line
|
||||
}
|
||||
|
||||
func (r *Row) String() string {
|
||||
return lineString(r, "Row")
|
||||
return partString(r, "Row")
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// COLUMN STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Column struct {
|
||||
Line
|
||||
}
|
||||
|
||||
func (c *Column) String() string {
|
||||
return lineString(c, "Column")
|
||||
return partString(c, "Column")
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// BLOCK STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Block struct {
|
||||
cells [][]*Cell
|
||||
}
|
||||
|
||||
func (b *Block) IsSolved() bool {
|
||||
//TODO
|
||||
return false
|
||||
return partIsSolved(b)
|
||||
}
|
||||
|
||||
func (b *Block) ForEachCell(fn func(cell *Cell)) {
|
||||
func (b *Block) ForEachCell(fn func(*Cell)) {
|
||||
if b == nil || fn == nil {
|
||||
return
|
||||
}
|
||||
for _, row := range b.cells {
|
||||
for _, cell := range row {
|
||||
fn(cell)
|
||||
@@ -92,56 +81,79 @@ func (b *Block) ForEachCell(fn func(cell *Cell)) {
|
||||
}
|
||||
|
||||
func (b *Block) GetMissingNumbers() []int {
|
||||
max := len(b.cells) * len(b.cells[0])
|
||||
return getMissingNumbersHelper(max, b.ForEachCell)
|
||||
if b == nil || len(b.cells) == 0 {
|
||||
return nil
|
||||
}
|
||||
size := 0
|
||||
for _, row := range b.cells {
|
||||
size += len(row)
|
||||
}
|
||||
return getMissingNumbers(size, 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)
|
||||
})
|
||||
func (b *Block) RemoveNote(f *Field, note int, trigger string, marks []Mark) error {
|
||||
return removeNoteFromPart(b, f, note, trigger, marks)
|
||||
}
|
||||
|
||||
func (b *Block) String() string {
|
||||
var str strings.Builder
|
||||
str.WriteString("Block:\t")
|
||||
lastLine := 0
|
||||
b.ForEachCell(func(cell *Cell) {
|
||||
fmt.Fprintf(&str, "%d ", cell.number)
|
||||
if lastLine == cell.Pos.inBlockRow {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
return str.String()
|
||||
return partString(b, "Block")
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// HELPER //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
func lineString(part Part, name string) string {
|
||||
var str strings.Builder
|
||||
fmt.Fprintf(&str, "%s:\t", name)
|
||||
func partIsSolved(part Part) bool {
|
||||
if part == nil {
|
||||
return false
|
||||
}
|
||||
cellCount := 0
|
||||
seen := make(map[int]struct{})
|
||||
valid := true
|
||||
part.ForEachCell(func(cell *Cell) {
|
||||
fmt.Fprintf(&str, "%d ", cell.number)
|
||||
cellCount++
|
||||
if cell == nil || cell.number <= 0 {
|
||||
valid = false
|
||||
return
|
||||
}
|
||||
if _, exists := seen[cell.number]; exists {
|
||||
valid = false
|
||||
}
|
||||
seen[cell.number] = struct{}{}
|
||||
})
|
||||
return str.String()
|
||||
return valid && cellCount > 0 && len(seen) == cellCount
|
||||
}
|
||||
|
||||
func getMissingNumbersHelper(max int, iterate func(fn func(cell *Cell))) []int {
|
||||
present := make(map[int]bool, max)
|
||||
func removeNoteFromPart(part Part, f *Field, note int, trigger string, marks []Mark) error {
|
||||
var firstErr error
|
||||
part.ForEachCell(func(cell *Cell) {
|
||||
if firstErr != nil || !cell.notes.Has(note) {
|
||||
return
|
||||
}
|
||||
firstErr = cell.notes.Remove(f, cell, note, trigger, marks)
|
||||
})
|
||||
return firstErr
|
||||
}
|
||||
|
||||
func partString(part Part, name string) string {
|
||||
if part == nil {
|
||||
return name + ":"
|
||||
}
|
||||
var result strings.Builder
|
||||
fmt.Fprintf(&result, "%s:\t", name)
|
||||
part.ForEachCell(func(cell *Cell) {
|
||||
fmt.Fprintf(&result, "%d ", cell.GetNumber())
|
||||
})
|
||||
return strings.TrimSuffix(result.String(), " ")
|
||||
}
|
||||
|
||||
func getMissingNumbers(max int, iterate func(func(*Cell))) []int {
|
||||
present := make(map[int]struct{}, max)
|
||||
iterate(func(cell *Cell) {
|
||||
if cell.number != 0 {
|
||||
present[cell.number] = true
|
||||
if cell != nil && cell.number != 0 {
|
||||
present[cell.number] = struct{}{}
|
||||
}
|
||||
})
|
||||
|
||||
var missing []int
|
||||
for i := 1; i <= max; i++ {
|
||||
if !present[i] {
|
||||
missing = append(missing, i)
|
||||
missing := make([]int, 0, max-len(present))
|
||||
for number := 1; number <= max; number++ {
|
||||
if _, exists := present[number]; !exists {
|
||||
missing = append(missing, number)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
|
||||
Reference in New Issue
Block a user