Files
Sudoku/field/parts.go
T

161 lines
3.0 KiB
Go

package field
import (
"fmt"
"strings"
)
type Part interface {
IsSolved() bool
ForEachCell(func(*Cell))
GetMissingNumbers() []int
RemoveNote(*Field, int, string, []Mark) error
String() string
}
type Line struct {
cells []*Cell
}
func (l *Line) IsSolved() bool {
return partIsSolved(l)
}
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 {
if l == nil {
return nil
}
return getMissingNumbers(len(l.cells), l.ForEachCell)
}
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 partString(l, "Line")
}
type Row struct {
Line
}
func (r *Row) String() string {
return partString(r, "Row")
}
type Column struct {
Line
}
func (c *Column) String() string {
return partString(c, "Column")
}
type Block struct {
cells [][]*Cell
}
func (b *Block) IsSolved() bool {
return partIsSolved(b)
}
func (b *Block) ForEachCell(fn func(*Cell)) {
if b == nil || fn == nil {
return
}
for _, row := range b.cells {
for _, cell := range row {
fn(cell)
}
}
}
func (b *Block) GetMissingNumbers() []int {
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(f *Field, note int, trigger string, marks []Mark) error {
return removeNoteFromPart(b, f, note, trigger, marks)
}
func (b *Block) String() string {
return partString(b, "Block")
}
func partIsSolved(part Part) bool {
if part == nil {
return false
}
cellCount := 0
seen := make(map[int]struct{})
valid := true
part.ForEachCell(func(cell *Cell) {
cellCount++
if cell == nil || cell.number <= 0 {
valid = false
return
}
if _, exists := seen[cell.number]; exists {
valid = false
}
seen[cell.number] = struct{}{}
})
return valid && cellCount > 0 && len(seen) == cellCount
}
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 != nil && cell.number != 0 {
present[cell.number] = struct{}{}
}
})
missing := make([]int, 0, max-len(present))
for number := 1; number <= max; number++ {
if _, exists := present[number]; !exists {
missing = append(missing, number)
}
}
return missing
}