I've always been curious about Sudoku solvers, thanks for sharing. Also, thanks for including the test cases. The following code passes against them. In general I found the code to be quite clear. Most of the changes I've made are for conciseness and reducing the overall runtime of your program.
For starters you'll notice I've added four new containers to the Board class (xs, col, pairs, sqr). The overall affect is that you won't have to recompute these containersavoid recomputing them with each call to isSolved(). I've also changed how we generate pairs (using tabulate over nested for loops) and the containers used in the square function which I've called sqr. As a side note, the first three could in fact be moved the the Board object and shared among all instantiations of the class.
In isSolved() we now generategenerates a single Boolean value for each check instead of a Seq of them. We do thisThis is done with the forall method which returns true if all values in the container are true when evaluated by a predicate expression (the predicate in this case being Board.solvedSeq). In this end this allows us to simply && the results together which is a bit quicker and clearer than what you previously had.
FinallyLastly, in Board.solvedSeq I removed the if-else expression andin Board.solvedSeq can be replaced it with thea comparison operator which is functionally equivalent in this case.
class Board(values: Int*) {
require(values.length == 81, "Board must contain 81 cells")
val xs = 0 until 9
val col = 0 until 81
val pairs = IndexedSeq.tabulate(3, 3)((_, _)).flatten
val sqr = (0 to 6 by 3).map(values.drop(_).sliding(3, 9).grouped(3).toSeq)
def isSolved(): Boolean = {
val rowsSolved: Boolean = xs.map(row).forall(Board.solvedSeq)
val colsSolved: Boolean = xs.map(column).forall(Board.solvedSeq)
val squaresSolved: Boolean =
pairs.forall(p =>
Board.solvedSeq(square(p._1)(p._2).flatten)
)
rowsSolved && colsSolved && squaresSolved
}
def row(i: Int): Seq[Int] = values.slice(i * 9, (i * 9) + 9)
def column(i: Int): Seq[Int] = col.drop(i).grouped(9).map(_.head).map(i => values(i)).toSeq
def square(i: Int): Seq[Seq[Seq[Int]]] = sqr(i)
}
object Board {
val compSeq = (1 to 9)
def solvedSeq(s: Seq[Int]): Boolean = s.distinct.sorted == compSeq
}