-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSudoku.scala
139 lines (116 loc) · 3.17 KB
/
Sudoku.scala
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
import java.io.File
import java.util.Scanner
import java.io.BufferedWriter
import java.io.FileWriter
// Build a sudoku from a 81 char line
class Sudoku(text: String) {
val rowSize = 9
val board = Array.ofDim[Integer](rowSize, rowSize)
var i = 0
var j = 0
for (char <- text) {
board(i)(j) = char.toInt - 48
if (j == rowSize - 1) {
i += 1 % rowSize
j = 0
} else {
j += 1 % rowSize
}
}
override def toString:String =
{
val sb = new StringBuilder()
val lineSep = System.getProperty("line.separator")
for (row:Array[Integer] <- this.board) yield {
sb.append((row map (i => i.toString)).mkString(" ")).append(lineSep)
}
sb.toString
}
}
object SudokuSolver {
def solve(sudoku:Sudoku):Sudoku =
{
val spot = nextEmpty(sudoku)
spot match {
case None => sudoku
case Some((i,j)) =>
for (value <- 1 to 9) {
if (canPut(sudoku, (i,j), value)) {
sudoku.board(i)(j) = value
val sudoku2 = solve(sudoku)
if (!nextEmpty(sudoku2).isDefined) {
return sudoku2 // solution found
}
}
}
// backtrack, solution not found
sudoku.board(i)(j) = 0
return sudoku
}
}
def nextEmpty(sudoku:Sudoku):Option[Tuple2[Integer,Integer]] =
{
for (i <- 0 to sudoku.rowSize-1) {
for (j <- 0 to sudoku.rowSize-1) {
if (sudoku.board(i)(j) == 0) return Some((i,j))
}
}
None
}
def canPut(sudoku:Sudoku, spot:Tuple2[Integer,Integer], value:Integer):Boolean =
{
for (i <- 0 to sudoku.rowSize-1) {
// test row
if (sudoku.board(i)(spot._2) == value) return false
// test column
if (sudoku.board(spot._1)(i) == value) return false
}
// test square
val a = spot._1 - (spot._1%3)
val b = spot._2 - (spot._2%3)
for (i <- a to a+2) {
for (j <- b to b+2) {
if (sudoku.board(i)(j) == value) return false
}
}
true
}
}
class ProgressBar(var totalSteps:Integer, var step:Integer, var resolution:Integer, var width:Integer) {
def advance
{
step += 1
if (totalSteps/resolution == 0) return
if (step % (totalSteps/resolution) != 0) return
var ratio:Float = step/totalSteps.toFloat
val count:Int = (ratio * width).toInt
print("%3d%% [".format((ratio * 100).toInt))
(0 to count-1) foreach {_ => print("=")}
(count to width-1) foreach {_ => print(" ")}
print( "]\r ")
System.out.flush()
}
}
object Sudoku {
def main(args: Array[String]) {
val file = new File(args(0))
// read entire input into memory
val input = new Scanner(file, "UTF-8").useDelimiter("\\A").next()
val output = new StringBuilder()
val before = System.nanoTime
val lines = input.split("\\r?\\n")
val progressBar = new ProgressBar(lines.length, 1, 100, 100)
val lineSep = System.getProperty("line.separator")
for (line <- lines) {
val sudoku = new Sudoku(line)
output.append(SudokuSolver.solve(sudoku)).append(lineSep)
progressBar.advance
}
// elapsed time
println("\nElapsed time: " + ((System.nanoTime - before)/1e6) + " ms")
// write solutions to file
val writer = new BufferedWriter(new FileWriter("solved_" + file.getName()))
writer.write(output.toString)
writer.close
}
}