···11+import Data.List
22+33+main :: IO ()
44+main = do
55+ contents <- getContents
66+ let entries = fmap read (lines contents)
77+ case find (\x -> (2020 - x) `elem` entries) entries of
88+ Just answer -> print $ answer * (2020 - answer)
99+ Nothing -> return ()
+6
2020/1/p2.hs
···11+main :: IO ()
22+main = do
33+ contents <- getContents
44+ let entries = fmap read (lines contents)
55+ let (a, b, c) = head [(x, y, z) | x <- entries, y <- entries, x + y <= 2020, z <- entries, x + y + z == 2020]
66+ print $ a * b * c
···11+import Data.List
22+import Data.Maybe
33+44+compute :: [Int] -> [(Int, Int)]
55+compute adaptors = computeInner [(0, 1)] adaptors
66+ where
77+ prev n p = fromMaybe 0 $ lookup n p
88+ computeInner :: [(Int, Int)] -> [Int] -> [(Int, Int)]
99+ computeInner p [] = p
1010+ computeInner p (a : as) =
1111+ let next = prev (a - 1) p + prev (a - 2) p + prev (a - 3) p in
1212+ computeInner ((a, next) : p) as
1313+1414+main :: IO ()
1515+main = do
1616+ adaptors <- sort . fmap read . lines <$> getContents
1717+ let
1818+ device = maximum adaptors + 3
1919+ levels = compute (adaptors ++ [device])
2020+ in print $ fromJust $ lookup device levels
+31
2020/11/p1.hs
···11+import Control.Monad
22+33+data Seat = Occupied | Unoccupied deriving (Eq)
44+55+seat '.' = Nothing
66+seat 'L' = Just Unoccupied
77+seat '#' = Just Occupied
88+seat _ = error "invalid seat"
99+1010+isOccupied :: Maybe Seat -> Bool
1111+isOccupied (Just Occupied) = True
1212+isOccupied _ = False
1313+1414+compute :: [[Maybe Seat]] -> Int
1515+compute seats = if seats == nextSeats
1616+ then length $ filter isOccupied (join seats)
1717+ else compute nextSeats
1818+ where
1919+ nextSeats = fmap (uncurry updateRow) ([0..] `zip` seats)
2020+ updateRow i row = fmap (uncurry (updateSeat i)) ([0..] `zip` row)
2121+ updateSeat _ _ Nothing = Nothing
2222+ updateSeat i j (Just seat) = case length $ filter isOccupied $ adjacent i j of
2323+ 0 -> Just Occupied
2424+ l | l >= 4 -> Just Unoccupied
2525+ _ -> Just seat
2626+ adjacent i j = [seats !! y !! x | y <- [i-1..i+1], x <- [j-1..j+1], x >= 0, y >= 0, y < length seats, x < length (seats !! y), (y, x) /= (i, j)]
2727+2828+main :: IO ()
2929+main = do
3030+ seats <- fmap (fmap seat) . lines <$> getContents
3131+ print $ compute seats
+43
2020/11/p2.hs
···11+import Control.Monad
22+import Data.Functor
33+import Data.List
44+import Data.Maybe
55+66+data Seat = Occupied | Unoccupied deriving (Eq, Show)
77+88+seat '.' = Nothing
99+seat 'L' = Just Unoccupied
1010+seat '#' = Just Occupied
1111+seat _ = error "invalid seat"
1212+1313+isOccupied :: Maybe Seat -> Bool
1414+isOccupied (Just Occupied) = True
1515+isOccupied _ = False
1616+1717+(!?) :: [a] -> Int -> Maybe a
1818+(!?) ls i | i >= 0 && i < length ls = Just (ls !! i)
1919+(!?) _ _ = Nothing
2020+2121+compute :: [[Maybe Seat]] -> Int
2222+compute seats = if seats == nextSeats
2323+ then length $ filter isOccupied (join seats)
2424+ else compute nextSeats
2525+ where
2626+ nextSeats = fmap (uncurry updateRow) ([0..] `zip` seats)
2727+ updateRow i row = fmap (uncurry (updateSeat i)) ([0..] `zip` row)
2828+ updateSeat _ _ Nothing = Nothing
2929+ updateSeat i j (Just seat) = case length $ filter isOccupied $ visible i j of
3030+ 0 -> Just Occupied
3131+ l | l >= 5 -> Just Unoccupied
3232+ _ -> Just seat
3333+ visible i j = join . find isJust <$> takeRay i j <$> directions
3434+ directions = [(dy, dx) | dy <- [-1..1], dx <- [-1..1], (dx, dy) /= (0, 0)]
3535+ takeRay :: Int -> Int -> (Int, Int) -> [Maybe Seat]
3636+ takeRay y x (dy, dx) = fromMaybe [] (seats !? ny >>= (!? nx) <&> (: takeRay ny nx (dy, dx)))
3737+ where ny = y + dy
3838+ nx = x + dx
3939+4040+main :: IO ()
4141+main = do
4242+ seats <- fmap (fmap seat) . lines <$> getContents
4343+ print $ compute seats
+57
2020/12/p1.hs
···11+data Direction = DN | DE | DS | DW deriving (Show, Eq)
22+33+l :: Direction -> Direction
44+l DN = DW
55+l DW = DS
66+l DS = DE
77+l DE = DN
88+99+r :: Direction -> Direction
1010+r DW = DN
1111+r DS = DW
1212+r DE = DS
1313+r DN = DE
1414+1515+data Instruction
1616+ = N Int
1717+ | E Int
1818+ | S Int
1919+ | W Int
2020+ | R Int
2121+ | L Int
2222+ | F Int
2323+ deriving (Show, Eq)
2424+2525+parse :: String -> Instruction
2626+parse ('N' : rest) = N (read rest)
2727+parse ('E' : rest) = E (read rest)
2828+parse ('S' : rest) = S (read rest)
2929+parse ('W' : rest) = W (read rest)
3030+parse ('R' : rest) = R (read rest `div` 90)
3131+parse ('L' : rest) = L (read rest `div` 90)
3232+parse ('F' : rest) = F (read rest)
3333+parse _ = error "Invalid instruction"
3434+3535+run :: [Instruction] -> (Direction, Int, Int) -> (Direction, Int, Int)
3636+run [] = id
3737+run (i : is) = run is . apply i
3838+3939+apply :: Instruction -> (Direction, Int, Int) -> (Direction, Int, Int)
4040+apply (N n) (d, x, y) = (d, x, y + n)
4141+apply (F n) (DN, x, y) = (DN, x, y + n)
4242+apply (S n) (d, x, y) = (d, x, y - n)
4343+apply (F n) (DS, x, y) = (DS, x, y - n)
4444+apply (E n) (d, x, y) = (d, x + n, y)
4545+apply (F n) (DE, x, y) = (DE, x + n, y)
4646+apply (W n) (d, x, y) = (d, x - n, y)
4747+apply (F n) (DW, x, y) = (DW, x - n, y)
4848+apply (L 0) h = h
4949+apply (L n) (d, x, y) = apply (L $ n - 1) (l d, x, y)
5050+apply (R 0) h = h
5151+apply (R n) (d, x, y) = apply (R $ n - 1) (r d, x, y)
5252+5353+main :: IO ()
5454+main = do
5555+ instructions <- fmap parse . lines <$> getContents
5656+ let (_, x, y) = run instructions (DE, 0, 0) in
5757+ print $ abs x + abs y
+48
2020/12/p2.hs
···11+data Instruction
22+ = N Int
33+ | E Int
44+ | S Int
55+ | W Int
66+ | R Int
77+ | L Int
88+ | F Int
99+ deriving (Show, Eq)
1010+1111+l :: Pos -> Pos
1212+l (x, y) = (-y, x)
1313+1414+r :: Pos -> Pos
1515+r (x, y) = (y, -x)
1616+1717+parse :: String -> Instruction
1818+parse ('N' : rest) = N (read rest)
1919+parse ('E' : rest) = E (read rest)
2020+parse ('S' : rest) = S (read rest)
2121+parse ('W' : rest) = W (read rest)
2222+parse ('R' : rest) = R (read rest `div` 90)
2323+parse ('L' : rest) = L (read rest `div` 90)
2424+parse ('F' : rest) = F (read rest)
2525+parse _ = error "Invalid instruction"
2626+2727+type Pos = (Int, Int)
2828+2929+run :: [Instruction] -> (Pos, Pos) -> (Pos, Pos)
3030+run [] = id
3131+run (i : is) = run is . apply i
3232+3333+apply :: Instruction -> (Pos, Pos) -> (Pos, Pos)
3434+apply (N n) ((x, y), p) = ((x, y + n), p)
3535+apply (S n) ((x, y), p) = ((x, y - n), p)
3636+apply (E n) ((x, y), p) = ((x + n, y), p)
3737+apply (W n) ((x, y), p) = ((x - n, y), p)
3838+apply (L 0) h = h
3939+apply (L n) (w, p) = apply (L $ n - 1) (l w, p)
4040+apply (R 0) h = h
4141+apply (R n) (w, p) = apply (R $ n - 1) (r w, p)
4242+apply (F n) ((dx, dy), (x, y)) = ((dx, dy), (x + dx * n, y + dy * n))
4343+4444+main :: IO ()
4545+main = do
4646+ instructions <- fmap parse . lines <$> getContents
4747+ let (_, (x, y)) = run instructions ((10, 1), (0, 0)) in
4848+ print $ abs x + abs y
+20
2020/13/p1.hs
···11+import Data.List
22+import Data.Ord
33+44+split :: Eq a => a -> [a] -> [[a]]
55+split x ys = splitInner x [] ys
66+ where
77+ splitInner _ [] [] = []
88+ splitInner _ r [] = [reverse r]
99+ splitInner x r (y : ys)
1010+ | x == y = reverse r : splitInner x [] ys
1111+ | otherwise = splitInner x (y : r) ys
1212+1313+main :: IO ()
1414+main = do
1515+ [arrival, buses] <- lines <$> getContents
1616+ let
1717+ start = read arrival
1818+ intervals = [read n | n <- split ',' buses, n /= "x"] :: [Int]
1919+ waits = [(n, n - start `mod` n) | n <- intervals]
2020+ in print . uncurry (*) $ minimumBy (comparing snd) waits
+26
2020/13/p2.hs
···11+import Data.Function
22+import Data.List
33+import Data.Maybe
44+import Text.Parsec
55+66+int :: Parsec String u Int
77+int = read <$> many1 digit
88+99+parseBuses = ((Just <$> int) <|> (char 'x' >> return Nothing)) `sepBy` char ','
1010+1111+answer :: [Maybe Int] -> Int
1212+answer buses = answer_ offset distance $ tail toFind
1313+ where
1414+ toFind = catMaybes $ zipWith (\i b -> (i,) <$> b) [0 :: Int ..] buses
1515+ (offset, distance) = head toFind
1616+1717+ answer_ n m [] = m - n
1818+ answer_ n d all@((i, m) : rest)
1919+ | n `mod` m == i `mod` m = answer_ n (d * m) rest
2020+ | otherwise = answer_ (n + d) d all
2121+2222+main :: IO ()
2323+main = do
2424+ [_, busstr] <- lines <$> getContents
2525+ let Right buses = parse parseBuses "" busstr
2626+ in print $ answer buses
+49
2020/14/p1.hs
···11+{-# LANGUAGE ViewPatterns #-}
22+import Data.List
33+import Data.Bits
44+55+split :: Eq a => [a] -> [a] -> [[a]]
66+split x ys = splitInner x [] ys
77+ where
88+ splitInner _ [] [] = []
99+ splitInner _ r [] = [reverse r]
1010+ splitInner x r (stripPrefix x -> Just ys) = reverse r : splitInner x [] ys
1111+ splitInner x r (y : ys) = splitInner x (y : r) ys
1212+1313+data Instruction
1414+ = Mask [Maybe Int]
1515+ | Set Int Int
1616+1717+parsebit :: Char -> Maybe Int
1818+parsebit '1' = Just 1
1919+parsebit '0' = Just 0
2020+parsebit _ = Nothing
2121+2222+parse :: String -> Instruction
2323+parse (stripPrefix "mask = " -> Just mask) = Mask (parsebit <$> mask)
2424+parse (stripPrefix "mem[" -> Just rest) = Set loc val
2525+ where [loc, val] = read <$> split "] = " rest
2626+2727+set :: Int -> Int -> [(Int, Int)] -> [(Int, Int)]
2828+set loc val map = case findIndex ((==) loc . fst) map of
2929+ Nothing -> (loc, val) : map
3030+ Just n -> (loc, val) : take n map ++ drop (n + 1) map
3131+3232+apply :: [Maybe Int] -> Int -> Int
3333+apply mask val = foldl' applyBit val ([0..] `zip` reverse mask)
3434+ where applyBit :: Int -> (Int, Maybe Int) -> Int
3535+ applyBit val (i, Nothing) = val
3636+ applyBit val (i, Just 0) = val .&. complement (bit i)
3737+ applyBit val (i, Just 1) = val .|. bit i
3838+3939+run :: [Instruction] -> [(Int, Int)]
4040+run = run_ [] (replicate 36 Nothing)
4141+ where
4242+ run_ mem _ [] = mem
4343+ run_ mem _ (Mask mask : is) = run_ mem mask is
4444+ run_ mem mask (Set loc val : is) = run_ (set loc (apply mask val) mem) mask is
4545+4646+main :: IO ()
4747+main = do
4848+ instructions <- fmap parse . lines <$> getContents
4949+ print $ sum $ snd <$> run instructions
+49
2020/14/p2.hs
···11+{-# LANGUAGE ViewPatterns #-}
22+import Data.Map (Map, insert, empty, elems)
33+import Data.List (stripPrefix, foldl', replicate)
44+import Data.Bits
55+66+split :: Eq a => [a] -> [a] -> [[a]]
77+split x ys = splitInner x [] ys
88+ where
99+ splitInner _ [] [] = []
1010+ splitInner _ r [] = [reverse r]
1111+ splitInner x r (stripPrefix x -> Just ys) = reverse r : splitInner x [] ys
1212+ splitInner x r (y : ys) = splitInner x (y : r) ys
1313+1414+data Instruction
1515+ = Mask [Maybe Int]
1616+ | Set Int Int
1717+1818+parsebit :: Char -> Maybe Int
1919+parsebit '1' = Just 1
2020+parsebit '0' = Just 0
2121+parsebit _ = Nothing
2222+2323+parse :: String -> Instruction
2424+parse (stripPrefix "mask = " -> Just mask) = Mask (parsebit <$> mask)
2525+parse (stripPrefix "mem[" -> Just rest) = Set loc val
2626+ where [loc, val] = read <$> split "] = " rest
2727+2828+setAll :: [Int] -> Int -> Map Int Int -> Map Int Int
2929+setAll locs val map = foldl' (flip (`insert` val)) map locs
3030+3131+apply :: [Maybe Int] -> Int -> [Int]
3232+apply mask = applyBits 0 (reverse mask)
3333+ where applyBits :: Int -> [Maybe Int] -> Int -> [Int]
3434+ applyBits _ [] x = [x]
3535+ applyBits i (Just 0 : ms) x = applyBits (i + 1) ms x
3636+ applyBits i (Just 1 : ms) x = flip setBit i <$> applyBits (i + 1) ms x
3737+ applyBits i (Nothing : ms) x = [flip setBit i, flip clearBit i] <*> applyBits (i + 1) ms x
3838+3939+run :: [Instruction] -> Map Int Int
4040+run = run_ empty (replicate 36 Nothing)
4141+ where
4242+ run_ mem _ [] = mem
4343+ run_ mem _ (Mask mask : is) = run_ mem mask is
4444+ run_ mem mask (Set loc val : is) = run_ (setAll (apply mask loc) val mem) mask is
4545+4646+main :: IO ()
4747+main = do
4848+ instructions <- fmap parse . lines <$> getContents
4949+ print $ sum $ elems $ run instructions
+29
2020/15/p1.hs
···11+import Control.Monad.State
22+import Data.Map (Map)
33+import Data.Map qualified as Map
44+import Text.Parsec
55+66+int :: Parsec String u Int
77+int = read <$> many digit
88+99+parseInput = int `sepBy` char ','
1010+1111+run :: Int -> [Int] -> Int
1212+run n st = evalState (run_ n) (Map.fromList $ zip (init st) [1 ..])
1313+ where
1414+ run_ :: Int -> Control.Monad.State.State (Map Int Int) Int
1515+ run_ n | n <= length st = return $ st !! (n - 1)
1616+ run_ i = do
1717+ prev <- run_ $ i - 1
1818+ before <- gets (Map.lookup prev)
1919+ let next = maybe 0 (i - 1 -) before
2020+ in do
2121+ modify (Map.insert prev (i - 1))
2222+ return next
2323+2424+answer contents = run 2020 input
2525+ where
2626+ Right input = parse parseInput "" contents
2727+2828+main :: IO ()
2929+main = getContents >>= print . answer
+28
2020/15/p2.hs
···11+import Control.Monad.State
22+import Data.IntMap.Strict (IntMap, (!?))
33+import Data.IntMap.Strict qualified as IntMap
44+import Text.Parsec
55+66+int :: Parsec String u Int
77+int = read <$> many digit
88+99+parseInput = int `sepBy` char ','
1010+1111+run :: Int -> [Int] -> Int
1212+run target st = run_ (length st + 1) 0 (IntMap.fromList $ zip st [1 ..])
1313+ where
1414+ run_ :: Int -> Int -> IntMap Int -> Int
1515+ run_ i n _ | i == target = n
1616+ run_ i n map =
1717+ let prev = map !? n
1818+ map' = IntMap.insert n i map
1919+ in case prev of
2020+ Nothing -> run_ (i + 1) 0 map'
2121+ Just time -> run_ (i + 1) (i - time) map'
2222+2323+answer contents = run 30000000 input
2424+ where
2525+ Right input = parse parseInput "" contents
2626+2727+main :: IO ()
2828+main = getContents >>= print . answer
+43
2020/16/p1.hs
···11+{-# LANGUAGE ViewPatterns, OverloadedStrings #-}
22+import Data.List
33+import qualified Data.Text as T
44+import Data.Text (Text)
55+66+split :: Eq a => [a] -> [a] -> [[a]]
77+split x ys = splitInner x [] ys
88+ where
99+ splitInner _ [] [] = []
1010+ splitInner _ r [] = [reverse r]
1111+ splitInner x r (stripPrefix x -> Just ys) = reverse r : splitInner x [] ys
1212+ splitInner x r (y : ys) = splitInner x (y : r) ys
1313+1414+data Field = Field Text [[Int]]
1515+fieldRanges (Field _ ranges) = ranges
1616+1717+newtype Ticket = Ticket [Int]
1818+ticketFields (Ticket fields) = fields
1919+2020+range :: Text -> [Int]
2121+range = containing . fmap (read . T.unpack) . T.splitOn "-"
2222+ where containing [low, hi] = [low..hi]
2323+2424+field :: Text -> Field
2525+field = makeField . T.splitOn ":"
2626+ where makeField [name, ranges] = Field name (range . T.strip <$> T.splitOn "or" ranges)
2727+2828+ticket :: Text -> Ticket
2929+ticket = Ticket . fmap (read . T.unpack) . T.splitOn ","
3030+3131+tickets :: [Text] -> [Ticket]
3232+tickets = fmap ticket . tail
3333+3434+parse :: Text -> ([Field], Ticket, [Ticket])
3535+parse input = (fmap field f, head $ tickets $ tail t, tickets $ tail ts)
3636+ where [f, t, ts] = split [""] $ T.lines input
3737+3838+main :: IO ()
3939+main = do
4040+ (fields, _, nearbyTickets) <- parse . T.pack <$> getContents
4141+ let ranges = fields >>= fieldRanges
4242+ values = nearbyTickets >>= ticketFields
4343+ in print $ sum $ filter (not . flip any (flip elem <$> ranges) . flip ($)) values
+77
2020/16/p2.hs
···11+{-# LANGUAGE ViewPatterns, OverloadedStrings #-}
22+import Control.Monad
33+import Data.List
44+import qualified Data.Map as Map
55+import Data.Map (Map)
66+import Data.Maybe
77+import qualified Data.Text as T
88+import Data.Text (Text)
99+1010+split :: Eq a => [a] -> [a] -> [[a]]
1111+split x ys = splitInner x [] ys
1212+ where
1313+ splitInner _ [] [] = []
1414+ splitInner _ r [] = [reverse r]
1515+ splitInner x r (stripPrefix x -> Just ys) = reverse r : splitInner x [] ys
1616+ splitInner x r (y : ys) = splitInner x (y : r) ys
1717+1818+data Field = Field Text [Int] deriving (Eq, Show)
1919+fieldRange (Field _ ranges) = ranges
2020+fieldName (Field name _) = name
2121+2222+newtype Ticket = Ticket [Int]
2323+ticketFields (Ticket fields) = fields
2424+2525+range :: Text -> [Int]
2626+range = containing . fmap (read . T.unpack) . T.splitOn "-"
2727+ where containing [low, hi] = [low..hi]
2828+2929+field :: Text -> Field
3030+field = makeField . T.splitOn ":"
3131+ where makeField [name, ranges] = Field name (range . T.strip =<< T.splitOn "or" ranges)
3232+3333+ticket :: Text -> Ticket
3434+ticket = Ticket . fmap (read . T.unpack) . T.splitOn ","
3535+3636+tickets :: [Text] -> [Ticket]
3737+tickets = fmap ticket
3838+3939+parse :: Text -> ([Field], Ticket, [Ticket])
4040+parse input = (fmap field f, head $ tickets $ tail t, tickets $ tail ts)
4141+ where [f, t, ts] = split [""] $ T.lines input
4242+4343+elemOf :: Eq a => [a] -> a -> Bool
4444+elemOf = flip elem
4545+4646+removeInvalid :: [Field] -> [Ticket] -> [Ticket]
4747+removeInvalid fields = filter (all (`elem` (fields >>= fieldRange)) . ticketFields)
4848+4949+findCandidates :: [Field] -> [Int] -> [Field]
5050+findCandidates fields xs = filter (\f -> all (`elem` fieldRange f) xs) fields
5151+5252+mapSnd :: (b -> c) -> (a, b) -> (a, c)
5353+mapSnd f (a, b) = (a, f b)
5454+5555+only :: [a] -> a
5656+only [a] = a
5757+only _ = error "Not the only one"
5858+5959+assignFields :: [(Int, [Field])] -> Map Int Field
6060+assignFields = only . assignFields_ Map.empty
6161+ where removeField f = sortOn (length . snd) . fmap (mapSnd (delete f))
6262+6363+ assignFields_ :: Map Int Field -> [(Int, [Field])] -> [Map Int Field]
6464+ assignFields_ m [] = [m]
6565+ assignFields_ m ((_, []) : _) = []
6666+ assignFields_ m ((i, fs) : cs) = fs >>= \f -> assignFields_ (Map.insert i f m) $ removeField f cs
6767+6868+main :: IO ()
6969+main = do
7070+ (fields, Ticket myTicket, nearbyTickets) <- parse . T.pack <$> getContents
7171+ let
7272+ validTickets = removeInvalid fields nearbyTickets
7373+ fieldValues = transpose (ticketFields <$> validTickets)
7474+ candidates = zip [0..] (findCandidates fields <$> fieldValues)
7575+ fieldAssignment = assignFields (sortOn (length . snd) candidates)
7676+ departureFields = Map.filter (T.isPrefixOf "departure") (fieldName <$> fieldAssignment)
7777+ in print $ product $ (myTicket !!) <$> Map.keys departureFields
+59
2020/17/p1.hs
···11+{-# LANGUAGE FlexibleInstances #-}
22+import Control.Monad
33+import Data.Maybe
44+55+tile :: Char -> Bool
66+tile '#' = True
77+tile '.' = False
88+99+update :: Int -> Bool -> Bool
1010+update 2 True = True
1111+update 3 _ = True
1212+update _ _ = False
1313+1414+class Remake i where
1515+ remake :: i -> i
1616+instance Remake Bool where
1717+ remake = const False
1818+instance (Functor f, Remake i) => Remake (f i) where
1919+ remake = fmap remake
2020+2121+class Expand e where
2222+ expand :: e -> e
2323+instance (Remake e, Expand e) => Expand [e] where
2424+ expand e = remake (head a) : a ++ [remake (last a)]
2525+ where a = fmap expand e
2626+instance Expand Bool where
2727+ expand = id
2828+2929+(!?) :: [a] -> Int -> Maybe a
3030+(!?) l x | x < 0 = Nothing
3131+(!?) l x | x >= length l = Nothing
3232+(!?) l x = Just (l !! x)
3333+3434+cycleOnce :: [[[Bool]]] -> [[[Bool]]]
3535+cycleOnce state = run (expand state)
3636+3737+neighbours :: [[[Bool]]] -> (Int, Int, Int) -> Int
3838+neighbours space (x, y, z) = count [space !? zz >>= (!? yy) >>= (!? xx) | xx <- [x-1..x+1],
3939+ yy <- [y-1..y+1],
4040+ zz <- [z-1..z+1],
4141+ xx /= x || yy /= y || zz /= z]
4242+ where count = length . filter id . catMaybes
4343+4444+run :: [[[Bool]]] -> [[[Bool]]]
4545+run space = [[[next (x, y, z) value | (x, value) <- [0..] `zip` row]
4646+ | (y, row) <- [0..] `zip` plane]
4747+ | (z, plane) <- [0..] `zip` space]
4848+ where
4949+ next :: (Int, Int, Int) -> Bool -> Bool
5050+ next = update . neighbours space
5151+5252+cycles :: Int -> [[[Bool]]] -> [[[Bool]]]
5353+cycles 0 state = state
5454+cycles n state = cycles (n - 1) $ cycleOnce state
5555+5656+main :: IO ()
5757+main = do
5858+ contents <- fmap (fmap tile) . lines <$> getContents
5959+ print . length . filter id . join . join $ cycles 6 [contents]
+61
2020/17/p2.hs
···11+{-# LANGUAGE FlexibleInstances #-}
22+import Control.Monad
33+import Data.Maybe
44+55+tile :: Char -> Bool
66+tile '#' = True
77+tile '.' = False
88+99+update :: Int -> Bool -> Bool
1010+update 2 True = True
1111+update 3 _ = True
1212+update _ _ = False
1313+1414+class Remake i where
1515+ remake :: i -> i
1616+instance Remake Bool where
1717+ remake = const False
1818+instance (Functor f, Remake i) => Remake (f i) where
1919+ remake = fmap remake
2020+2121+class Expand e where
2222+ expand :: e -> e
2323+instance (Remake e, Expand e) => Expand [e] where
2424+ expand e = remake (head a) : a ++ [remake (last a)]
2525+ where a = fmap expand e
2626+instance Expand Bool where
2727+ expand = id
2828+2929+(!?) :: [a] -> Int -> Maybe a
3030+(!?) l x | x < 0 = Nothing
3131+(!?) l x | x >= length l = Nothing
3232+(!?) l x = Just (l !! x)
3333+3434+cycleOnce :: [[[[Bool]]]] -> [[[[Bool]]]]
3535+cycleOnce state = run (expand state)
3636+3737+neighbours :: [[[[Bool]]]] -> (Int, Int, Int, Int) -> Int
3838+neighbours space (x, y, z, w) = count [space !? ww >>= (!? zz) >>= (!? yy) >>= (!? xx) | xx <- [x-1..x+1],
3939+ yy <- [y-1..y+1],
4040+ zz <- [z-1..z+1],
4141+ ww <- [w-1..w+1],
4242+ xx /= x || yy /= y || zz /= z || ww /= w]
4343+ where count = length . filter id . catMaybes
4444+4545+run :: [[[[Bool]]]] -> [[[[Bool]]]]
4646+run hyperspace = [[[[next (x, y, z, w) value | (x, value) <- [0..] `zip` row]
4747+ | (y, row) <- [0..] `zip` plane]
4848+ | (z, plane) <- [0..] `zip` space]
4949+ | (w, space) <- [0..] `zip` hyperspace]
5050+ where
5151+ next :: (Int, Int, Int, Int) -> Bool -> Bool
5252+ next = update . neighbours hyperspace
5353+5454+cycles :: Int -> [[[[Bool]]]] -> [[[[Bool]]]]
5555+cycles 0 state = state
5656+cycles n state = cycles (n - 1) $ cycleOnce state
5757+5858+main :: IO ()
5959+main = do
6060+ contents <- fmap (fmap tile) . lines <$> getContents
6161+ print . length . filter id . join . join . join $ cycles 6 [[contents]]
+63
2020/18/p1.hs
···11+{-# LANGUAGE OverloadedStrings #-}
22+import Control.Monad.State
33+import Data.Either
44+import Data.Maybe
55+import qualified Data.Text as T
66+import Data.Text (Text)
77+import Data.Text.Read
88+99+data Op = Add | Mul
1010+1111+eval :: Text -> Int
1212+eval = evalState (eval' 0 Add)
1313+1414+number :: State Text Int
1515+number = do
1616+ e <- gets (decimal . T.strip)
1717+ case e of
1818+ Left _ -> do
1919+ e <- get
2020+ error $ T.unpack e
2121+ Right (num, str) -> do
2222+ put $ T.strip str
2323+ return num
2424+2525+parenthesized :: State Text Int
2626+parenthesized = do
2727+ modify (T.strip . T.tail)
2828+ eval' 0 Add
2929+3030+getValue :: State Text Int
3131+getValue = do
3232+ str <- gets T.strip
3333+ if "(" `T.isPrefixOf` str
3434+ then parenthesized
3535+ else number
3636+3737+getOp :: State Text (Maybe Op)
3838+getOp = do
3939+ mc <- gets T.uncons
4040+ case mc of
4141+ Nothing -> return Nothing
4242+ Just (c, cs) -> do
4343+ put $ T.strip cs
4444+ case c of
4545+ '+' -> return $ Just Add
4646+ '*' -> return $ Just Mul
4747+ ')' -> return Nothing
4848+ _ -> error [c]
4949+5050+eval' :: Int -> Op -> State Text Int
5151+eval' lhs Add = do
5252+ rhs <- getValue
5353+ op <- getOp
5454+ let v = lhs + rhs in
5555+ maybe (return v) (eval' v) op
5656+eval' lhs Mul = do
5757+ rhs <- getValue
5858+ op <- getOp
5959+ let v = lhs * rhs in
6060+ maybe (return v) (eval' v) op
6161+6262+main :: IO ()
6363+main = getContents >>= print . sum . fmap eval . T.lines . T.pack
+69
2020/18/p2.hs
···11+{-# LANGUAGE OverloadedStrings, LambdaCase #-}
22+import Control.Monad.State
33+import Data.Either
44+import Data.Maybe
55+import qualified Data.Text as T
66+import Data.Text (Text)
77+import Data.Text.Read
88+99+data Op = Add | Mul
1010+1111+eval :: Text -> Int
1212+eval = evalState factors
1313+1414+number :: State Text Int
1515+number = do
1616+ e <- gets (decimal . T.strip)
1717+ case e of
1818+ Left _ -> do
1919+ e <- get
2020+ error $ T.unpack e
2121+ Right (num, str) -> do
2222+ put $ T.strip str
2323+ return num
2424+2525+parenthesized :: State Text Int
2626+parenthesized = do
2727+ modify (T.strip . T.tail)
2828+ x <- factors
2929+ modify (T.strip . T.tail)
3030+ return x
3131+3232+getValue :: State Text Int
3333+getValue = do
3434+ str <- gets T.strip
3535+ if "(" `T.isPrefixOf` str
3636+ then parenthesized
3737+ else number
3838+3939+mapsnd :: (b -> c) -> (a, b) -> (a, c)
4040+mapsnd f (a, b) = (a, f b)
4141+4242+op :: Char -> State Text Bool
4343+op c = do
4444+ mc <- gets $ fmap (mapsnd T.strip) . T.uncons
4545+ case mc of
4646+ Just (cx, cs) | cx == c -> do
4747+ put cs
4848+ return True
4949+ _ -> return False
5050+5151+add = op '+'
5252+mul = op '*'
5353+5454+terms :: State Text Int
5555+terms = do
5656+ lhs <- getValue
5757+ add >>= \case
5858+ False -> return lhs
5959+ True -> (lhs +) <$> terms
6060+6161+factors :: State Text Int
6262+factors = do
6363+ lhs <- terms
6464+ mul >>= \case
6565+ False -> return lhs
6666+ True -> (lhs *) <$> factors
6767+6868+main :: IO ()
6969+main = getContents >>= print . sum . fmap eval . T.lines . T.pack
···11+main :: IO ()
22+main = do
33+ contents <- getContents
44+ let
55+ levels = lines contents
66+ a = checkAll [0,3..] levels
77+ b = checkAll [0..] levels
88+ c = checkAll [0,5..] levels
99+ d = checkAll [0,7..] levels
1010+ e = checkAll [0..] [l | (i, l) <- zip [0..] levels, i `mod` 2 == 0]
1111+ in print $ a * b * c * d * e
1212+1313+checkAll :: [Int] -> [String] -> Int
1414+checkAll step levels = length $ filter check $ zip step levels
1515+1616+check :: (Int, String) -> Bool
1717+check (i, line) = line !! (i `mod` length line) == '#'
+16
2020/4/p1.hs
···11+import Data.List
22+33+main :: IO ()
44+main = do
55+ contents <- getContents
66+ print $ checkAll $ lines contents
77+88+checkAll :: [String] -> Int
99+checkAll [] = 0
1010+checkAll ls =
1111+ if length [w | w <- fmap (take 3) (passport >>= words), w `elem` ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]] == 7
1212+ then 1 + checkAll rest
1313+ else checkAll rest
1414+ where (passport, rest) = case findIndex null ls of
1515+ Nothing -> (ls, [])
1616+ Just n -> let (p, r) = splitAt n ls in (p, tail r)
+36
2020/4/p2.hs
···11+{-# LANGUAGE ViewPatterns #-}
22+import Data.List
33+44+main :: IO ()
55+main = do
66+ contents <- getContents
77+ print $ checkAll $ lines contents
88+99+checkAll :: [String] -> Int
1010+checkAll [] = 0
1111+checkAll ls =
1212+ if length (filter valid (passport >>= words)) == 7
1313+ then 1 + checkAll rest
1414+ else checkAll rest
1515+ where (passport, rest) = case findIndex null ls of
1616+ Nothing -> (ls, [])
1717+ Just n -> let (p, r) = splitAt n ls in (p, tail r)
1818+1919+valid :: String -> Bool
2020+valid (stripPrefix "byr:" -> Just y) = let year = read y in year >= 1920 && year <= 2002
2121+valid (stripPrefix "iyr:" -> Just y) = let year = read y in year >= 2010 && year <= 2020
2222+valid (stripPrefix "eyr:" -> Just y) = let year = read y in year >= 2020 && year <= 2030
2323+valid (stripPrefix "hgt:" -> Just str)
2424+ | "cm" `isSuffixOf` str = height >= 150 && height <= 193
2525+ | "in" `isSuffixOf` str = height >= 59 && height <= 76
2626+ | otherwise = False
2727+ where
2828+ height :: Int
2929+ height = read (take (length str - 2) str)
3030+valid (stripPrefix "hcl:#" -> Just col) | length col == 6 = all isHex col
3131+valid (stripPrefix "ecl:" -> Just col) | col `elem` ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"] = True
3232+valid (stripPrefix "pid:" -> Just pid) | length pid == 9 = all (`elem` ['0'..'9']) pid
3333+valid _ = False
3434+3535+isHex :: Char -> Bool
3636+isHex ch = ch `elem` ['0'..'9'] ++ ['a'..'f']
+20
2020/5/p1.hs
···11+main :: IO ()
22+main = do
33+ contents <- getContents
44+ print $ maximum (fmap seatID (lines contents))
55+66+seatID :: String -> Int
77+seatID s = let (row, col) = rowcol s in row * 8 + col
88+99+rowcol :: String -> (Int, Int)
1010+rowcol = rowcol_ [0..127] [0..8]
1111+ where
1212+ rowcol_ row col [] = (head row, head col)
1313+ rowcol_ row col s = case s of
1414+ 'F' : s -> rowcol_ (take halfrow row) col s
1515+ 'B' : s -> rowcol_ (drop halfrow row) col s
1616+ 'L' : s -> rowcol_ row (take halfcol col) s
1717+ 'R' : s -> rowcol_ row (drop halfcol col) s
1818+ _ -> error "wrong"
1919+ where halfrow = length row `div` 2
2020+ halfcol = length col `div` 2
+25
2020/5/p2.hs
···11+import Data.List
22+33+main :: IO ()
44+main = do
55+ contents <- getContents
66+ let
77+ seats = sort $ fmap seatID (lines contents)
88+ Just (before, _) = find (\(x, y) -> x + 2 == y) (zip seats (tail seats))
99+ in print $ before + 1
1010+1111+seatID :: String -> Int
1212+seatID s = let (row, col) = rowcol s in row * 8 + col
1313+1414+rowcol :: String -> (Int, Int)
1515+rowcol = rowcol_ [0..127] [0..8]
1616+ where
1717+ rowcol_ row col [] = (head row, head col)
1818+ rowcol_ row col s = case s of
1919+ 'F' : s -> rowcol_ (take halfrow row) col s
2020+ 'B' : s -> rowcol_ (drop halfrow row) col s
2121+ 'L' : s -> rowcol_ row (take halfcol col) s
2222+ 'R' : s -> rowcol_ row (drop halfcol col) s
2323+ _ -> error "wrong"
2424+ where halfrow = length row `div` 2
2525+ halfcol = length col `div` 2
+24
2020/6/p1.hs
···11+groups :: [String] -> [[String]]
22+groups = splitWhere null
33+44+splitWhere :: (a -> Bool) -> [a] -> [[a]]
55+splitWhere = splitWhere_ []
66+ where
77+ splitWhere_ [] _ [] = []
88+ splitWhere_ g _ [] = [g]
99+ splitWhere_ g f (a : as)
1010+ | f a = reverse g : splitWhere_ [] f as
1111+ | otherwise = splitWhere_ (a : g) f as
1212+1313+uniq :: Eq a => [a] -> [a]
1414+uniq = uniq_ []
1515+ where
1616+ uniq_ as [] = as
1717+ uniq_ u (a : as)
1818+ | a `elem` u = uniq_ u as
1919+ | otherwise = uniq_ (a : u) as
2020+2121+main :: IO ()
2222+main = do
2323+ contents <- getContents
2424+ print (sum $ fmap (length . uniq . concat) (groups $ lines contents))
+30
2020/6/p2.hs
···11+groups :: [String] -> [[String]]
22+groups = splitWhere null
33+44+splitWhere :: (a -> Bool) -> [a] -> [[a]]
55+splitWhere = splitWhere_ []
66+ where
77+ splitWhere_ [] _ [] = []
88+ splitWhere_ g _ [] = [g]
99+ splitWhere_ g f (a : as)
1010+ | f a = reverse g : splitWhere_ [] f as
1111+ | otherwise = splitWhere_ (a : g) f as
1212+1313+uniq :: Eq a => [a] -> [a]
1414+uniq = uniq_ []
1515+ where
1616+ uniq_ as [] = as
1717+ uniq_ u (a : as)
1818+ | a `elem` u = uniq_ u as
1919+ | otherwise = uniq_ (a : u) as
2020+2121+allAnswered :: [String] -> Int
2222+allAnswered group = length $ filter answered questions
2323+ where
2424+ answered q = all (q `elem`) group
2525+ questions = (uniq . concat) group
2626+2727+main :: IO ()
2828+main = do
2929+ contents <- getContents
3030+ print (sum $ fmap allAnswered (groups $ lines contents))
+45
2020/7/p1.hs
···11+import Control.Monad
22+import Data.List
33+import Data.Maybe
44+55+data Rule = Rule String [String] deriving (Show)
66+77+mapsnd :: (b -> c) -> (a, b) -> (a, c)
88+mapsnd f (x, y) = (x, f y)
99+1010+rule :: String -> Rule
1111+rule str = Rule from to
1212+ where
1313+ a : b : _ : _ : rest = words str
1414+ from = a ++ " " ++ b
1515+ to = bags rest
1616+ bags (_ : a : b : _ : rest) = (a ++ " " ++ b) : bags rest
1717+ bags _ = []
1818+1919+ruleFor s (Rule f _) = s == f
2020+2121+directlyAllowed :: Rule -> (String, Maybe Bool)
2222+directlyAllowed (Rule k []) = (k, Just False)
2323+directlyAllowed (Rule k rs)
2424+ | "shiny gold" `elem` rs = (k, Just True)
2525+ | otherwise = (k, Nothing)
2626+2727+check :: [Rule] -> [(String, Maybe Bool)] -> [(String, Bool)]
2828+check rules pairs
2929+ | all (isJust . snd) pairs = fmap (mapsnd fromJust) pairs
3030+ | otherwise = check rules $ fmap allowed pairs
3131+ where
3232+ allowed (s, Just n) = (s, Just n)
3333+ allowed (s, Nothing) = (s, updated)
3434+ where
3535+ Rule _ to = fromJust (find (ruleFor s) rules)
3636+ children = fmap (\s -> snd . fromJust $ find ((== s) . fst) pairs) to
3737+ updated = foldM (\b c -> (|| b) <$> c) False children :: Maybe Bool
3838+3939+main :: IO ()
4040+main = do
4141+ contents <- getContents
4242+ let
4343+ rules = fmap rule $ lines contents
4444+ allowed = fmap directlyAllowed rules
4545+ in print $ length $ filter snd $ check rules allowed
+29
2020/7/p2.hs
···11+import Data.List
22+import Data.Maybe
33+44+data Rule = Rule String [(Int, String)] deriving (Show)
55+66+mapsnd :: (b -> c) -> (a, b) -> (a, c)
77+mapsnd f (x, y) = (x, f y)
88+99+rule :: String -> Rule
1010+rule str = Rule from to
1111+ where
1212+ a : b : _ : _ : rest = words str
1313+ from = a ++ " " ++ b
1414+ to = bags rest
1515+ bags (n : a : b : _ : rest) = (read n, a ++ " " ++ b) : bags rest
1616+ bags _ = []
1717+1818+ruleFor s (Rule f _) = s == f
1919+2020+mustContain :: String -> [Rule] -> Int
2121+mustContain bag rules =
2222+ let Rule _ to = fromJust $ find (ruleFor bag) rules in
2323+ sum $ fmap (\(x, bag) -> x * (1 + mustContain bag rules)) to
2424+2525+main :: IO ()
2626+main = do
2727+ contents <- getContents
2828+ let rules = fmap rule $ lines contents in
2929+ print $ mustContain "shiny gold" rules
+27
2020/8/p1.hs
···11+{-# LANGUAGE ViewPatterns #-}
22+import Control.Monad
33+import Data.List
44+55+data Instruction
66+ = Nop
77+ | Acc Int
88+ | Jmp Int
99+1010+parse :: String -> Instruction
1111+parse (fmap read . stripPrefix "acc +" -> Just i) = Acc i
1212+parse (fmap read . stripPrefix "acc -" -> Just i) = Acc (-i)
1313+parse (fmap read . stripPrefix "jmp +" -> Just i) = Jmp i
1414+parse (fmap read . stripPrefix "jmp -" -> Just i) = Jmp (-i)
1515+parse _ = Nop
1616+1717+exec :: Int -> [Int] -> [Instruction] -> Int
1818+exec i e _ | i `elem` e = 0
1919+exec i e inst = case inst !! i of
2020+ Nop -> exec (i + 1) (i : e) inst
2121+ Acc n -> n + exec (i + 1) (i : e) inst
2222+ Jmp n -> exec (i + n) (i : e) inst
2323+2424+main :: IO ()
2525+main = do
2626+ instructions <- fmap parse . lines <$> getContents
2727+ print $ exec 0 [] instructions
+46
2020/8/p2.hs
···11+{-# LANGUAGE ViewPatterns #-}
22+import Control.Monad
33+import Data.Functor
44+import Data.List
55+import Data.Maybe
66+77+data Instruction
88+ = Nop Int
99+ | Acc Int
1010+ | Jmp Int
1111+1212+swap :: Instruction -> Maybe Instruction
1313+swap (Nop i) = Just (Jmp i)
1414+swap (Jmp i) = Just (Nop i)
1515+swap _ = Nothing
1616+1717+parse :: String -> Instruction
1818+parse (fmap read . stripPrefix "acc +" -> Just i) = Acc i
1919+parse (fmap read . stripPrefix "acc -" -> Just i) = Acc (-i)
2020+parse (fmap read . stripPrefix "jmp +" -> Just i) = Jmp i
2121+parse (fmap read . stripPrefix "jmp -" -> Just i) = Jmp (-i)
2222+parse (fmap read . stripPrefix "nop +" -> Just i) = Nop i
2323+parse (fmap read . stripPrefix "nop -" -> Just i) = Nop (-i)
2424+parse _ = error "invalid"
2525+2626+exec :: Int -> [Int] -> [Instruction] -> Maybe Int
2727+exec i e _ | i `elem` e = Nothing
2828+exec i _ inst | i >= length inst = Just 0
2929+exec i e inst = case inst !! i of
3030+ Nop _ -> exec (i + 1) (i : e) inst
3131+ Acc n -> (n +) <$> exec (i + 1) (i : e) inst
3232+ Jmp n -> exec (i + n) (i : e) inst
3333+3434+connect :: [Instruction] -> [Instruction] -> Instruction -> [Instruction]
3535+connect ls rs i = ls ++ (i : rs)
3636+3737+fix :: [Instruction] -> Int
3838+fix inst = fromJust $ join $ find isJust $ zipWith3 try (inits (init inst)) (tails (tail inst)) (swap <$> inst)
3939+ where
4040+ try :: [Instruction] -> [Instruction] -> Maybe Instruction -> Maybe Int
4141+ try ls rs x = x <&> connect ls rs >>= exec 0 []
4242+4343+main :: IO ()
4444+main = do
4545+ instructions <- fmap parse . lines <$> getContents
4646+ print $ fix instructions
+12
2020/9/p1.hs
···11+check :: [Int] -> [Int] -> Int
22+check _ [] = error "invalid"
33+check preamble (x : xs) = if x `elem` valid preamble then check (x : init preamble) xs else x
44+55+valid :: [Int] -> [Int]
66+valid preamble = [(preamble !! i) + (preamble !! j) | i <- [0..23], j <- [i+1..24]]
77+88+main :: IO ()
99+main = do
1010+ numbers <- fmap read . lines <$> getContents :: IO [Int]
1111+ let (preamble, contents) = splitAt 25 numbers in
1212+ print $ check (reverse preamble) contents
+25
2020/9/p2.hs
···11+import Data.Maybe
22+33+findInvalid :: [Int] -> [Int] -> Int
44+findInvalid _ [] = error "invalid"
55+findInvalid preamble (x : xs) = if x `elem` valid preamble then findInvalid (x : init preamble) xs else x
66+77+valid :: [Int] -> [Int]
88+valid preamble = [(preamble !! i) + (preamble !! j) | i <- [0..23], j <- [i+1..24]]
99+1010+rangeOf :: Int -> [Int] -> [Int]
1111+rangeOf x xs = fromMaybe (rangeOf x (tail xs)) $ findRange x xs 0
1212+ where
1313+ findRange :: Int -> [Int] -> Int -> Maybe [Int]
1414+ findRange t (x : xs) a
1515+ | x + a == t = Just [x]
1616+ | x + a < t = (x :) <$> findRange t xs (x + a)
1717+ | otherwise = Nothing
1818+1919+main :: IO ()
2020+main = do
2121+ numbers <- fmap read . lines <$> getContents :: IO [Int]
2222+ let (preamble, contents) = splitAt 25 numbers
2323+ invalid = findInvalid (reverse preamble) contents
2424+ range = rangeOf invalid numbers
2525+ in print $ minimum range + maximum range
+50
2020/Justfile
···11+set dotenv-load
22+set quiet
33+set shell := ["fish", "-c"]
44+55+year := env_var("YEAR")
66+session := env_var("SESSION")
77+88+[no-cd]
99+default: p1 p2
1010+1111+all:
1212+ #!/usr/bin/env fish
1313+ for file in (ls)
1414+ if test -d $file
1515+ echo "Day $file"
1616+ just run $file
1717+ end
1818+ end
1919+2020+[no-cd]
2121+p1 input="input": (do "p1" input)
2222+2323+[no-cd]
2424+p2 input="input": (do "p2" input)
2525+2626+[no-cd]
2727+do part input:
2828+ #!/usr/bin/env fish
2929+ if test -f {{part}}.hs
3030+ ghc {{part}} -O -outputdir.{{part}} > /dev/null
3131+ and time ./{{part}} < {{input}}
3232+ else if ls | rg "\.cabal\$" -q
3333+ cabal build {{part}} > /dev/null
3434+ and time cabal run {{part}} < {{input}}
3535+ else if test -f {{part}}.fish
3636+ time ./{{part}}.fish < {{input}}
3737+ else
3838+ echo "Current directory does not contain known solution configuration"
3939+ exit 1
4040+ end
4141+4242+run day:
4343+ cd {{day}} && just
4444+4545+get day:
4646+ mkdir -p {{day}}
4747+ curl https://adventofcode.com/{{year}}/day/{{day}}/input \
4848+ -X GET \
4949+ -H "Cookie: session={{session}}" \
5050+ -o {{day}}/input
+3
2020/README.md
···11+# Some Christmas Coding Fun
22+33+Inspired by @ArcQ. [Advent of Code](https://adventofcode.com/)