|      1 // Sudoku |         | 
|      2 //======== |         | 
|      3  |         | 
|      4 // call parallel version with |         | 
|      5 // |         | 
|      6 // scala -cp scala-parallel-collections_2.13-0.2.0.jar sudoku_test.scala  |         | 
|      7 // |         | 
|      8 // or |         | 
|      9 // |         | 
|     10 // scalac -cp scala-parallel-collections_2.13-0.2.0.jar sudoku_test.scala |         | 
|     11 // java -cp .:scala-library-2.13.0.jar:scala-parallel-collections_2.13-0.2.0.jar Sudoku |         | 
|     12  |         | 
|     13 object Sudoku extends App {  |         | 
|     14  |         | 
|     15 import scala.collection.parallel.CollectionConverters._ |         | 
|     16  |         | 
|     17 type Pos = (Int, Int) |         | 
|     18 val emptyValue = '.' |         | 
|     19 val maxValue = 9 |         | 
|     20  |         | 
|     21 val allValues = "123456789".toList |         | 
|     22 val indexes = (0 to 8).toList |         | 
|     23  |         | 
|     24  |         | 
|     25 def empty(game: String) = game.indexOf(emptyValue) |         | 
|     26 def isDone(game: String) = empty(game) == -1  |         | 
|     27 def emptyPosition(game: String) = { |         | 
|     28   val e = empty(game) |         | 
|     29   (e % maxValue, e / maxValue) |         | 
|     30 } |         | 
|     31  |         | 
|     32  |         | 
|     33 def get_row(game: String, y: Int) = indexes.map(col => game(y * maxValue + col)) |         | 
|     34 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * maxValue)) |         | 
|     35  |         | 
|     36 def get_box(game: String, pos: Pos): List[Char] = { |         | 
|     37     def base(p: Int): Int = (p / 3) * 3 |         | 
|     38     val x0 = base(pos._1) |         | 
|     39     val y0 = base(pos._2) |         | 
|     40     for (x <- (x0 until x0 + 3).toList; |         | 
|     41          y <- (y0 until y0 + 3).toList) yield game(x + y * maxValue) |         | 
|     42 }          |         | 
|     43  |         | 
|     44  |         | 
|     45 def update(game: String, pos: Int, value: Char): String =  |         | 
|     46   game.updated(pos, value) |         | 
|     47  |         | 
|     48 def toAvoid(game: String, pos: Pos): List[Char] =  |         | 
|     49   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos)) |         | 
|     50  |         | 
|     51 def candidates(game: String, pos: Pos): List[Char] =  |         | 
|     52   allValues.diff(toAvoid(game, pos)) |         | 
|     53  |         | 
|     54 def search(game: String): List[String] = { |         | 
|     55   if (isDone(game)) List(game) |         | 
|     56   else { |         | 
|     57     val cs = candidates(game, emptyPosition(game)) |         | 
|     58     cs.par.map(c => search(update(game, empty(game), c))).toList.flatten |         | 
|     59   } |         | 
|     60 } |         | 
|     61  |         | 
|     62  |         | 
|     63 def pretty(game: String): String =  |         | 
|     64   "\n" + (game.grouped(maxValue).mkString(",\n")) |         | 
|     65  |         | 
|     66  |         | 
|     67 val game2 = """8........ |         | 
|     68               |..36..... |         | 
|     69               |.7..9.2.. |         | 
|     70               |.5...7... |         | 
|     71               |....457.. |         | 
|     72               |...1...3. |         | 
|     73               |..1....68 |         | 
|     74               |..85...1. |         | 
|     75               |.9....4..""".stripMargin.replaceAll("\\n", "") |         | 
|     76  |         | 
|     77 // for measuring time |         | 
|     78 def time_needed[T](i: Int, code: => T) = { |         | 
|     79   val start = System.nanoTime() |         | 
|     80   for (j <- 1 to i) code |         | 
|     81   val end = System.nanoTime() |         | 
|     82   s"${(end - start) / 1.0e9} secs" |         | 
|     83 } |         | 
|     84  |         | 
|     85  |         | 
|     86 println(time_needed(10, search(game2))) |         | 
|     87  |         | 
|     88 } |         |