150 lines
2.7 KiB
Go
150 lines
2.7 KiB
Go
package game
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
type Grid [9][9]uint8
|
|
|
|
type Move struct {
|
|
Row int
|
|
Col int
|
|
Prev uint8
|
|
Next uint8
|
|
At time.Time
|
|
}
|
|
|
|
type Board struct {
|
|
Given [9][9]bool
|
|
Values Grid
|
|
}
|
|
|
|
func NewBoardFromPuzzle(p Grid) Board {
|
|
var b Board
|
|
for r := 0; r < 9; r++ {
|
|
for c := 0; c < 9; c++ {
|
|
v := p[r][c]
|
|
if v != 0 {
|
|
b.Given[r][c] = true
|
|
}
|
|
b.Values[r][c] = v
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *Board) IsGiven(row, col int) bool { return b.Given[row][col] }
|
|
|
|
func (b *Board) SetValue(row, col int, v uint8) (prev uint8, ok bool) {
|
|
if b.Given[row][col] {
|
|
return b.Values[row][col], false
|
|
}
|
|
prev = b.Values[row][col]
|
|
b.Values[row][col] = v
|
|
return prev, true
|
|
}
|
|
|
|
func InBounds(row, col int) bool { return row >= 0 && row < 9 && col >= 0 && col < 9 }
|
|
|
|
// DuplicateMap marks cells that duplicate the selected cell's value across row/col/box.
|
|
func DuplicateMap(g Grid, selRow, selCol int) [9][9]bool {
|
|
var dup [9][9]bool
|
|
v := g[selRow][selCol]
|
|
if v == 0 {
|
|
return dup
|
|
}
|
|
for i := 0; i < 9; i++ {
|
|
if g[selRow][i] == v && i != selCol {
|
|
dup[selRow][i] = true
|
|
}
|
|
if g[i][selCol] == v && i != selRow {
|
|
dup[i][selCol] = true
|
|
}
|
|
}
|
|
r0 := (selRow / 3) * 3
|
|
c0 := (selCol / 3) * 3
|
|
for r := r0; r < r0+3; r++ {
|
|
for c := c0; c < c0+3; c++ {
|
|
if (r != selRow || c != selCol) && g[r][c] == v {
|
|
dup[r][c] = true
|
|
}
|
|
}
|
|
}
|
|
return dup
|
|
}
|
|
|
|
// DuplicateMapAll marks any duplicates in rows, columns, or 3x3 blocks across the entire grid.
|
|
func DuplicateMapAll(g Grid) [9][9]bool {
|
|
var dup [9][9]bool
|
|
// rows
|
|
for r := 0; r < 9; r++ {
|
|
count := map[uint8]int{}
|
|
for c := 0; c < 9; c++ {
|
|
v := g[r][c]
|
|
if v != 0 {
|
|
count[v]++
|
|
}
|
|
}
|
|
for c := 0; c < 9; c++ {
|
|
v := g[r][c]
|
|
if v != 0 && count[v] > 1 {
|
|
dup[r][c] = true
|
|
}
|
|
}
|
|
}
|
|
// cols
|
|
for c := 0; c < 9; c++ {
|
|
count := map[uint8]int{}
|
|
for r := 0; r < 9; r++ {
|
|
v := g[r][c]
|
|
if v != 0 {
|
|
count[v]++
|
|
}
|
|
}
|
|
for r := 0; r < 9; r++ {
|
|
v := g[r][c]
|
|
if v != 0 && count[v] > 1 {
|
|
dup[r][c] = true
|
|
}
|
|
}
|
|
}
|
|
// blocks
|
|
for br := 0; br < 3; br++ {
|
|
for bc := 0; bc < 3; bc++ {
|
|
count := map[uint8]int{}
|
|
for r := br * 3; r < br*3+3; r++ {
|
|
for c := bc * 3; c < bc*3+3; c++ {
|
|
v := g[r][c]
|
|
if v != 0 {
|
|
count[v]++
|
|
}
|
|
}
|
|
}
|
|
for r := br * 3; r < br*3+3; r++ {
|
|
for c := bc * 3; c < bc*3+3; c++ {
|
|
v := g[r][c]
|
|
if v != 0 && count[v] > 1 {
|
|
dup[r][c] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return dup
|
|
}
|
|
|
|
// ConflictMap marks cells that violate Sudoku constraints (duplicates), excluding givens.
|
|
func ConflictMap(values Grid, given [9][9]bool) [9][9]bool {
|
|
all := DuplicateMapAll(values)
|
|
var bad [9][9]bool
|
|
for r := 0; r < 9; r++ {
|
|
for c := 0; c < 9; c++ {
|
|
if given[r][c] {
|
|
continue
|
|
}
|
|
bad[r][c] = all[r][c]
|
|
}
|
|
}
|
|
return bad
|
|
}
|