progs/sudoku.scala
changeset 301 c3b33c709696
parent 268 e43f7e92ba26
child 320 cdfb2ce30a3d
equal deleted inserted replaced
300:72688efdf17c 301:c3b33c709696
     1 // Sudoku
     1 // Sudoku
     2 //========
     2 //========
       
     3 
       
     4 // call parallel version with
       
     5 //
       
     6 // scala -cp scala-parallel-collections_2.13-0.2.0.jar sudoku.scala 
       
     7 //
       
     8 // java -cp .:scala-library-2.13.0.jar:scala-parallel-collections_2.13-0.2.0.jar Sudoku
     3 
     9 
     4 object Sudoku extends App { 
    10 object Sudoku extends App { 
     5 
    11 
     6 import scala.collection.parallel.CollectionConverters._
    12 import scala.collection.parallel.CollectionConverters._
     7 
    13 
    29     for (x <- (x0 until x0 + 3).toList;
    35     for (x <- (x0 until x0 + 3).toList;
    30          y <- (y0 until y0 + 3).toList) yield game(x + y * maxValue)
    36          y <- (y0 until y0 + 3).toList) yield game(x + y * maxValue)
    31 }         
    37 }         
    32 
    38 
    33 
    39 
    34 //get_row(game0, 0)
       
    35 //get_row(game0, 1)
       
    36 //get_box(game0, (3,1))
       
    37 
       
    38 def update(game: String, pos: Int, value: Char): String = 
    40 def update(game: String, pos: Int, value: Char): String = 
    39   game.updated(pos, value)
    41   game.updated(pos, value)
    40 
    42 
    41 def toAvoid(game: String, pos: Pos): List[Char] = 
    43 def toAvoid(game: String, pos: Pos): List[Char] = 
    42   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
    44   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
    43 
    45 
    44 def candidates(game: String, pos: Pos): List[Char] = 
    46 def candidates(game: String, pos: Pos): List[Char] = 
    45   allValues.diff(toAvoid(game, pos))
    47   allValues.diff(toAvoid(game, pos))
    46 
       
    47 //candidates(game0, (0, 0))
       
    48 
       
    49 //def pretty(game: String): String = 
       
    50 //  "\n" ++ (game.sliding(maxValue, maxValue).mkString("\n"))
       
    51 
    48 
    52 def search(game: String): List[String] = {
    49 def search(game: String): List[String] = {
    53   if (isDone(game)) List(game)
    50   if (isDone(game)) List(game)
    54   else 
    51   else 
    55     candidates(game, emptyPosition(game)).par.
    52     candidates(game, emptyPosition(game)).par.
    56       map(c => search(update(game, empty(game), c))).toList.flatten
    53       map(c => search(update(game, empty(game), c))).toList.flatten
    57 }
    54 }
    58 
    55 
    59 // two easy games
    56 // a list of hard games according to Andrew Coles and Peter Norvig
    60 val game0 = """.14.6.3..
       
    61               |62...4..9
       
    62               |.8..5.6..
       
    63               |.6.2....3
       
    64               |.7..1..5.
       
    65               |5....9.6.
       
    66               |..6.2..3.
       
    67               |1..5...92
       
    68               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
       
    69 
       
    70 val game1 = """23.915...
       
    71               |...2..54.
       
    72               |6.7......
       
    73               |..1.....9
       
    74               |89.5.3.17
       
    75               |5.....6..
       
    76               |......9.5
       
    77               |.16..7...
       
    78               |...329..1""".stripMargin.replaceAll("\\n", "")
       
    79 
       
    80 
       
    81 // a game that is in the sligtly harder category
       
    82 val game2 = """8........
       
    83               |..36.....
       
    84               |.7..9.2..
       
    85               |.5...7...
       
    86               |....457..
       
    87               |...1...3.
       
    88               |..1....68
       
    89               |..85...1.
       
    90               |.9....4..""".stripMargin.replaceAll("\\n", "")
       
    91 
       
    92 // a game with multiple solutions
       
    93 val game3 = """.8...9743
       
    94               |.5...8.1.
       
    95               |.1.......
       
    96               |8....5...
       
    97               |...8.4...
       
    98               |...3....6
       
    99               |.......7.
       
   100               |.3.5...8.
       
   101               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   102 
    57 
   103 val hard_games = 
    58 val hard_games = 
   104   List("85...24..72......9..4.........1.7..23.5...9...4...........8..7..17..........36.4.",
    59   List("4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......",
       
    60        "52...6.........7.13...........4..8..6......5...........418.........3..2...87.....",
       
    61        "6.....8.3.4.7.................5.4.7.3..2.....1.6.......2.....5.....8.6......1....",
       
    62        "48.3............71.2.......7.5....6....2..8.............1.76...3.....4......5....",
       
    63        "....14....3....2...7..........9...3.6.1.............8.2.....1.4....5.6.....7.8...",
       
    64        "......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.",
       
    65        "6.2.5.........3.4..........43...8....1....2........7..5..27...........81...6.....",
       
    66        ".524.........7.1..............8.2...3.....6...9.5.....1.6.3...........897........",
       
    67        "6.2.5.........4.3..........43...8....1....2........7..5..27...........81...6.....",
       
    68        ".923.........8.1...........1.7.4...........658.........6.5.2...4.....7.....9.....",
       
    69 
       
    70        "85...24..72......9..4.........1.7..23.5...9...4...........8..7..17..........36.4.",
   105        "..53.....8......2..7..1.5..4....53...1..7...6..32...8..6.5....9..4....3......97..",
    71        "..53.....8......2..7..1.5..4....53...1..7...6..32...8..6.5....9..4....3......97..",
   106        "12..4......5.69.1...9...5.........7.7...52.9..3......2.9.6...5.4..9..8.1..3...9.4",
    72        "12..4......5.69.1...9...5.........7.7...52.9..3......2.9.6...5.4..9..8.1..3...9.4",
   107        "...57..3.1......2.7...234......8...4..7..4...49....6.5.42...3.....7..9....18.....",
    73        "...57..3.1......2.7...234......8...4..7..4...49....6.5.42...3.....7..9....18.....",
   108        "7..1523........92....3.....1....47.8.......6............9...5.6.4.9.7...8....6.1.",
    74        "7..1523........92....3.....1....47.8.......6............9...5.6.4.9.7...8....6.1.",
   109        "1....7.9..3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..",
    75        "1....7.9..3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..",
   207        ".5..9....1.....6.....3.8.....8.4...9514.......3....2..........4.8...6..77..15..6.",
   173        ".5..9....1.....6.....3.8.....8.4...9514.......3....2..........4.8...6..77..15..6.",
   208        ".....2.......7...17..3...9.8..7......2.89.6...13..6....9..5.824.....891..........",
   174        ".....2.......7...17..3...9.8..7......2.89.6...13..6....9..5.824.....891..........",
   209        "3...8.......7....51..............36...2..4....7...........6.13..452...........8..")
   175        "3...8.......7....51..............36...2..4....7...........6.13..452...........8..")
   210 
   176 
   211 
   177 
   212 
       
   213 //search(game0).map(pretty)
       
   214 //search(game1).map(pretty)
       
   215 
       
   216 // for measuring time
   178 // for measuring time
   217 def time_needed[T](i: Int, code: => T) = {
   179 def time_needed[T](i: Int, code: => T) = {
   218   val start = System.nanoTime()
   180   val start = System.nanoTime()
   219   for (j <- 1 to i) code
   181   for (j <- 1 to i) code
   220   val end = System.nanoTime()
   182   val end = System.nanoTime()
   221   s"${(end - start) / i / 1.0e9} secs"
   183   (end - start) / i / 1.0e9
   222 }
   184 }
   223 
   185 
   224 //search(game2).map(pretty)
   186 
   225 //search(game3).distinct.length
   187 val total = 
   226 println(time_needed(10, search(game0)))
   188   (for ((game, i) <- hard_games.zipWithIndex) yield {
   227 println(time_needed(10, search(game1)))
   189     val secs = time_needed(1, search(game))
   228 println(time_needed(4, search(game2)))
   190     println(f"${i}%2.0f: ${game} |${secs}%2.3f secs")
   229 println(time_needed(8, search(game3)))
   191     secs
   230 
   192   }).sum
   231 for (i <- 0 until hard_games.length) {
   193 
   232     println("   " ++ hard_games(i))
   194 println(f"\ntotal: ${total}%.3f secs")
   233     println(s"$i: ${time_needed(1, search(hard_games(i)))}")
   195 
   234 }
   196 }
   235 
   197 
   236 }
   198 
       
   199 
       
   200 // 1 single thread version 800 secs
       
   201 // 4 cores parallel version on moderate laptop 400 secs
       
   202 // 8 cores (4 physical + 4 hyperthread): 290 secs
       
   203 // 36 cores (18 physical + 18 hyperthread): 142 secs
       
   204