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