Guest User

Untitled

a guest
Jun 14th, 2018
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -----------------------------
  2. -- Nonogram Solving Module --
  3. -----------------------------
  4.  
  5. module Nonogram
  6. (
  7.     Grid,
  8.     Line,
  9.     Cell(..),
  10.  
  11.     PuzzleDefinition,
  12.     LineDefinition,
  13.     Strip,
  14.  
  15.     solvePuzzle
  16. ) where
  17.  
  18. --
  19. -- Imports
  20. --
  21.  
  22. import Data.List
  23. import Control.Exception
  24.  
  25. --
  26. -- Types
  27. --
  28.  
  29. -- A puzzle definition is consisted of a list of row definitions and column definitions.
  30. -- Note: The length of the rows should be equal to the amount of columns and vice versa.
  31. type PuzzleDefinition = ([LineDefinition], [LineDefinition])
  32.  
  33. -- A line defintion is consited of a list of strips of black cells.
  34. type LineDefinition = [Strip]
  35.  
  36. -- A strip defines the length of the sequence of consequtive black cells in a line.
  37. -- Note: Strips should be positive numbers.
  38. --       There shouldn't be strips of zero length.
  39. type Strip = Int
  40.  
  41. -- A grid is consisted of a list of rows.
  42. type Grid = [Line]
  43.  
  44. -- A line is consisted of a list of cells.
  45. type Line = [Cell]
  46.  
  47. -- Cell is a data type that defines the state of a single cell.
  48. -- Note: The Unknown state could be represented differently,
  49. --       perhaps as a non-deterministic computation (i.e. [White, Black]).
  50. --       This way maybe some of the functions could be expressed more elegantly.
  51. data Cell = Unknown
  52.           | Black
  53.           | White
  54.           deriving (Eq, Show)
  55.  
  56. --
  57. -- Functions
  58. --
  59.  
  60. -- This function takes a puzzle definition and returns the solution grid to that puzzle.
  61. -- If the puzzle cannot be solved without guesses, the resulting grid will contain Unknown cells.
  62. solvePuzzle :: PuzzleDefinition -> Grid
  63. solvePuzzle puzzleDef =
  64.     let (rows, cols) = puzzleDef
  65.         width = length cols
  66.         hight = length rows
  67.         grid = unknownGrid width hight
  68.     in solveGrid puzzleDef grid
  69.  
  70. -- This function takes a grid size (width and hight), and returns a grid filled with Unknown cells.
  71. unknownGrid :: Int -> Int -> Grid
  72. unknownGrid width hight = replicate hight (unknownLine width)
  73.  
  74. -- This function takes a line length, and returns a line filled with Unknown cells.
  75. unknownLine :: Int -> Line
  76. unknownLine lineLength = replicate lineLength Unknown
  77.  
  78. --TODO: refactor?
  79. -- This function takes a puzzle definition and a grid, and returns the solved grid.
  80. -- If the grid cannot be solved without guesses, some cells in the solution will be Unknown.
  81. solveGrid :: PuzzleDefinition -> Grid -> Grid
  82. solveGrid puzzleDef rows =
  83.     let (rowDefs, colDefs) = puzzleDef
  84.         -- Solve grid using row definitions
  85.         solvedRows = zipWith solveLine rowDefs rows
  86.         -- Transpose grid to get the columns
  87.         cols = transpose solvedRows
  88.         -- Solve grid using column definitions
  89.         solvedCols = zipWith solveLine colDefs cols
  90.         -- Transpose grid again to get the rows
  91.         newRows = transpose solvedCols
  92.     in -- If solving the columns didn't improve the solution,
  93.        -- Then we solved the grid as much as possible (without guesses).
  94.        -- Else, continue solving the grid based on the information we obtained
  95.        -- at this step.
  96.        if newRows == solvedRows
  97.        then newRows
  98.        else solveGrid puzzleDef newRows
  99.  
  100. -- This function takes a line definition and a line, and returns the solved line.
  101. -- If the line cannot be completely solved, some cells will remain Unknown.
  102. -- If the line has not possible solution, an error is produced.
  103. solveLine :: LineDefinition -> Line -> Line
  104. solveLine lineDef line =
  105.     let solutions = (lineSolutions lineDef line)
  106.         mergedSolution = foldr1 mergeSolutions solutions
  107.     in if null solutions
  108.        then error "Line has no possible solution"
  109.        else mergedSolution
  110.  
  111. -- This function takes a line definition and a line, and returns all the possible
  112. -- solutions to that line.
  113. lineSolutions :: LineDefinition -> Line -> [Line]
  114. lineSolutions lineDef line = filter (isSolution lineDef) (linePermutaions line)
  115.  
  116. -- This function takes a line definition and a line, and checks whether the line
  117. -- is a solution to the line definition.
  118. isSolution :: LineDefinition -> Line -> Bool
  119. isSolution strips cells =
  120.     let blocks = group cells
  121.         blackBlocks = filter ((==Black) . head) blocks
  122.         blackBlocksLengths = map length blackBlocks
  123.     in blackBlocksLengths == strips
  124.  
  125. -- This function takes a line, and returns all possible permutations of that line.
  126. -- I.E. for every Unknown cell, it produces a line with that cell White, and a
  127. -- line with that cell Black.
  128. linePermutaions :: Line -> [Line]
  129. linePermutaions (Unknown:cells) =
  130.     let otherLinePermutations = linePermutaions cells
  131.         unknownIsBlackPremutations = map (Black:) otherLinePermutations
  132.         unknownIsWhitePremutations = map (White:) otherLinePermutations
  133.     in unknownIsBlackPremutations ++ unknownIsWhitePremutations
  134. linePermutaions (knownCell:cells) = map (knownCell:) (linePermutaions cells)
  135. linePermutaions [] = [[]]
  136.  
  137. -- This function takes two lines (that are possible solutions to a line definition),
  138. -- and returns a line that is a merge of these solutions.
  139. -- Merging solutions keeps cells that are equal in both solutions.
  140. -- Unknown cells are placed instead of cells that are not equal.
  141. mergeSolutions :: Line -> Line -> Line
  142. mergeSolutions a b = zipWith keepCellIfEqual a b
  143.  
  144. -- This function takes two cells, and returns the first if they are equal,
  145. -- otherwise it returns an Unknown cell.
  146. keepCellIfEqual :: Cell -> Cell -> Cell
  147. keepCellIfEqual a b =
  148.     if a == b
  149.     then a
  150.     else Unknown
Add Comment
Please, Sign In to add comment