Advertisement
roachls

NonogramSolver_updated

Feb 2nd, 2019
299
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 4.49 KB | None | 0 0
  1. package org.roach.nonogramsolver
  2.  
  3. import scala.io.Source
  4. import org.apache.logging.log4j.Logger
  5. import org.apache.logging.log4j.LogManager
  6.  
  7. object NonogramSolver {
  8.   val logger = LogManager.getLogger
  9.  
  10.   def main(args: Array[String]): Unit = {
  11.     val (hsize, vsize, cols, rows) = {
  12.       val lines = Source.fromFile(args(0)).getLines().filter(l => !l.startsWith("#") && l.length != 0).toList
  13.       val hsize = lines(0).toInt
  14.       val vsize = lines(1).toInt
  15.       val cols = lines(2).replace("\",\"", ":").replace("\"","").split(":").map(c => Requirement(c.split(",").map(l => l.toInt).toList)).toList
  16.       val rows = lines(3).replace("\",\"", ":").replace("\"","").split(":").map(r => Requirement(r.split(",").map(l => l.toInt).toList)).toList
  17.       (hsize, vsize, cols, rows)
  18.     }
  19.  
  20.     val filterGrid = Grid(for (col <- cols) yield col.generateFilter(vsize)).inverse
  21.  
  22.     // Generate list of all possible potential rows
  23.     val potentialRowList = for ((row, filter) <- rows zip filterGrid.contents) yield row.generateRows(hsize).filter(r => (r compatibleWith filter) && !r.isErrorRow)
  24.  
  25.     // Generate list of all possible potential columns
  26.     val potentialColList = for (col <- cols) yield col.generateRows(vsize).filter(c => !c.isErrorRow)
  27.  
  28.     val orderToTryRows: List[Int] = (0 to vsize - 1).toList
  29.  
  30.     /*
  31.      * The main engine that solves the given puzzle
  32.      * Substitutes one row into the startGrid and then recursively calls itself
  33.      * with the new startGrid and tries the next row.
  34.      * @param startGrid The solution so far, which starts as all empty cells
  35.      * @param rowList A list of integer lists for each row
  36.      * @param cd The column difference so far.
  37.      * @param rowsToTryNext A list of rows to try for this iteration
  38.      * @param filter The current filter grid
  39.      * @return Some(Grid) which is the solution of the puzzle, or None
  40.      */
  41.     var done = false;
  42.     def solve(
  43.       startGrid: Grid,
  44.       rowList: List[Requirement],
  45.       cd: Int,
  46.       rowsToTryNext: List[Int],
  47.       filter: Grid): Option[Grid] = {
  48.       if (filter.hasErrors) return None
  49.       if (rowsToTryNext != Nil) {
  50.         logger.trace("In solve with " + rowsToTryNext.head)
  51.       }
  52.       rowList match {
  53.         case Nil => Some(startGrid) // If we got this far, then we've solved the puzzle
  54.         case row :: tail =>
  55.           val whichRow = rowsToTryNext.head
  56.           val rowsToTry = potentialRowList(whichRow).filter(r => r compatibleWith filter(whichRow))
  57.           if (rowsToTry == Nil) {
  58.             logger.debug("Stopping because none of " + potentialRowList(whichRow) + " matched " + filter(whichRow) + " for row " + whichRow)
  59.           }
  60.           rowsToTry.foreach { r =>
  61.             // Builds a new grid with r substituted for row #whichRow
  62.             val newGrid = startGrid.updated(whichRow, r)
  63.             val newColumnDiff = newGrid.columnDiff(cols)
  64.             if (cd - newColumnDiff == r.countOs) { // Only try rows where every O counts
  65.               val tGrid = newGrid.inverse
  66.               val allColumnsPossible = (potentialColList, tGrid.contents).zipped.forall(
  67.                 (p, filter) => p exists (_ compatibleWith filter))
  68.               val newFilter = (newGrid & filter).applyRules(cols)
  69.               // Only try rows where all columns are possible
  70.               if (allColumnsPossible) {
  71.                 val solveTail = solve(newGrid, tail, newColumnDiff, rowsToTryNext.tail, newFilter)
  72.                 if (solveTail != None) {
  73.                   return solveTail
  74.                 }
  75.               }
  76.             }
  77.           }
  78.           // Only reach here if all tries are exhausted and didn't work
  79.           logger.trace("\nFinal filter:\n" + filter)
  80.           None
  81.       }
  82.     }
  83.  
  84.      val t1 = System.currentTimeMillis()
  85.     solve(Grid.emptyGrid(hsize, vsize), rows, Grid.emptyGrid(hsize, vsize).columnDiff(cols), orderToTryRows, filterGrid) match {
  86.       case Some(solution) =>
  87.          println("\nSolved!")
  88.          println(s"Rows: $rows")
  89.          println(s"Cols: $cols")
  90.         println(solution)
  91.       case None =>
  92.         println("\n\nRows: " + rows)
  93.         println("Cols: " + cols)
  94.         println("After an exhaustive search I couldn't solve it. :-(")
  95.     }
  96.     lazy val t2 = System.currentTimeMillis()
  97.     val time = (t2 - t1).asInstanceOf[Float]
  98.     if (time <= 1000.0f) println("\n" + time + " msecs")
  99.     else if (time <= 1000000.0f) println("\n" + time / 1000.0f + " secs")
  100.     else println("\n" + time / 60000.0f + " min")
  101.   }
  102. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement