diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..0e3d880 Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index 9be54f0..b0ece73 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,42 @@ +# Hersh Hajare +## Shopify technical challenge submission + +## My attempt +Unfortunately, due to technical issues, I had only a few hours to complete the challenge. I knew that I had to attempt this challenge using some sort of recursive approach. The idea of making a backtrack approach came from a class discussion involving a similar problem, so I knew exactly where to start. Once I had an idea of what the recursive function would look like, the rest came easily. To ensure my code worked properly, I used many test cases, including the most difficult Sudoku puzzles I could find online. This ensured correctness. + +Something to note is the code works assuming the inputted puzzle is a valid Sudoku puzzle. There were instances of the program giving a solution with an invalid puzzle. To solve this I had included a check to see if the inputted puzzle is valid. + +For example: This input is not a valid Sudoku puzzle, there cannot be two 5s within the same section and column. +``` +[ + [5, 3, 0, 0, 7, 0, 0, 0, 0], + [5, 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] +] +``` + +However, the program would output the following: +``` +[ + [5, 3, 4, 6, 7, 8, 9, 1, 2], + [5, 7, 2, 1, 9, 5, 3, 4, 8], + [1, 9, 8, 3, 4, 2, 5, 6, 7], + [8, 5, 9, 7, 6, 1, 4, 2, 3], + [4, 2, 6, 8, 5, 3, 7, 9, 1], + [7, 1, 3, 9, 2, 4, 8, 5, 6], + [9, 6, 1, 5, 3, 7, 2, 8, 4], + [2, 8, 7, 4, 1, 9, 6, 3, 5], + [3, 4, 5, 2, 8, 6, 1, 7, 9] +] +``` + + # Technical Instructions 1. Fork this repo to your local Github account. 2. Create a new branch to complete all your work in. diff --git a/sudoku b/sudoku new file mode 100755 index 0000000..fc8fbc1 Binary files /dev/null and b/sudoku differ diff --git a/sudoku.go b/sudoku.go index 06ab7d0..3512f69 100644 --- a/sudoku.go +++ b/sudoku.go @@ -1 +1,195 @@ package main + +import "fmt" + +const dim = 9 +const emptyCell = 0 + +// solves the Sudoku puzzle using backtracking +func solve(grid [][]int) bool { + row, col, found := findEmptyCell(grid) + + // check if successful + if !found { + return true + } + + // Filling empty cells + for num := 1; num <= dim; num++ { + if checkIfSafe(grid, row, col, num) { + grid[row][col] = num + // recursive step to solve puzzle + if solve(grid) { + return true + } + + // If solution is invalid, backtrack + grid[row][col] = emptyCell + } + } + return false +} + +// finds the position of empty cells +func findEmptyCell(grid [][]int) (int, int, bool) { + for row := 0; row < dim; row++ { + for col := 0; col < dim; col++ { + if grid[row][col] == emptyCell { + return row, col, true + } + } + } + return -1, -1, false +} + +// checks if specified value is present in the given row +func checkRow(grid [][]int, row int, val int) bool { + for columnIndex := 0; columnIndex < dim; columnIndex++ { + if grid[row][columnIndex] == val { + return true + } + } + return false +} + +// checks if specified value is present in the given column +func checkCol(grid [][]int, col int, val int) bool { + for rowIndex := 0; rowIndex < dim; rowIndex++ { + if grid[rowIndex][col] == val { + return true + } + } + return false +} + +// checks if specified value is present in the given section +func checkSection(grid [][]int, row int, col int, val int) bool { + for rowIndex := 0; rowIndex < 3; rowIndex++ { + for colIndex := 0; colIndex < 3; colIndex++ { + if grid[rowIndex+row][colIndex+col] == val { + return true + } + } + } + return false +} + +// checks whether the given value is able to be placed in the given position +func checkIfSafe(grid [][]int, row int, col int, val int) bool { + return grid[row][col] == emptyCell && !checkRow(grid, row, val) && !checkCol(grid, col, val) && !checkSection(grid, row-row%3, col-col%3, val) +} + +// prints the grid +func printGrid(grid [][]int) { + for rowIndex := 0; rowIndex < dim; rowIndex++ { + for colIndex := 0; colIndex < dim; colIndex++ { + fmt.Printf("%d ", grid[rowIndex][colIndex]) + } + fmt.Println() + } +} + +// solves the puzzle +func SolveSudoku(input [][]int) [][]int { + // checks if input is 9x9 + if len(input) != 9 { + return nil + } + for _, row := range input { + if len(row) != 9 { + return nil + } + } + + if !isValidInput(input){ + return nil + } + + if !solve(input) { + return nil + } + + return input +} + +func isValidInput(grid [][]int) bool { + for i := 0; i < 9; i++ { + if !checkValidRow(grid, i) || !checkValidCol(grid, i) || !checkValidSection(grid, i) { + return false + } + } + return true +} + +// checks if the specified row is valid +func checkValidRow(grid [][]int, row int) bool { + visited := make(map[int]bool) + for _, value := range grid[row] { + if value != 0 { + if visited[value] { + return false + } + visited[value] = true + } + } + return true +} + +// checks if the specified column is valid +func checkValidCol(grid [][]int, col int) bool { + visited := make(map[int]bool) + for row := 0; row < 9; row++ { + value := grid[row][col] + if value != 0 { + if visited[value] { + return false + } + visited[value] = true + } + } + return true +} + +// checks if the specified 3x3 box is valid +func checkValidSection(grid [][]int, box int) bool { + visited := make(map[int]bool) + rowOffset := (box / 3) * 3 + colOffset := (box % 3) * 3 + // section row + for i := 0; i < 3; i++ { + // section column + for j := 0; j < 3; j++ { + value := grid[rowOffset+i][colOffset+j] + if value != 0 { + if visited[value] { + return false + } + visited[value] = true + } + } + } + return true +} + +// main function, used for debugging +func main() { + grid := [][]int { + {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}, + } + + + solved := SolveSudoku(grid) + if solved != nil { + printGrid(solved) + return + } + fmt.Println("Cannot be solved") +}