Nonogram Solver in Copris
- Top
- Akari
- Fillomino
- Hakyuu
- Hashiwokakero
- Heyawake
- Hitori
- Kakuro
- Kurodoko
- Masyu
- Nonogram
- Numberlink
- Nurikabe
- Polarium
- Shakashaka
- Shikaku
- Slitherlink
- Sokoban
- Sudoku
- Yajilin
Table of Contents
Overview
This page presents a Nonogram solver written in Copris, a Constraint Programming DSL (Domain-Specific Language) embedded in Scala. It can solve a very large puzzle, such as Warship (100 x 100) by P. R. H. Ainsworth available from http://www.comp.lancs.ac.uk/~ss/nonogram/puzzles.
What's New
- Release of version 2.0
- Release of version 1.2
- Version number is added.
- "N-Dom Puzzles" section is added.
- "Constraint Model" section is added.
- First release
Download
- Version 2.0
-
- Scala jar file: ../build/copris-puzzles-2.0.jar (1575981 bytes)
- Source file: ../build/copris-puzzles-src-2.0.zip (29766 bytes)
- Multicolor version
-
- Please visit Multicolor Nonogram Solver in Copris page by Jan Wolter.
Requirements
How to use
scala -cp copris-puzzles-2.0.jar nonogram.Solver cwd_file_name
- CWD format file can be read by the solver.
- Visit Web Paint-by-Number Puzzle Export page by Jan Wolter to download CWD format files.
- The program also checks the uniqueness of the solution.
For some large puzzles, you need to specify an option for larger JVM heap space.
scala -cp copris-puzzles-2.0.jar -J-Xmx2g nonogram.Solver cwd_file_name
If you have a SAT solver (such as MiniSat, GlueMiniSat) installed on your computer, you can use it to get better performance. Specify the full path of the SAT solver program with "-s1" or "-s2" option.
scala -cp copris-puzzles-2.0.jar nonogram.Solver -s2 minisat cwd_file_name
Example Usage
$ scala -cp copris-puzzles-2.0.jar nonogram.Solver webpbn-00006.cwd ..##................ .##................. .#.................. .#.................. .#.....###.......... ##....#####......... #....#######...#...# #....########..##.## #...#########..##### ##..################ .#.################# .#######.########### ..#####...#####.###. ..#####...####...... ...###.....###...... ...##......##....... ..##........#....... ..#.........#....... ..##........##...... ..##........##...... Unique solution
Performance Evaluation
Solver performance is measured on version 1.1 in 2013.
Performance Comparison
The performance of many Nonogram solvers is reporeted in Survey of Paint-by-Number Puzzle Solvers by Jan Wolter. His web page is very well written with complehensive evalution and analysis of Nonogram solvers.
Puzzle | Size | Copris | Wu | Syromolotov | Wolter | Olsak | Simpson | BGU | Lager-kvist | Kjeller-strand | Kjeller-strand |
---|---|---|---|---|---|---|---|---|---|---|---|
/Gecode | /Gecode | /Lazyfd | |||||||||
#1: Dancer | 5 x 10 | 0.61 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.06 | 0.01 | 0.01 | 0.04 |
#6: Cat | 20 x 20 | 2.33 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.08 | 0.01 | 0.02 | 0.44 |
#21: Skid | 14 x 25 | 2.24 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.08 | 0.01 | 0.02 | 0.64 |
#27: Buck | 27 x 23 | 3.87 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.16 | 0.01 | 0.02 | 1.10 |
#23: Edge | 10 x 11 | 0.60 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.09 | 0.01 | 0.01 | 0.08 |
#2413: Smoke | 20 x 20 | 3.17 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.19 | 0.01 | 0.02 | 0.60 |
#16: Knot | 34 x 34 | 5.43 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.20 | 0.01 | 0.02 | 5.50 |
#529: Swing | 45 x 45 | 5.74 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.25 | 0.02 | 0.04 | 13.20 |
#65: Mum | 34 x 40 | 5.43 | 0.00 | 0.01 | 0.01 | 0.00 | 0.01 | 0.26 | 0.02 | 0.04 | 11.00 |
#7604: DiCap | 52 x 63 | 10.37 | 0.01 | 0.11 | 0.02 | 0.00 | 0.00 | 2.10 | 0.02 | 0.06 | + |
#1694: Tragic | 45 x 50 | 6.11 | 0.02 | 0.02 | 0.04 | 0.03 | 0.54 | 0.62 | 0.14 | 216.00 | 32.10 |
#1611: Merka | 55 x 60 | 8.42 | 0.02 | 0.01 | 0.01 | 0.01 | 0.00 | 0.35 | 0.03 | + | 66.00 |
#436: Petro | 40 x 35 | 5.05 | 0.01 | 0.04 | 0.06 | 15.20 | 0.10 | 0.58 | 84.00 | 1.60 | 6.20 |
#4645: M&M | 50 x 70 | 6.90 | - | 0.07 | 0.07 | 0.10 | 0.02 | 0.84 | 0.93 | 0.28 | 48.10 |
#3541: Signed | 60 x 50 | 7.37 | 0.07 | 0.09 | 0.04 | 1.10 | 324.00 | 0.68 | 0.57 | 0.56 | 60.00 |
#803: Light | 50 x 45 | 4.71 | 0.20 | 0.19 | 0.38 | + | 0.02 | 1.10 | + | + | 4.70 |
#6574: Forever | 25 x 25 | 3.70 | 0.34 | 13.90 | 3.70 | 2.00 | 18.90 | 44.30 | 4.70 | 2.30 | 1.70 |
#10810: Center | 60 x 60 | 12.94 | 5.40 | 0.05 | 8.60 | 0.00 | 0.01 | 0.86 | 0.04 | 0.07 | 27.80 |
#2040: Hot | 55 x 60 | 8.90 | 0.11 | 0.06 | 0.83 | + | 72.00 | 0.93 | + | + | 156.00 |
#6739: Karate | 40 x 40 | 6.44 | 0.06 | 0.03 | 0.80 | 17.30 | 1098.00 | 0.61 | 56.00 | 57.80 | 13.60 |
#8098: 9-Dom | 19 x 19 | 3.01 | 20.30 | 11.70 | 11.00 | 240.00 | + | 168.00 | 756.00 | 3.80 | 1.80 |
#2556: Flag | 65 x 45 | 39.07 | - | 0.02 | 0.55 | 1.50 | 0.00 | 24.20 | 3.40 | + | 4.20 |
#2712: Lion | 47 x 47 | 11.29 | 53.40 | 24.80 | 6.30 | + | + | 7.80 | + | + | + |
#10088: Marley | 52 x 63 | 15.49 | 54.00 | 4.20 | + | + | + | 22.00 | + | + | 174.00 |
#18297: Thing | 36 x 42 | 26.03 | 504.00 | + | 402.00 | + | + | 120.00 | + | + | 216.00 |
#9892: Nature | 50 x 40 | 38.58 | 108.00 | + | + | 1116.00 | + | + | + | + | 144.00 |
#12548: Sierp | 47 x 40 | 49.19 | + | 132.00 | + | + | + | + | + | + | + |
- The above table is constructed from the reported results in Jan Wolter's page updated on Wed Nov 21 19:38:58 PST 2012. The results of Copris solver is added to his results. Note that Copris is run on a faster machine.
- The table shows the CPU time in seconds of each solver for each instance.
- The CPU time includes the time for "uniqueness checking".
- Copris solver was run on Intel Xeon 3.47GHz machine with:
- Ubuntu Linux 12.04
- Java version "1.6.0_27", OpenJDK Runtime Environment
- Scala version 2.9.1
- Copris version 2.0.0
- Sugar version 2.0.0
- Sat4j version 2.3.3
- Other solvers were run on AMD Phenom II X4 810 2.6GHz machine.
- "+" means the timeout (1800 seconds), and "-" means memory over.
- log/nonogram-bach-sat4j233.log contains detailed results of Copris.
Note that Copris is the only solver which can solve all instances. The speed of Copris can be several times faster if you use other SAT solver such as Glucose, MiniSat, etc.
Results of Using Other SAT Solvers
The following table shows the results when other SAT solvers are used. The speed becomes faster than using Sat4j, but there are no big differences among other SAT solvers.
Puzzle | Size | Sat4j | Clasp | Glucose | GlueMiniSat | Lingeling | MiniSat | PrecoSAT |
---|---|---|---|---|---|---|---|---|
#1: Dancer | 5 x 10 | 0.61 | 0.49 | 0.53 | 0.52 | 0.43 | 0.53 | 0.42 |
#6: Cat | 20 x 20 | 2.33 | 2.02 | 2.03 | 2.01 | 2.21 | 2.02 | 2.04 |
#21: Skid | 14 x 25 | 2.24 | 2.01 | 2.03 | 2.00 | 1.84 | 1.72 | 2.03 |
#27: Buck | 27 x 23 | 3.87 | 3.51 | 3.14 | 3.49 | 3.29 | 3.55 | 3.17 |
#23: Edge | 10 x 11 | 0.60 | 0.60 | 0.60 | 0.60 | 0.60 | 0.60 | 0.60 |
#2413: Smoke | 20 x 20 | 3.17 | 2.82 | 2.80 | 2.81 | 2.55 | 2.71 | 2.80 |
#16: Knot | 34 x 34 | 5.43 | 4.36 | 4.22 | 4.18 | 4.50 | 4.17 | 0.00 |
#529: Swing | 45 x 45 | 5.74 | 5.42 | 5.11 | 5.09 | 5.21 | 5.09 | 5.45 |
#65: Mum | 34 x 40 | 5.43 | 4.67 | 4.47 | 4.40 | 5.24 | 4.39 | 4.73 |
#7604: DiCap | 52 x 63 | 10.37 | 8.44 | 7.03 | 6.73 | 8.08 | 7.02 | 7.16 |
#1694: Tragic | 45 x 50 | 6.11 | 5.43 | 5.23 | 5.19 | 5.50 | 5.09 | 5.20 |
#1611: Merka | 55 x 60 | 8.42 | 7.88 | 7.27 | 7.19 | 7.80 | 0.00 | 7.56 |
#436: Petro | 40 x 35 | 5.05 | 0.00 | 3.90 | 3.88 | 4.15 | 4.10 | 0.00 |
#4645: M&M | 50 x 70 | 6.90 | 6.65 | 5.95 | 6.09 | 7.16 | 5.54 | 6.20 |
#3541: Signed | 60 x 50 | 7.37 | 6.54 | 5.61 | 6.05 | 7.04 | 5.66 | 6.07 |
#803: Light | 50 x 45 | 4.71 | 4.69 | 4.15 | 3.90 | 5.37 | 4.04 | 4.47 |
#6574: Forever | 25 x 25 | 3.70 | 3.34 | 3.28 | 2.96 | 3.43 | 3.15 | 3.37 |
#10810: Center | 60 x 60 | 12.94 | 12.23 | 12.22 | 12.30 | 13.34 | 12.21 | 12.54 |
#2040: Hot | 55 x 60 | 8.90 | 7.63 | 7.13 | 6.76 | 8.32 | 6.47 | 7.23 |
#6739: Karate | 40 x 40 | 6.44 | 5.36 | 5.21 | 5.29 | 5.87 | 5.24 | 5.41 |
#8098: 9-Dom | 19 x 19 | 3.01 | 2.30 | 2.20 | 2.07 | 2.83 | 2.07 | 2.32 |
#2556: Flag | 65 x 45 | 39.07 | 4.28 | 4.96 | 4.34 | 4.33 | 5.44 | 4.25 |
#2712: Lion | 47 x 47 | 11.29 | 7.88 | 7.28 | 7.09 | 8.24 | 6.93 | 5.97 |
#10088: Marley | 52 x 63 | 15.49 | 28.03 | 8.53 | 8.32 | 15.86 | 8.35 | 9.69 |
#18297: Thing | 36 x 42 | 26.03 | 9.27 | 9.32 | 8.71 | 10.59 | 11.27 | 8.73 |
#9892: Nature | 50 x 40 | 38.58 | 9.82 | 10.29 | 12.20 | 13.43 | 8.87 | 9.74 |
#12548: Sierp | 47 x 40 | 49.19 | 45.70 | 16.70 | 35.40 | 21.25 | 26.25 | 21.33 |
Average | 10.85 | 7.46 | 5.60 | 6.28 | 6.61 | 5.65 | 5.50 |
- The table shows the CPU time in seconds of Copris with each SAT solver for each instance.
- Sat4j version 2.3.3
- Clasp version 2.1.1
- Glucose version 2.1 (without preprocessing)
- GlueMiniSat version 2.2.5 (without preprocessing)
- Lingeling version 587f
- MiniSat version 2.2 (without preprocessing)
- PrecoSAT version 576
Large Puzzles
Copris can solve very large puzzles.
Puzzle | Size | Sat4j | Clasp | Glucose | GlueMiniSat | Lingeling | MiniSat | PrecoSAT |
---|---|---|---|---|---|---|---|---|
Artist | 75 x 100 | 9.22 | 8.78 | 8.64 | 8.58 | 8.70 | 8.57 | 8.89 |
Balance | 100 x 75 | 8.72 | 8.28 | 8.01 | 8.20 | 8.37 | 8.01 | 8.49 |
Warship | 100 x 100 | 10.75 | 11.75 | 9.42 | 9.42 | 9.65 | 9.36 | 10.05 |
#22336: Gettys | 99 x 59 | 323.08 | 111.72 | 64.06 | 55.38 | 73.90 | 89.70 | 69.73 |
Average | 87.94 | 35.13 | 22.53 | 20.40 | 25.16 | 28.91 | 24.29 |
- The table shows the CPU time in seconds of Copris with each SAT solver for each instance.
- "balance", "artist", and "warship" instances can be found at http://www.comp.lancs.ac.uk/~ss/nonogram/puzzles by Steven Simpson.
- "#22336: Gettys" is avaiable from Web Paint-by-Number Puzzle Export page maintained by Jan Wolter.
N-Dom Puzzles
The following table shows the performance of Copris to solve the n-Dom puzzles explained in Comparison of Solvers on the n-Dom Puzzles by Jan Wolter.
n | Sat4j | Clasp | Glucose | GlueMiniSat | Lingeling | MiniSat | PrecoSAT |
---|---|---|---|---|---|---|---|
5 | 0.82 | 0.78 | 0.63 | 0.76 | 0.78 | 0.77 | 0.64 |
6 | 1.13 | 0.85 | 0.86 | 0.86 | 1.17 | 0.86 | 0.85 |
7 | 1.42 | 1.12 | 1.13 | 1.11 | 1.37 | 1.11 | 1.13 |
8 | 2.13 | 1.66 | 1.65 | 1.66 | 2.18 | 1.66 | 1.70 |
9 | 2.91 | 2.05 | 2.04 | 2.04 | 2.94 | 2.28 | 2.33 |
10 | 4.12 | 2.66 | 2.77 | 2.63 | 3.56 | 2.63 | 2.68 |
11 | 4.92 | 2.78 | 2.79 | 3.02 | 3.83 | 2.64 | 2.81 |
12 | 7.52 | 3.69 | 3.82 | 3.65 | 5.53 | 3.93 | 3.82 |
13 | 11.73 | 4.37 | 4.91 | 4.61 | 7.63 | 4.87 | 4.77 |
14 | 16.16 | 4.90 | 5.76 | 6.17 | 8.28 | 7.34 | 7.21 |
15 | 21.58 | 9.90 | 9.45 | 8.79 | 14.89 | 16.86 | 8.26 |
16 | 52.48 | 18.62 | 16.39 | 13.40 | 21.85 | 17.08 | 19.21 |
17 | 75.98 | 37.00 | 20.38 | 23.75 | 35.51 | 36.02 | 37.39 |
18 | 150.09 | 67.40 | 37.00 | 33.29 | 51.89 | 88.05 | 48.35 |
19 | 278.08 | 120.82 | 53.66 | 53.95 | 93.07 | 82.35 | 93.74 |
20 | 429.95 | 170.01 | 82.17 | 78.03 | 154.15 | 195.80 | 141.90 |
Average | 66.31 | 28.04 | 15.34 | 14.86 | 25.54 | 29.02 | 23.55 |
- The table shows the CPU time in seconds of Copris with each SAT solver for each instance.
- For clasp, "–configuration=jumpy" option is used.
Constraint Model
Input
- \(m\) : number of rows
- \(n\) : number of columns
- \(rows(i)(k)\) : run length of the \(k\)-th block in the \(i\)-th row (\(i=0 .. m-1\))
- \(cols(j)(k)\) : run length of the \(k\)-th block in the \(j\)-th column (\(j=0 .. n-1\))
CSP Variables
- \(x(i,j) \in \{0, 1\}\): color of \((i,j)\) cell (0: white, 1: black)
- \(r(i,k) \in \{0 .. n-rows(i)(k)\}\): left-most position of the \(k\)-th block in the \(i\)-th row
- \(c(j,k) \in \{0 .. m-cols(j)(k)\}\): top-most position of the \(k\)-th block in the \(j\)-th column
Constraints
- Blocks are not overlapped each other.
- \(r(i,k) + rows(i)(k) < r(i,k+1)\)
- \(c(j,k) + cols(j)(k) < c(j,k+1)\)
- The \((i,j)\) cell is black iff it is contained in some block.
- \(x(i,j)=1 \Leftrightarrow \bigvee_k (r(i,k) \le j \;\land\; j < r(i,k)+rows(i,k))\)
- \(x(i,j)=1 \Leftrightarrow \bigvee_k (c(j,k) \le i \;\land\; i < c(j,k)+cols(j,k))\)
Source Code
The following shows the source code of the solver (Nonogram.scala).
1: /* 2: * Nonogram Solver in Copris 3: * by Naoyuki Tamura 4: * http://bach.istc.kobe-u.ac.jp/copris/puzzles/nonogram/ 5: */ 6: package nonogram 7: import jp.kobe_u.copris._ 8: import jp.kobe_u.copris.dsl._ 9: import scala.io.Source 10: 11: object Solver { 12: var m = 0 13: var n = 0 14: var rows: Seq[Seq[Int]] = Seq.empty 15: var cols: Seq[Seq[Int]] = Seq.empty 16: def parse(source: Source) = { 17: val lines = source.getLines.map(_.trim) 18: m = lines.next.toInt 19: n = lines.next.toInt 20: rows = for (i <- 0 until m; s = lines.next) 21: yield if (s == "") Seq.empty 22: else s.split("\\s+").toSeq.map(_.toInt) 23: lines.next 24: cols = for (j <- 0 until n; s = lines.next) 25: yield if (s == "") Seq.empty 26: else s.split("\\s+").toSeq.map(_.toInt) 27: source.close 28: } 29: def define = { 30: for (i <- 0 until m; j <- 0 until n) 31: int('x(i,j), 0, 1) 32: for (i <- 0 until m; k <- 0 until rows(i).size) 33: int('r(i,k), 0, n-rows(i)(k)) 34: for (i <- 0 until m; k <- 0 until rows(i).size-1) 35: add('r(i,k) + rows(i)(k) < 'r(i,k+1)) 36: for (j <- 0 until n; k <- 0 until cols(j).size) 37: int('c(j,k), 0, m-cols(j)(k)) 38: for (j <- 0 until n; k <- 0 until cols(j).size-1) 39: add('c(j,k) + cols(j)(k) < 'c(j,k+1)) 40: for (i <- 0 until m; j <- 0 until n) { 41: val rs = for (k <- 0 until rows(i).size) 42: yield 'r(i,k) <= j && 'r(i,k) + rows(i)(k) > j 43: add('x(i,j) > 0 <==> Or(rs)) 44: val cs = for (k <- 0 until cols(j).size) 45: yield 'c(j,k) <= i && 'c(j,k) + cols(j)(k) > i 46: add('x(i,j) > 0 <==> Or(cs)) 47: } 48: } 49: def showSolution = { 50: for (i <- 0 until m) { 51: val xs = for (j <- 0 until n) 52: yield if (solution('x(i,j)) == 0) "." else "#" 53: println(xs.mkString) 54: } 55: } 56: def main(args: Array[String]) = { 57: val name = "nonogram.Solver" 58: var help = false 59: var quiet = false 60: def parseOptions(args: List[String]): List[String] = args match { 61: case "-h" :: rest => 62: { help = true; parseOptions(rest) } 63: case "-s1" :: solver :: rest => 64: { use(new sugar.Solver(csp, new sugar.SatSolver1(solver))); parseOptions(rest) } 65: case "-s2" :: solver :: rest => 66: { use(new sugar.Solver(csp, new sugar.SatSolver2(solver))); parseOptions(rest) } 67: case "-smt" :: solver :: rest => 68: { use(new smt.Solver(csp, new smt.SmtSolver(solver))); parseOptions(rest) } 69: case "-jsr331" :: rest => 70: { use(new jsr331.Solver(csp)); parseOptions(rest) } 71: case "-pb" :: solver :: encoding :: rest => 72: { use(new pb.Solver(csp, new pb.PbSolver(solver), encoding)); parseOptions(rest) } 73: case "-q" :: rest => 74: { quiet = true; parseOptions(rest) } 75: case _ => 76: args 77: } 78: parseOptions(args.toList) match { 79: case Nil if ! help => 80: parse(Source.stdin) 81: case file :: Nil if ! help => 82: parse(Source.fromFile(file)) 83: case _ => { 84: println("Usage: scala " + name + " [options] [inputFile]") 85: println("\t-h : Help") 86: println("\t-q : Quiet output") 87: println("\t-s1 solver : Use SAT solver (minisat, etc.)") 88: println("\t-s2 solver : Use SAT solver (precosat, etc.)") 89: println("\t-smt solver : Use SMT solver (z3, etc.)") 90: println("\t-jsr331 : Use JSR331 solver") 91: println("\t-pb solver encoding : Use PB solver (clasp, etc.)") 92: System.exit(1) 93: } 94: } 95: define 96: if (find) { 97: if (! quiet) 98: showSolution 99: if (findNext) 100: println("Multiple solutions") 101: else 102: println("Unique solution") 103: } 104: } 105: }
License
This software is distributed under the BSD 3-Clause License. See LICENSE.
Links
- Copris (Constraint Programming DSL embedded in Scala)
- Sugar (an award winning Constraint Solver)
- Default constraint solver used in Copris
- Sat4j (SAT solver in Java)
- Default SAT solver used in Copris
- Wikipedia: Nonogram
- Survey of Paint-by-Number Puzzle Solvers (by Jan Wolter)
- Nonogram Solver (by Steven Simpson)
- Update on Nonogram (by Hakan Kjellerstrand)
- http://www.janko.at/Raetsel/Nonogramme/