# Advent of Code 2021 day 13

The complexity in day 13 came from an unexpected place.

I continued my trend of created possibly too many data types, with explicit data types for everything I could think of (emphasising the "parse, don't validate" mantra). I use V2 from linear for representing points, and have bespoke data types for both folds and the axis of a fold.

type Coord = V2 Int
type Sheet = S.Set Coord

data Axis = X | Y
deriving (Eq, Ord, Show)

data Fold = Fold Axis Int
deriving (Eq, Ord, Show)


Parsing the input file has some wrinkles that may be worth noting, specifically how to parse values of type Axis, which have no parameters. Using attoparsec:

inputP = (,) <$> sheetP <* many1 endOfLine <*> foldsP sheetP = S.fromList <$> dotP sepBy endOfLine
dotP = V2 <$> decimal <* "," <*> decimal foldsP = foldP sepBy endOfLine foldP = Fold <$> ("fold along " *> axisP) <* "=" <*> decimal

axisP = ("x" *> pure X) <|> ("y" *> pure Y)


Note the use of pure to generate Axis values.

Folding a sheet involves partitioning the sheet into the two halves, transforming the dots in the right or bottom half, then merging the two results.

There's some duplicated code here. I could possibly DRY it up only doing a fold in one dimension, and handling the other by transposing the points before and after that fold. But I think this solution is simple enough.

doFold :: Sheet -> Fold -> Sheet
doFold sheet (Fold X i) = S.union left foldedRight
where (left, right) = S.partition ((<= i) . getX) sheet
foldedRight = S.map foldDot right
foldDot (V2 x y) = V2 (i - (x - i)) y

doFold sheet (Fold Y i) = S.union top foldedBottom
where (top, bottom) = S.partition ((<= i) . getY) sheet
foldedBottom = S.map foldDot bottom
foldDot (V2 x y) = V2 x (i - (y - i))


After that, all that's left is to display the final values. I've got OverloadedLists turned on by default in my Cabal file, which means I need a few explicit type allocations here. That took me too long to work out and solve!

showSheet :: Sheet -> String
showSheet sheet = unlines [ concat [showPoint (V2 x y) | x <- ([0..maxX] :: [Int])
] | y <- ([0..maxY] :: [Int]) ]
where showPoint here = if here S.member sheet then "█" else " "
maxX = S.findMax $S.map getX sheet maxY = S.findMax$ S.map getY sheet


## Code

You can get the code from my locally-hosed Git repo, or from Gitlab.