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')