Files
termdoku/internal/game/board.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
}