never executed always true always false
    1 {-|
    2 Module:      Y2021.D13
    3 Description: Advent of Code 2021 Day 13 Solutions.
    4 License:     MIT
    5 Maintainer:  @tylerjl
    6 
    7 Solutions to the 2021 day 13 set of problems for <adventofcode.com>.
    8 -}
    9 module Y2021.D13
   10   ( parse13
   11   , part13A
   12   , part13B
   13   ) where
   14 
   15 import Advent.OCR
   16 import Control.Applicative
   17 import Data.Attoparsec.Text hiding (take, takeWhile)
   18 import Data.Bifunctor              (second, first)
   19 import Data.Either.Utils           (fromRight)
   20 import Data.Foldable
   21 import Data.List.Extra             (transpose)
   22 import Data.Set                    (Set)
   23 import Data.Text                   (Text)
   24 import Data.Tuple.Extra            ((***))
   25 
   26 import qualified Data.Set as S
   27 
   28 -- |Type alias for better readability.
   29 type Point = (Int, Int)
   30 -- |Pretty simple; which axis to fold across.
   31 data Axis = X | Y deriving (Eq, Show)
   32 -- |A fold instruction from the problem set input.
   33 data FoldGuide = FoldAt Axis Int deriving Show
   34 
   35 -- |Solution to part A
   36 part13A :: Text -> Int
   37 part13A = S.size . uncurry (foldl' origami) . second (take 1) . parse13
   38 
   39 -- |Solution to part B
   40 part13B :: Text -> Maybe String
   41 part13B = parseLetters . uncurry (foldl' origami) . parse13
   42 
   43 -- |I wrote this one up to render output, but using an upstream parser gets
   44 -- actual letters, so this might come later but is unused for now.
   45 _displayPoints :: Set Point -> String
   46 _displayPoints points = unlines $ transpose $ map render [0..rows]
   47   where
   48     grid = S.toList points
   49     (rows, cols) = (maximum *** maximum) $ unzip grid
   50     render r = map (curry display r) [0..cols]
   51     display point | S.member point points = '#'
   52                   | otherwise = ' '
   53 
   54 -- |Central function that applies a "fold" to a set of points.
   55 origami :: Set Point -> FoldGuide -> Set Point
   56 origami points (FoldAt axis crease)
   57   = S.foldl' pointFold S.empty points
   58   where
   59     pointFold paper point@(x, y)
   60       | axis == X && x < crease = S.insert point paper
   61       | axis == X = S.insert (crease - (x - crease), y) paper
   62       | axis == Y && y < crease = S.insert point paper
   63       | otherwise = S.insert (x, crease - (y - crease)) paper
   64 
   65 -- |The parsing entrypoint turns puzzle input into the final form of a set of
   66 -- points.
   67 parse13 :: Text -> (Set Point, [FoldGuide])
   68 parse13 = first (foldl' (flip S.insert) S.empty) . parse13'
   69 
   70 -- |Day 13 input is a list of points followed by fold instructions. This
   71 -- intermediate function gets the raw values before putting them into a `Set`.
   72 parse13' :: Text -> ([Point], [FoldGuide])
   73 parse13' = fromRight . parseOnly (parser <* endOfInput)
   74   where
   75     parser = (,) <$> points <* endOfLine <*> foldInstr `sepBy1` endOfLine <* endOfLine
   76     points = point `sepBy1` endOfLine <* endOfLine
   77     point = (,) <$> decimal <* char ',' <*> decimal
   78     foldInstr = FoldAt <$> (string "fold along " *> axis) <*> (char '=' *> decimal)
   79     axis = (X <$ char 'x') <|> (Y <$ char 'y')