Advertisement
roachls

NonogramSolver

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