never executed always true always false
    1 {-|
    2 Module:      Y2015.D06
    3 Description: Advent of Code Day 06 Solutions.
    4 License:     MIT
    5 Maintainer:  @tylerjl
    6 
    7 Solutions to the day 06 set of problems for <adventofcode.com>.
    8 -}
    9 module Y2015.D06
   10   ( testA
   11   , testB
   12   , Instruction(..)
   13   , Range(..)
   14   , parseInstructions
   15   , configureGridA
   16   , configureGridB
   17   , lightSimulation
   18   ) where
   19 
   20 import Control.Applicative ((<|>))
   21 import Data.List (foldl')
   22 
   23 import qualified Data.Array.Repa as R
   24 import Data.Array.Repa (Z(..), (:.)(..))
   25 import qualified Text.Parsec as P
   26 import Text.Parsec.Char (char, endOfLine)
   27 import Text.Parsec.String (Parser)
   28 import Data.Vector.Unboxed.Base (Unbox)
   29 
   30 import Y2015.Util (regularParse, intParser)
   31 
   32 type Point = (Int, Int)
   33 
   34 -- |Represents a two-dimensional range of lights.
   35 data Range =
   36   Range Point
   37         Point
   38   deriving (Eq, Show)
   39 
   40 -- |Type of light grid instruction.
   41 data Instruction
   42   = On Range
   43   | Off Range
   44   | Toggle Range
   45   deriving (Show)
   46 
   47 size :: Int
   48 size = 1000
   49 
   50 initialGrid :: R.Array R.U R.DIM2 Int
   51 initialGrid =
   52   R.fromListUnboxed (Z :. size :. size :: R.DIM2) (replicate (size * size) 0)
   53 
   54 instructionsParser :: Parser [Instruction]
   55 instructionsParser = P.many (instruction <* P.optional endOfLine)
   56 
   57 instruction :: Parser Instruction
   58 instruction = On <$> directive "turn on"
   59           <|> Off <$> directive "turn off"
   60           <|> Toggle <$> directive "toggle"
   61 
   62 directive :: String -> Parser Range
   63 directive s = P.skipMany1 (P.try (P.string s *> P.skipMany1 P.space)) *> range
   64 
   65 range :: Parser Range
   66 range = Range <$> point <* P.string " through " <*> point
   67 
   68 point :: Parser Point
   69 point = (,) <$> intParser <* char ',' <*> intParser
   70 
   71 -- |Folding function to aggregate computation for 'Instruction's per part
   72 -- |A spec.
   73 configureGridA
   74   :: R.Array R.U R.DIM2 Int -- ^ Light grid.
   75   -> Instruction -- ^ Operation 'Instruction'.
   76   -> R.Array R.U R.DIM2 Int -- ^ Resultant light grid.
   77 configureGridA a (On r) = switch a (const 1) r
   78 configureGridA a (Off r) = switch a (const 0) r
   79 configureGridA a (Toggle r) = switch a toggle r
   80 
   81 -- |Folding function to aggregate computation for 'Instruction's per part
   82 -- |B spec.
   83 configureGridB
   84   :: R.Array R.U R.DIM2 Int -- ^ Light grid.
   85   -> Instruction -- ^ Operation 'Instruction'.
   86   -> R.Array R.U R.DIM2 Int -- ^ Resultant light grid.
   87 configureGridB a (On r) = switch a (+ 1) r
   88 configureGridB a (Off r) = switch a dim r
   89 configureGridB a (Toggle r) = switch a (+ 2) r
   90 
   91 toggle :: Int -> Int
   92 toggle 1 = 0
   93 toggle _ = 1
   94 
   95 dim :: Int -> Int
   96 dim = max 0 . subtract 1
   97 
   98 switch
   99   :: (R.Source r a, Unbox a)
  100   => R.Array r R.DIM2 a -> (a -> a) -> Range -> R.Array R.U R.DIM2 a
  101 switch a f r = R.computeS $ R.traverse a id (set f r)
  102 
  103 -- This is pretty confusing:
  104 --    Custom mapping function (set the lights)
  105 -- -> Range to apply the function upon
  106 -- -> Function to retrieve original elements from
  107 -- -> Original array constructor
  108 -- -> New (or unchanged) value
  109 set :: (a -> a) -> Range -> (R.DIM2 -> a) -> R.DIM2 -> a
  110 set f (Range (x', y') (x'', y'')) g (Z :. x :. y)
  111   | withinX && withinY = f orig
  112   | otherwise = orig
  113   where
  114     withinX = x >= x' && x <= x''
  115     withinY = y >= y' && y <= y''
  116     orig = g (Z :. x :. y)
  117 
  118 -- |Execute 'Instruction' and return number of lit lights per part A spec.
  119 testA
  120   :: Instruction -- ^ Given 'Instruction'.
  121   -> Int -- ^ Number of lit lights.
  122 testA = R.foldAllS (+) 0 . configureGridA initialGrid
  123 
  124 -- |Execute 'Instruction' and return number of lit lights per part B spec.
  125 testB
  126   :: Instruction -- ^ Given 'Instruction'
  127   -> Int -- ^ Number of lit lights.
  128 testB = R.foldAllS (+) 0 . configureGridB initialGrid
  129 
  130 -- |Parses a string into a list of 'Instruction's.
  131 parseInstructions
  132   :: String -- ^ Input string to parse.
  133   -> [Instruction] -- ^ Either an error or parsed structure.
  134 parseInstructions = either err suc . regularParse instructionsParser
  135   where err = error . show
  136         suc r = r
  137 
  138 -- |Run a light simulation
  139 lightSimulation
  140   :: Foldable t
  141   => (R.Array R.U R.DIM2 Int -> a -> R.Array R.U R.DIM2 Int) -- ^ REPA Light grid
  142   -> t a -- ^ 'Instruction's
  143   -> Int -- ^ Lit lights
  144 lightSimulation f = R.sumAllS . foldl' f initialGrid