/
sudoku.go
177 lines (139 loc) · 3.32 KB
/
sudoku.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// package main accepts a sudoku puzzle on standard in and outputs a solved
// puzzle on standard out.
package main
import (
"bufio"
"flag"
"fmt"
"os"
"strconv"
"strings"
)
var nums = [10]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
var v = flag.Bool("v", false, "verbose output")
func main() {
flag.Parse()
var board [81]int
buildBoard(bufio.NewScanner(os.Stdin), &board)
if *v == true {
fmt.Println("Input:")
printBoard(board)
}
solve(&board, 0)
if *v == true {
fmt.Println("Output:")
}
printBoard(board)
}
// buildBoard builds a "board" (an array) by scanning input, splitting comma-
// separated integers and inserting them into an array.
func buildBoard(input *bufio.Scanner, board *[81]int) {
l := 0
for input.Scan() {
for i, n := range strings.Split(input.Text(), ",") {
var val int
// If i is a dash, val is 0
if n == "-" {
val = 0
} else {
// Convert i to an int
val2, err := strconv.Atoi(n)
if err != nil {
fmt.Println(os.Stderr, err)
os.Exit(2)
}
val = val2
}
board[i+9*l] = val
}
l++
}
}
// Print the board in an attractive manner.
func printBoard(board [81]int) {
for i, n := range board {
fmt.Print(n)
if i%9 == 8 {
fmt.Printf("\n")
} else {
fmt.Printf(",")
}
}
}
// Solves the board in-place by:
// * Returns true if the board is complete
// * If the current space is non-zero and valid,
// calling itself recursively on the next space.
// * Otherwise, going through the numbers 9 - 0 (DESC) and seeing if they're
// valid.
// * If they are valid, try solving the next space, and return true if it
// works.
// * Otherwise, keep trying numbers.
func solve(board *[81]int, ptr int) bool {
if ptr >= 81 {
return true
}
if board[ptr] != 0 && checkSpace(*board, ptr) == true {
return solve(board, ptr+1)
}
for _, n := range nums {
board[ptr] = n
if board[ptr] == 0 {
return false
}
if checkSpace(*board, ptr) == false {
continue
}
if solve(board, ptr+1) == true {
return true
}
}
return true
}
// Calculate the x,y coordinate of a position in the board array.
func x_y(ptr int) (int, int) {
x := ptr % 9
y := ptr / 9
return x, y
}
// isSameSector checks whether two pairs of x,y coordinates are in the
// same sector of the game board.
func inSameSector(x, y, ptr_x, ptr_y int) bool {
return x/3 == ptr_x/3 && y/3 == ptr_y/3
}
// checkSpace checks a space on the board to see whether the current column,
// row and sector are valid. It ignores zeros, and so relies on the algorithm
// to never leave a space zero.
func checkSpace(board [81]int, ptr int) bool {
ptr_x, ptr_y := x_y(ptr)
row := make(map[int]bool)
col := make(map[int]bool)
sec := make(map[int]bool)
for i := 0; i < len(board); i++ {
// If the space is a zero, ignore it.
if board[i] == 0 {
continue
}
val := board[i]
x, y := x_y(i)
if y == ptr_y && axisIsInvalid(val, row) {
return false
}
if x == ptr_x && axisIsInvalid(val, col) {
return false
}
if inSameSector(x, y, ptr_x, ptr_y) && axisIsInvalid(val, sec) {
return false
}
}
return true
}
// axisIsInvalid checks to see whether an axis (x, y or sector) is invalid.
// It is invalid if it contains more than one number.
func axisIsInvalid(val int, seen map[int]bool) bool {
if seen[val] == true {
return true
}
seen[val] = true
return false
}