| 
     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 }  |