diff --git a/dfs.go b/dfs.go new file mode 100644 index 0000000..6f29625 --- /dev/null +++ b/dfs.go @@ -0,0 +1,46 @@ +package main + +// dfs flattens the 2D board array into a 1D array and recursively fills in numbers. +// n: The index in the flattened 1D array. +// rows: The set of numbers already used in each row when examining position n. +// cols: The set of numbers already used in each column when examining position n. +// grids: The set of numbers already used in each grid (3x3 grid) when examining position n. +func dfs(board [][]int, n int, rows []int, cols []int, grids []int) bool { + // Recursive end condition: if n is equal to the total number of cells, all positions are filled. + if n == len(board)*len(board) { + return true + } + // Locate the row and column based on n. + var i, j = n / len(board), n % len(board) + // If the current position is already filled, skip to the next position. + if board[i][j] != 0 { + return dfs(board, n+1, rows, cols, grids) + } + // Attempt to fill the current position with numbers 1 to 9. + for k := 1; k <= 9; k++ { + var bit = 1 << k + // Check if the number is not yet used in the corresponding row, column, and grid. + if unused := rows[i]&bit == 0 && cols[j]&bit == 0 && grids[i/3*3+j/3]&bit == 0; unused { + // Mark the number as used in the row, column, and grid. + rows[i] |= bit + cols[j] |= bit + grids[i/3*3+j/3] |= bit + // Continue to fill the next position. + if dfs(board, n+1, rows, cols, grids) { + // If the recursion returns true, it means all subsequent positions are correctly filled, + // so we can fill the current position. + board[i][j] = k + return true + } else { + // If the recursion returns false, it means subsequent positions failed to fill, + // so the current number is invalid and we need to try the next number. + // Before trying the next number, we need to unmark the current number in the row, column, and grid. + rows[i] &= ^bit + cols[j] &= ^bit + grids[i/3*3+j/3] &= ^bit + } + } + } + // If none of the numbers 1 to 9 can be filled in the current position, the Sudoku puzzle is unsolvable at this configuration. + return false +} diff --git a/error.go b/error.go new file mode 100644 index 0000000..d41ff9b --- /dev/null +++ b/error.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" +) + +const ( + ERR_INVALID_SUDOKU_SIZE = -000001 // sudoku size is invalid + ERR_INVALID_SUDOKU = -000002 // sudoku cannot be solved +) + +func reportErr(errCode int) { + var errInfo string + switch errCode { + case ERR_INVALID_SUDOKU_SIZE: + errInfo = "Sudoku size is invalid" + case ERR_INVALID_SUDOKU: + errInfo = "Sudoku is unsolvable" + } + + fmt.Printf("%v with Error code:%v", errInfo, errCode) +} diff --git a/go.mod b/go.mod index d895f5c..cd74c1c 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1 @@ module sudoku - -go 1.21.5 diff --git a/sudoku.go b/sudoku.go index 06ab7d0..0426320 100644 --- a/sudoku.go +++ b/sudoku.go @@ -1 +1,37 @@ package main + +const ( + LEGAL_SUDOKU_SIZE = 9 // legal size of the input sudoku + +) + +func SolveSudoku(board [][]int) [][]int { + + // check input size + if len(board) != LEGAL_SUDOKU_SIZE { + reportErr(ERR_INVALID_SUDOKU_SIZE) + return board + } + for _, row := range board { + if len(row) != LEGAL_SUDOKU_SIZE { + reportErr(ERR_INVALID_SUDOKU_SIZE) + return board + } + } + + // Initialize slices to keep track of the numbers used in each row, column, and zone (3x3 grid), + // storing this information as bits. + var rows, cols, grids = make([]int, 9), make([]int, 9), make([]int, 9) + for i := range board { + for j := range board[i] { + if board[i][j] != 0 { + var bit = 1 << board[i][j] + rows[i] |= bit // Mark the bit as used in the row. + cols[j] |= bit // Mark the bit as used in the column. + grids[i/3*3+j/3] |= bit // Mark the bit as used in the zone. + } + } + } + dfs(board, 0, rows, cols, grids) + return board +}