···11+aoc
22+---
33+44+solutions for aoc written in haskell
55+66+all solutions are (soon) to be written in literate haskell
77+and exported to an interactive book using ./book/build.sh.
+117
src/2016/01.lhs
···11+= 2016
22+33+== Day 1
44+55+=== Part 1
66+77+We start with some imports obviously:
88+99+\begin{code}
1010+{-# LANGUAGE LambdaCase #-}
1111+import qualified Data.Set as S
1212+\end{code}
1313+1414+Once we have those in place, begin by parsing the input; the input is of the form:
1515+1616+```
1717+R24, L45, CNN ...
1818+```
1919+2020+Where `C` is a letter, `NN` form a natural number. First we normalize the input by appending a "," to the end. We then use `words` to split on spaces, now each element is of the form `CNN,`. Using `init` we can drop the trailing comma, and finally, call `extract` to parse `C` and `NN` separately.
2121+2222+\begin{code}
2323+parse :: String -> [(Char, Int)]
2424+parse input = map (extract . init) $ words normalized
2525+ where normalized = input ++ ","
2626+ extract (d:rest) = (d, read rest)
2727+\end{code}
2828+2929+Next, we define a list of axes, the +x axis is defined as (1, 0) and so on:
3030+3131+\begin{code}
3232+axis :: [(Int, Int)]
3333+axis = [(0, 1), (1, 0), (0, -1), (-1, 0)]
3434+\end{code}
3535+3636+And finally, the first bit of interesting computation; given a direction, we apply a move to the right or left. Each face is represented by an integer (0 being North, 1 being East etc.),
3737+Turning right adds one; and turing left subtracts one, the modulo makes the computation cyclic:
3838+3939+\begin{code}
4040+dir :: Int -> Char -> Int
4141+dir face = \case
4242+ 'R' -> (face + 1) `mod` 4
4343+ 'L' -> (face - 1) `mod` 4
4444+\end{code}
4545+4646+Now, given a position, and a movement; we can compute the new position like so; first compute the new face to look at by turning,
4747+next, determine the axis that we would move along. If we are moving along the +x axis, (dx, dy) is set to (1, 0). Thus the new position is given by movind `l * dx` along the x-axis, and `l * dy` along the y-axis (which would be zero if `dy` is zero).
4848+4949+\begin{code}
5050+move (face, x, y) (d, l) = (nf, x + l * dx, y + l * dy)
5151+ where nf = dir face d
5252+ (dx, dy) = axis !! nf
5353+\end{code}
5454+5555+The problem requires us to compute the Manhattan distance of the destination location; which is given by:
5656+5757+\begin{code}
5858+mag (_, x, y) = abs x + abs y
5959+\end{code}
6060+6161+Thus, the solution to part 1 is given by `p1`; apply the `move` function starting at (0, 0, 0); and applying all the moves in the input. Finally compute the Manhattan distance of the destination from the origin.
6262+6363+\begin{code}
6464+p1 :: [(Char, Int)] -> Int
6565+p1 n = mag $ foldl' move (0, 0, 0) n
6666+\end{code}
6767+6868+=== Part 2
6969+7070+In the second part, we are tasked with finding the first point that we cross over. This requires us to first enumerate all the points we have visited by making each move.
7171+7272+This `range` function is a helper to enumerate all points between two integers:
7373+7474+\begin{code}
7575+range :: Int -> Int -> [Int]
7676+range a b
7777+ | a <= b = [a .. b]
7878+ | otherwise = [a, a - 1 .. b]
7979+\end{code}
8080+8181+The first point we cross over will then be the first duplicate element in the list of points we visit, `firstDup` uses `Data.Set` to determine the first duplicate point from a sequence:
8282+8383+\begin{code}
8484+firstDup :: Ord a => [a] -> a
8585+firstDup xs = go S.empty xs
8686+ where
8787+ go seen (x : xs)
8888+ | x `S.member` seen = x
8989+ | otherwise = go (S.insert x seen) xs
9090+\end{code}
9191+9292+Thus, the solution to the second part is given by `p2`. First, `pts` is calculated as all the final locations after each move. We then run through these pairwise and calculate all the points in between. Finally, we determine the magnitude of the first point we visit twice.
9393+9494+\begin{code}
9595+p2 :: [(Char, Int)] -> Int
9696+p2 n = mag $ firstDup coords
9797+ where
9898+ pts = scanl move (0, 0, 0) n
9999+ pairs = zip pts (tail pts)
100100+ coords =
101101+ [ (0, x, y)
102102+ | ((_, x1, y1), (_, x2, y2)) <- pairs,
103103+ x <- range x1 x2,
104104+ y <- range y1 y2,
105105+ (x, y) /= (x1, y1)
106106+ ]
107107+\end{code}
108108+109109+Finally the `main` function is defined like so:
110110+111111+\begin{code}
112112+input = "R8, R4, R4, R8"
113113+main = do
114114+ let f = parse input
115115+ print $ p1 f
116116+ print $ p2 f
117117+\end{code}
+46
src/2016/02.lhs
···11+== Day 2
22+33+=== Part 1
44+55+\begin{code}
66+{-# LANGUAGE LambdaCase #-}
77+88+import qualified Data.Map as M
99+import System.Environment (getArgs)
1010+1111+grid = M.fromList $ zip [(x, y) | y <- [1, 0, -1], x <- [-1, 0, 1]] [1 .. 9]
1212+1313+grid2 =
1414+ M.fromList $
1515+ concat
1616+ [ [((0, 2), '1')],
1717+ [((-1, 1), '2'), ((0, 1), '3'), ((1, 1), '4')],
1818+ [((-2, 0), '5'), ((-1, 0), '6'), ((0, 0), '7'), ((1, 0), '8'), ((2, 0), '9')],
1919+ [((-1, -1), 'A'), ((0, -1), 'B'), ((1, -1), 'C')],
2020+ [((0, -2), 'D')]
2121+ ]
2222+2323+move = \case
2424+ 'L' -> (-1, 0)
2525+ 'R' -> (1, 0)
2626+ 'U' -> (0, 1)
2727+ 'D' -> (0, -1)
2828+2929+mmove g pos d = if M.member npos g then npos else pos
3030+ where
3131+ delta = move d
3232+ npos = (fst pos + fst delta, snd pos + snd delta)
3333+3434+p1 ns = tail $ map (grid M.!) $ scanl (foldl' (mmove grid)) (0, 0) ns
3535+3636+p2 ns = tail $ map (grid2 M.!) $ scanl (foldl' (mmove grid2)) (-2, 0) ns
3737+3838+main = do
3939+ args <- getArgs
4040+ n <- case args of
4141+ ["-"] -> getContents
4242+ [file] -> readFile file
4343+ let f = lines n
4444+ print $ p1 f
4545+ print $ p2 f
4646+\end{code}
+24
src/2016/03.lhs
···11+import qualified Data.List as L
22+import Data.List.Split (chunksOf, splitOn)
33+import qualified Data.Text as T
44+import System.Environment (getArgs)
55+66+strip = T.unpack . T.strip . T.pack
77+88+parse i = map read $ filter (not . null) $ map strip $ splitOn " " i
99+1010+possible l = all test $ L.permutations l
1111+ where
1212+ test [a, b, c] = a + b > c
1313+1414+p1 = length . filter possible
1515+1616+main = do
1717+ args <- getArgs
1818+ n <- case args of
1919+ ["-"] -> getContents
2020+ [file] -> readFile file
2121+ let f = map parse $ lines n
2222+ let f2 = chunksOf 3 $ concat $ L.transpose f
2323+ print $ p1 f
2424+ print $ p1 f2
+43
src/2016/04.lhs
···11+import Data.Char
22+import qualified Data.List as L
33+import Data.List.Split (splitOn)
44+import qualified Data.Map as M
55+import qualified Data.Text as T
66+import System.Environment (getArgs)
77+88+parse i = (ls, read r, init c)
99+ where
1010+ parts = splitOn "-" i
1111+ ls = concat $ init parts
1212+ [r, c] = splitOn "[" (L.last parts)
1313+1414+freqs = M.fromListWith (+) . map (,1)
1515+1616+cksum m = take 5 $ map fst $ L.sortBy f $ M.toList m
1717+ where
1818+ f (a, f1) (b, f2) = compare f2 f1 <> compare a b
1919+2020+p1 it =
2121+ sum $
2222+ map (\(_, b, _) -> b) $
2323+ filter solve it
2424+ where
2525+ solve (ls, room, c) = cksum (freqs ls) == c
2626+2727+decrypt n = map (chr . (+ 97) . (`mod` 26) . (+ n) . flip (-) 97 . ord)
2828+2929+p2 it =
3030+ L.find ((== "northpoleobjectstorage") . fst) $
3131+ map (\(a, b, _) -> (decrypt b a, b)) $
3232+ filter solve it
3333+ where
3434+ solve (ls, room, c) = cksum (freqs ls) == c
3535+3636+main = do
3737+ args <- getArgs
3838+ n <- case args of
3939+ ["-"] -> getContents
4040+ [file] -> readFile file
4141+ let f = map parse $ lines n
4242+ print $ p1 f
4343+ print $ p2 f
+41
src/2016/05.lhs
···11+import qualified Crypto.Hash.MD5 as MD5
22+import qualified Data.ByteString.Base16 as B16
33+import Data.ByteString.Char8 (pack)
44+import qualified Data.ByteString.Char8 as BS
55+import qualified Data.List as L
66+import qualified Data.Map as M
77+import System.Environment (getArgs)
88+99+validHashes inp =
1010+ [ h
1111+ | f <- [0 ..],
1212+ let h = BS.unpack $ B16.encode $ MD5.hash $ BS.pack $ inp ++ show f,
1313+ "00000" `L.isPrefixOf` h
1414+ ]
1515+1616+p1 = take 8 . map (!! 5) . validHashes
1717+1818+p2 inp =
1919+ M.elems $
2020+ go
2121+ M.empty
2222+ [ (pos, val)
2323+ | h <- validHashes inp,
2424+ let pos = h !! 5,
2525+ let val = h !! 6,
2626+ pos `elem` "01234567"
2727+ ]
2828+ where
2929+ go seen ((p, v) : rest)
3030+ | length seen == 8 = seen
3131+ | M.member p seen = go seen rest
3232+ | otherwise = go (M.insert p v seen) rest
3333+3434+main = do
3535+ args <- getArgs
3636+ n <- case args of
3737+ ["-"] -> getContents
3838+ [file] -> readFile file
3939+ let f = init n
4040+ print $ p1 f
4141+ print $ p2 f
+24
src/2016/06.lhs
···11+import qualified Data.List as L
22+import qualified Data.Map as M
33+import Data.Ord (comparing)
44+import System.Environment (getArgs)
55+66+freqs = M.fromListWith (+) . map (,1)
77+88+highest = L.maximumBy (comparing snd) . M.toList
99+1010+lowest = L.minimumBy (comparing snd) . M.toList
1111+1212+p1 = map (fst . highest . freqs)
1313+1414+p2 = map (fst . lowest . freqs)
1515+1616+main = do
1717+ args <- getArgs
1818+ n <- case args of
1919+ ["-"] -> getContents
2020+ [file] -> readFile file
2121+ let f = L.transpose $ lines n
2222+ putStrLn $ p1 f
2323+ putStrLn $ p2 f
2424+