

package mainimport ( "errors" "fmt" "os")const ( rows, columns = 9, 9 empty = 0)// Cell is a square on the Sudoku grid.type Cell struct { digit int8 fixed bool}// Grid is a Sudoku grid.type Grid [rows][columns]Cell// Errors that could occur.var ( ErrBounds = errors.New("out of bounds") ErrDigit = errors.New("invalid digit") ErrInRow = errors.New("digit already present in this row") ErrInColumn = errors.New("digit already present in this column") ErrInRegion = errors.New("digit already present in this region") ErrFixedDigit = errors.New("initial digits cannot be overwritten"))// NewSudoku makes a new Sudoku grid.func NewSudoku(digits [rows][columns]int8) *Grid { var grid Grid for r := 0; r < rows; r++ { for c := 0; c < columns; c++ { d := digits[r][c] if d != empty { grid[r][c].digit = d grid[r][c].fixed = true } } } return &grid}// Set a digit on a Sudoku grid.func (g *Grid) Set(row, column int, digit int8) error { switch { case !inBounds(row, column): return ErrBounds case !validDigit(digit): return ErrDigit case g.isFixed(row, column): return ErrFixedDigit case g.inRow(row, digit): return ErrInRow case g.inColumn(column, digit): return ErrInColumn case g.inRegion(row, column, digit): return ErrInRegion } g[row][column].digit = digit return nil}// Clear a cell from the Sudoku grid.func (g *Grid) Clear(row, column int) error { switch { case !inBounds(row, column): return ErrBounds case g.isFixed(row, column): return ErrFixedDigit } g[row][column].digit = empty return nil}func inBounds(row, column int) bool { if row < 0 || row >= rows || column < 0 || column >= columns { return false } return true}func validDigit(digit int8) bool { return digit >= 1 && digit <= 9}func (g *Grid) inRow(row int, digit int8) bool { for c := 0; c < columns; c++ { if g[row][c].digit == digit { return true } } return false}func (g *Grid) inColumn(column int, digit int8) bool { for r := 0; r < rows; r++ { if g[r][column].digit == digit { return true } } return false}func (g *Grid) inRegion(row, column int, digit int8) bool { startRow, startColumn := row/3*3, column/3*3 for r := startRow; r < startRow+3; r++ { for c := startColumn; c < startColumn+3; c++ { if g[r][c].digit == digit { return true } } } return false}func (g *Grid) isFixed(row, column int) bool { return g[row][column].fixed}func main() { s := NewSudoku([rows][columns]int8{ {5, 3, 0, 0, 7, 0, 0, 0, 0}, {6, 0, 0, 1, 9, 5, 0, 0, 0}, {0, 9, 8, 0, 0, 0, 0, 6, 0}, {8, 0, 0, 0, 6, 0, 0, 0, 3}, {4, 0, 0, 8, 0, 3, 0, 0, 1}, {7, 0, 0, 0, 2, 0, 0, 0, 6}, {0, 6, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 4, 1, 9, 0, 0, 5}, {0, 0, 0, 0, 8, 0, 0, 7, 9}, }) err := s.Set(1, 1, 4) if err != nil { fmt.Println(err) os.Exit(1) } for _, row := range s { fmt.Println(row) }}
package mainimport "testing"var grid = [rows][columns]int8{ {5, 3, 0, 0, 7, 0, 0, 0, 0}, {6, 0, 0, 1, 9, 5, 0, 0, 0}, {0, 9, 8, 0, 0, 0, 0, 6, 0}, {8, 0, 0, 0, 6, 0, 0, 0, 3}, {4, 0, 0, 8, 0, 3, 0, 0, 1}, {7, 0, 0, 0, 2, 0, 0, 0, 6}, {0, 6, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 4, 1, 9, 0, 0, 5}, {0, 0, 0, 0, 8, 0, 0, 7, 9},}func TestOk(t *testing.T) { s := NewSudoku(grid) tests := []struct { row, column int digit int8 }{ {1, 1, 4}, {0, 3, 6}, {1, 6, 4}, } for _, tt := range tests { err := s.Set(tt.row, tt.column, tt.digit) if err != nil { t.Errorf("Expected no error, got %v for row: %d, column: %d, digit: %d.", err, tt.row, tt.column, tt.digit) } err = s.Clear(tt.row, tt.column) if err != nil { t.Fatalf("Unexpected error clearing row: %d, column: %d", tt.row, tt.column) } }}func TestClear(t *testing.T) { s := NewSudoku(grid) err := s.Clear(0, 0) if err != ErrFixedDigit { t.Errorf("Clear expected ErrFixedDigit, got %v", err) } err = s.Clear(1, 1) if err != nil { t.Errorf("Clear expected no error, got %v", err) }}func TestErrors(t *testing.T) { s := NewSudoku(grid) tests := []struct { row, column int digit int8 err error }{ {-1, 0, 8, ErrBounds}, {0, -1, 8, ErrBounds}, {9, 0, 8, ErrBounds}, {0, 9, 8, ErrBounds}, {0, 0, -1, ErrDigit}, {0, 0, 10, ErrDigit}, {0, 2, 5, ErrInRow}, {2, 0, 5, ErrInColumn}, {1, 1, 8, ErrInRegion}, {2, 3, 7, ErrInRegion}, {0, 0, 1, ErrFixedDigit}, } for _, tt := range tests { err := s.Set(tt.row, tt.column, tt.digit) if err != tt.err { t.Errorf("Expected error %q, got %v for row: %d, column: %d, digit: %d.", tt.err, err, tt.row, tt.column, tt.digit) } }}