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 }