├── .gitignore ├── Build.hs ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Setup.hs ├── aoc2021.cabal ├── app └── aoc.hs ├── bench └── Bench.hs ├── cabal.project.freeze ├── draft └── day03.md ├── feed.xml ├── reflections-out ├── day01.md └── day02.md ├── reflections.md ├── reflections ├── day01.md └── day02.md ├── script ├── generate_days.hs └── open_files.vim ├── src ├── AOC.hs └── AOC │ ├── Challenge.hs │ ├── Challenge │ ├── Day01.hs │ ├── Day02.hs │ ├── Day03.hs │ ├── Day04.hs │ ├── Day05.hs │ ├── Day06.hs │ ├── Day07.hs │ ├── Day08.hs │ ├── Day09.hs │ ├── Day10.hs │ ├── Day11.hs │ ├── Day12.hs │ ├── Day13.hs │ ├── Day14.hs │ ├── Day15.hs │ ├── Day16.hs │ ├── Day17.hs │ ├── Day18.hs │ ├── Day19.hs │ ├── Day20.hs │ ├── Day21.hs │ ├── Day22.hs │ ├── Day23.hs │ ├── Day24.hs │ └── Day25.hs │ ├── Common.hs │ ├── Common │ ├── FinitarySet.hs │ ├── Point.hs │ └── Search.hs │ ├── Discover.hs │ ├── Prelude.hs │ ├── Run.hs │ ├── Run │ ├── Config.hs │ ├── Interactive.hs │ └── Load.hs │ ├── Solver.hs │ ├── Util.hs │ └── Util │ └── DynoMap.hs ├── stack.yaml ├── stack.yaml.lock ├── template ├── DayXX.hs ├── README.md.template ├── feed-item.xml.template ├── feed.xml.template ├── reflection.md.template ├── reflections.md.template └── standalone-reflection.md.template ├── test-data ├── 2016 │ ├── 01a.txt │ ├── 01b.txt │ ├── 02a.txt │ ├── 02b.txt │ ├── 03a.txt │ ├── 04a.txt │ ├── 05a.txt │ ├── 05b.txt │ ├── 06a.txt │ ├── 06b.txt │ ├── 07a.txt │ ├── 07b.txt │ ├── 08a.txt │ ├── 09a.txt │ └── 09b.txt ├── 2017 │ ├── 01a.txt │ ├── 01b.txt │ ├── 02a.txt │ ├── 02b.txt │ ├── 03a.txt │ ├── 03b.txt │ ├── 04a.txt │ ├── 04b.txt │ ├── 05a.txt │ ├── 05b.txt │ ├── 06a.txt │ ├── 06b.txt │ ├── 07a.txt │ ├── 07b.txt │ ├── 08a.txt │ ├── 08b.txt │ ├── 09a.txt │ ├── 09b.txt │ ├── 10b.txt │ ├── 11a.txt │ ├── 11b.txt │ ├── 13a.txt │ ├── 13b.txt │ ├── 14a.txt │ ├── 14b.txt │ ├── 17a.txt │ ├── 18b.txt │ ├── 19a.txt │ ├── 19b.txt │ ├── 20a.txt │ └── 20b.txt ├── 2018 │ ├── 01a.txt │ ├── 01b.txt │ ├── 02a.txt │ ├── 02b.txt │ ├── 03a.txt │ ├── 03b.txt │ ├── 04a.txt │ ├── 04b.txt │ ├── 05a.txt │ ├── 05b.txt │ ├── 06a.txt │ ├── 06b.txt │ ├── 07a.txt │ ├── 07b.txt │ ├── 08a.txt │ ├── 08b.txt │ ├── 09a.txt │ ├── 10b.txt │ ├── 11a.txt │ ├── 11b.txt │ ├── 14a.txt │ ├── 14b.txt │ ├── 15a.txt │ ├── 15b.txt │ ├── 17a.txt │ ├── 18a.txt │ ├── 20a.txt │ ├── 22a.txt │ ├── 22b.txt │ ├── 23a.txt │ ├── 23b.txt │ ├── 24a.txt │ └── 24b.txt ├── 2019 │ ├── 01a.txt │ ├── 01b.txt │ ├── 02a.txt │ ├── 03a.txt │ ├── 03b.txt │ ├── 04a.txt │ ├── 04b.txt │ ├── 05a.txt │ ├── 06a.txt │ ├── 06b.txt │ ├── 07a.txt │ ├── 07b.txt │ ├── 08a.txt │ ├── 09a.txt │ ├── 10a.txt │ ├── 10b.txt │ ├── 12a.txt │ ├── 12b.txt │ ├── 14a.txt │ ├── 14b.txt │ ├── 16a.txt │ ├── 16b.txt │ ├── 18a.txt │ ├── 18b.txt │ ├── 20a.txt │ ├── 20b.txt │ └── 24b.txt ├── 2020 │ ├── 01a.txt │ ├── 01b.txt │ ├── 02a.txt │ ├── 02b.txt │ ├── 04a.txt │ ├── 04b.txt │ ├── 05a.txt │ ├── 07a.txt │ ├── 07b.txt │ ├── 09a.txt │ ├── 09b.txt │ ├── 10a.txt │ ├── 10b.txt │ ├── 11a.txt │ ├── 11b.txt │ ├── 12a.txt │ ├── 12b.txt │ ├── 13a.txt │ ├── 13b.txt │ ├── 14a.txt │ ├── 14b.txt │ ├── 15a.txt │ ├── 15b.txt │ ├── 16a.txt │ ├── 16b.txt │ ├── 17a.txt │ ├── 17b.txt │ ├── 18a.txt │ ├── 18b.txt │ ├── 19a.txt │ ├── 19b.txt │ ├── 20a.txt │ ├── 20b.txt │ ├── 21a.txt │ ├── 21b.txt │ ├── 22a.txt │ ├── 22b.txt │ ├── 23a.txt │ ├── 23b.txt │ ├── 24a.txt │ └── 24b.txt └── 2021 │ ├── 01a.txt │ ├── 01b.txt │ ├── 02a.txt │ ├── 02b.txt │ ├── 04a.txt │ ├── 04b.txt │ ├── 05a.txt │ ├── 05b.txt │ ├── 06a.txt │ ├── 06b.txt │ ├── 07a.txt │ ├── 07b.txt │ ├── 08a.txt │ ├── 08b.txt │ ├── 09a.txt │ ├── 09b.txt │ ├── 10a.txt │ ├── 10b.txt │ ├── 11a.txt │ ├── 11b.txt │ ├── 12a.txt │ ├── 12b.txt │ ├── 13a.txt │ ├── 14a.txt │ ├── 14b.txt │ ├── 15a.txt │ ├── 15b.txt │ ├── 16a.txt │ ├── 16b.txt │ ├── 17a.txt │ └── 17b.txt └── test └── Spec.hs /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | *~ 3 | /data 4 | /prompt 5 | /aoc-conf.yaml 6 | /logs 7 | /bench-out 8 | /tmp 9 | /scratch 10 | _* 11 | /dist-newstyle 12 | /tags 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Justin Le (c) 2021 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Justin Le nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /aoc2021.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 2.4 2 | name: aoc2021 3 | version: 0.1.0.0 4 | synopsis: Development environment for Advent of Code challenges 5 | description: Scaffolding for an integrated development environment for Advent of Code 6 | challenges. Includes auto-runner, prompt displays and countdowns, and 7 | integrated submissions. 8 | category: Web 9 | homepage: https://github.com/mstksg/advent-of-code-2021#readme 10 | bug-reports: https://github.com/mstksg/advent-of-code-2021/issues 11 | author: Justin Le 12 | maintainer: justin@jle.im 13 | copyright: (c) Justin Le 2021 14 | license: MIT 15 | license-file: LICENSE 16 | build-type: Simple 17 | extra-source-files: 18 | README.md 19 | CHANGELOG.md 20 | 21 | source-repository head 22 | type: git 23 | location: https://github.com/mstksg/advent-of-code-2021.git 24 | 25 | common common-options 26 | build-depends: advent-of-code-api >=0.2.7 27 | , Cabal 28 | , aeson 29 | , ansi-terminal 30 | , base >=4.7 && <5 31 | , bitvec 32 | , advent-of-code-ocr 33 | , bytestring 34 | , comonad 35 | , conduino 36 | , constraints 37 | , constraints-extras 38 | , containers 39 | , criterion 40 | , data-default-class 41 | , data-fix 42 | , data-interval 43 | , data-memocombinators 44 | , deepseq 45 | , dependent-sum 46 | , directory 47 | , distributive 48 | , dlist 49 | , extended-reals 50 | , fgl 51 | , filepath 52 | , fin 53 | , finitary 54 | , finite-typelits 55 | , foldl 56 | , ghc-typelits-natnormalise 57 | , groups 58 | , hashable 59 | , haskeline 60 | , haskell-names 61 | , haskell-src-exts 62 | , heredoc 63 | , hpack 64 | , lens 65 | , linear 66 | , megaparsec 67 | , microlens-th 68 | , monoid-extras 69 | , mtl 70 | , nonempty-containers 71 | , pandoc 72 | , parallel 73 | , parser-combinators 74 | , pointedlist 75 | , profunctors 76 | , psqueues 77 | , recursion-schemes 78 | , safe 79 | , semialign 80 | , semigroupoids 81 | , split 82 | , strict-tuple 83 | , tagsoup 84 | , template-haskell 85 | , text 86 | , th-abstraction 87 | , these 88 | , time 89 | , transformers 90 | , vector 91 | , vector-algorithms 92 | , vector-sized 93 | , yaml 94 | 95 | ghc-options: -Wall 96 | -Wcompat 97 | -Widentities 98 | -Wincomplete-uni-patterns 99 | -Wincomplete-record-updates 100 | -Wno-partial-type-signatures 101 | if impl(ghc >= 8.0) 102 | ghc-options: -Wredundant-constraints 103 | if impl(ghc >= 8.2) 104 | ghc-options: -fhide-source-paths 105 | if impl(ghc >= 8.4) 106 | ghc-options: -Wmissing-export-lists 107 | -Wpartial-fields 108 | if impl(ghc >= 8.8) 109 | ghc-options: -Wmissing-deriving-strategies 110 | 111 | default-language: Haskell2010 112 | default-extensions: 113 | AllowAmbiguousTypes 114 | ApplicativeDo 115 | BangPatterns 116 | BlockArguments 117 | DataKinds 118 | DeriveAnyClass 119 | DeriveFoldable 120 | DeriveFunctor 121 | DeriveGeneric 122 | DeriveTraversable 123 | DerivingStrategies 124 | DerivingVia 125 | EmptyCase 126 | FlexibleContexts 127 | FlexibleInstances 128 | FunctionalDependencies 129 | GADTs 130 | GeneralizedNewtypeDeriving 131 | ImplicitParams 132 | KindSignatures 133 | LambdaCase 134 | MonadComprehensions 135 | MultiParamTypeClasses 136 | MultiWayIf 137 | NumDecimals 138 | OverloadedLabels 139 | PartialTypeSignatures 140 | PatternGuards 141 | PatternSynonyms 142 | PolyKinds 143 | RankNTypes 144 | RecordWildCards 145 | ScopedTypeVariables 146 | StandaloneDeriving 147 | TemplateHaskell 148 | TupleSections 149 | TypeApplications 150 | TypeFamilies 151 | TypeInType 152 | TypeOperators 153 | UndecidableInstances 154 | ViewPatterns 155 | 156 | library 157 | import: common-options 158 | exposed-modules: 159 | AOC 160 | AOC.Challenge 161 | AOC.Challenge.Day01 162 | AOC.Challenge.Day02 163 | AOC.Challenge.Day03 164 | AOC.Challenge.Day04 165 | AOC.Challenge.Day05 166 | AOC.Challenge.Day06 167 | AOC.Challenge.Day07 168 | AOC.Challenge.Day08 169 | AOC.Challenge.Day09 170 | AOC.Challenge.Day10 171 | AOC.Challenge.Day11 172 | AOC.Challenge.Day12 173 | AOC.Challenge.Day13 174 | AOC.Challenge.Day14 175 | AOC.Challenge.Day15 176 | AOC.Challenge.Day16 177 | AOC.Challenge.Day17 178 | AOC.Challenge.Day18 179 | AOC.Challenge.Day19 180 | AOC.Challenge.Day20 181 | AOC.Challenge.Day21 182 | AOC.Challenge.Day22 183 | AOC.Challenge.Day23 184 | AOC.Challenge.Day24 185 | AOC.Challenge.Day25 186 | AOC.Common 187 | AOC.Common.FinitarySet 188 | AOC.Common.Point 189 | AOC.Common.Search 190 | AOC.Discover 191 | AOC.Prelude 192 | AOC.Run 193 | AOC.Run.Config 194 | AOC.Run.Interactive 195 | AOC.Run.Load 196 | AOC.Solver 197 | AOC.Util 198 | AOC.Util.DynoMap 199 | other-modules: 200 | Paths_aoc2021 201 | hs-source-dirs: 202 | src 203 | other-modules: 204 | Paths_aoc2021 205 | 206 | executable aoc2021 207 | import: common-options 208 | main-is: aoc.hs 209 | hs-source-dirs: 210 | app 211 | build-depends: 212 | ansi-terminal 213 | , aoc2021 214 | , base >=4.7 && <5 215 | , containers 216 | , deepseq 217 | , finite-typelits 218 | , microlens 219 | , mtl 220 | , optparse-applicative 221 | default-language: Haskell2010 222 | ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2 223 | 224 | test-suite aoc2021-test 225 | import: common-options 226 | type: exitcode-stdio-1.0 227 | main-is: Spec.hs 228 | hs-source-dirs: 229 | test 230 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 231 | build-depends: 232 | ansi-terminal 233 | , aoc2021 234 | , base >=4.7 && <5 235 | , mtl 236 | default-language: Haskell2010 237 | 238 | benchmark aoc2021-bench 239 | type: exitcode-stdio-1.0 240 | main-is: Bench.hs 241 | hs-source-dirs: 242 | bench 243 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 244 | build-depends: 245 | aoc2021 246 | , base >=4.7 && <5 247 | , mtl 248 | default-language: Haskell2010 249 | -------------------------------------------------------------------------------- /app/aoc.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# OPTIONS_GHC -Werror=incomplete-patterns #-} 3 | 4 | import AOC 5 | import Control.Applicative 6 | import Control.DeepSeq 7 | import Control.Exception 8 | import Control.Lens hiding (argument) 9 | import Control.Monad 10 | import Control.Monad.Except 11 | import Data.Char 12 | import Data.Foldable 13 | import Data.IORef 14 | import Data.Maybe 15 | import Options.Applicative 16 | import System.IO.Error 17 | import Text.Printf 18 | import Text.Read 19 | import qualified Data.List as L 20 | import qualified Data.Map as M 21 | import qualified System.Console.ANSI as ANSI 22 | 23 | data Mode = MRun MainRunOpts 24 | | MView MainViewOpts 25 | | MSubmit MainSubmitOpts 26 | 27 | data Opts = O { _oMode :: !Mode 28 | , _oConfig :: !(Maybe FilePath) 29 | } 30 | 31 | main :: IO () 32 | main = do 33 | inputCache <- newIORef Nothing 34 | O{..} <- execParser $ info (parseOpts inputCache <**> helper) 35 | ( fullDesc 36 | <> header "aoc-dev - Advent of Code interactive development environment" 37 | <> progDesc ("Run, test, bench, challenges from Advent of Code, and view prompts. Available days: " ++ availableDays) 38 | ) 39 | cfg <- configFile $ fromMaybe defConfPath _oConfig 40 | out <- runExceptT $ case _oMode of 41 | MRun mro -> void $ mainRun cfg mro 42 | MView mvo -> void $ mainView cfg mvo 43 | MSubmit mso -> void $ mainSubmit cfg mso 44 | forOf_ _Left out $ \e -> do 45 | withColor ANSI.Vivid ANSI.Red $ 46 | putStrLn "[ERROR]" 47 | mapM_ putStrLn e 48 | where 49 | availableDays = L.intercalate ", " 50 | . map (show . dayInt) 51 | . M.keys 52 | $ challengeMap 53 | 54 | 55 | -- --------- 56 | -- | Parsers 57 | -- --------- 58 | 59 | readDay :: ReadM Day 60 | readDay = eitherReader $ \s -> do 61 | n <- maybe (Left "Invalid day") Right $ readMaybe s 62 | maybe (Left "Day out of range") Right $ mkDay n 63 | 64 | readPart :: ReadM Part 65 | readPart = eitherReader $ \case 66 | "" -> Left "No part" 67 | "a" -> Right Part1 68 | "b" -> Right Part2 69 | _ -> Left "Invalid part (not 'a' or 'b')" 70 | 71 | parseChallengeSpec :: Parser ChallengeSpec 72 | parseChallengeSpec = do 73 | d <- argument pDay ( metavar "DAY" 74 | <> help "Day of challenge (1 - 25)" 75 | ) 76 | p <- argument pPart ( metavar "PART" 77 | <> help "Challenge part (a, b, c, etc.)" 78 | ) 79 | pure $ CS d p 80 | where 81 | pDay = readDay 82 | pPart = readPart 83 | 84 | parseTestSpec :: Parser TestSpec 85 | parseTestSpec = do 86 | d <- argument pDay ( metavar "DAY" 87 | <> help "Day of challenge (1 - 25), or \"all\"" 88 | ) 89 | p <- optional $ argument pPart ( metavar "PART" 90 | <> help "Challenge part (a, b, c, etc.)" 91 | ) 92 | pure $ case d of 93 | Just d' -> case p of 94 | Just p' -> TSDayPart (CS d' p') 95 | Nothing -> TSDayAll d' 96 | Nothing -> TSAll 97 | where 98 | pDay = asum [ Nothing <$ maybeReader (guard . (== "all") . map toLower) 99 | , Just <$> readDay 100 | ] 101 | pPart = readPart 102 | 103 | parseOpts 104 | :: IORef (Maybe (Maybe (Day, String))) 105 | -> Parser Opts 106 | parseOpts inputCache = do 107 | _oConfig <- optional . strOption . mconcat $ 108 | [ long "config" 109 | , metavar "PATH" 110 | , help $ printf "Path to configuration file (default: %s)" defConfPath 111 | ] 112 | _oMode <- subparser . mconcat $ 113 | [ command "run" $ 114 | info (MRun <$> parseRun <**> helper) (progDesc "Run, test, and benchmark challenges" ) 115 | , command "view" $ 116 | info (MView <$> parseView <**> helper) (progDesc "View a prompt for a given challenge" ) 117 | , command "submit" $ 118 | info (MSubmit <$> parseSubmit <**> helper) (progDesc "Test and submit answers for challenges") 119 | , command "test" $ 120 | info (MRun <$> parseTest <**> helper) (progDesc "Alias for run --test" ) 121 | , command "bench" $ 122 | info (MRun <$> parseBench <**> helper) (progDesc "Alias for run --bench" ) 123 | , command "countdown" $ 124 | info (MView <$> parseCountdown <**> helper) (progDesc "Alias for view --countdown" ) 125 | ] 126 | pure O{..} 127 | where 128 | parseRun :: Parser MainRunOpts 129 | parseRun = do 130 | _mroSpec <- parseTestSpec 131 | _mroActual <- fmap not . switch . mconcat $ 132 | [ long "skip" 133 | , short 's' 134 | , help "Do not run the actual input, but still run tests or benchmarks" 135 | ] 136 | _mroTest <- switch . mconcat $ 137 | [ long "test" 138 | , short 't' 139 | , help "Run sample tests" 140 | ] 141 | _mroBench <- switch . mconcat $ 142 | [ long "bench" 143 | , short 'b' 144 | , help "Run benchmarks" 145 | ] 146 | _mroLock <- switch . mconcat $ 147 | [ long "lock" 148 | , short 'l' 149 | , help "Lock in results as \"correct\" answers" 150 | ] 151 | takeStdin <- switch . mconcat $ 152 | [ long "stdin" 153 | , help "Take first input from stdin instead of input directory" 154 | ] 155 | pure $ let _mroInput 156 | | takeStdin = \d _ -> pullStdin inputCache d 157 | | otherwise = \_ _ -> pure Nothing 158 | in MRO{..} 159 | parseView :: Parser MainViewOpts 160 | parseView = do 161 | _mvoSpec <- parseTestSpec 162 | _mvoWait <- switch . mconcat $ 163 | [ long "countdown" 164 | , short 'c' 165 | , help "Countdown until release if not yet available" 166 | ] 167 | pure MVO{..} 168 | parseSubmit :: Parser MainSubmitOpts 169 | parseSubmit = do 170 | _msoSpec <- parseChallengeSpec 171 | _msoTest <- fmap not . switch . mconcat $ 172 | [ long "skip-tests" 173 | , short 's' 174 | , help "Skip running tests before submission" 175 | ] 176 | _msoForce <- switch . mconcat $ 177 | [ long "force" 178 | , short 'f' 179 | , help "Always submit, even if tests fail" 180 | ] 181 | _msoLock <- fmap not . switch . mconcat $ 182 | [ long "no-lock" 183 | , short 'n' 184 | , help "Do not lock in answer, even if correct submission was received" 185 | ] 186 | pure MSO{..} 187 | parseTest :: Parser MainRunOpts 188 | parseTest = parseRun & mapped . mroTest .~ True 189 | parseBench :: Parser MainRunOpts 190 | parseBench = parseRun & mapped . mroBench .~ True 191 | parseCountdown :: Parser MainViewOpts 192 | parseCountdown = parseView & mapped . mvoWait .~ True 193 | 194 | pullStdin 195 | :: IORef (Maybe (Maybe (Day, String))) -- ^ Nothing: first time; Just Nothing: failed forever. 196 | -> Day 197 | -> IO (Maybe String) 198 | pullStdin inputCache d = readIORef inputCache >>= \case 199 | Nothing -> do 200 | out <- fmap eitherToMaybe . tryJust (guard . isEOFError) $ 201 | evaluate . force =<< getContents 202 | writeIORef inputCache . Just $ (d,) <$> out 203 | pure out 204 | Just old -> pure . firstJust (\(d',o) -> o <$ guard (d == d')) $ old 205 | -------------------------------------------------------------------------------- /bench/Bench.hs: -------------------------------------------------------------------------------- 1 | 2 | import AOC 3 | import Control.Monad.Except 4 | 5 | main :: IO () 6 | main = do 7 | cfg <- configFile defConfPath 8 | void . runExceptT . mainRun cfg $ (defaultMRO TSAll) 9 | { _mroBench = True 10 | } 11 | -------------------------------------------------------------------------------- /draft/day03.md: -------------------------------------------------------------------------------- 1 | I'm going to solve Part 2 first, and then try to see if we can use the 2 | mechanisms we build in order to also solve Part 1! 3 | 4 | For part 2, the problem statement made me visualize the entire collection as a 5 | binary tree -- a [prefix trie][trie blog], one of my favorite data structures! 6 | 7 | [trie blog]: https://blog.jle.im/entry/tries-with-recursion-schemes.html 8 | 9 | For example, the list: 10 | 11 | ``` 12 | 001 13 | 011 14 | 101 15 | 111 16 | ``` 17 | 18 | Can be represented as: 19 | 20 | ``` 21 | ,-[] 22 | ,-* 23 | , `-[] 24 | ,-* 25 | , ` ,-[] 26 | , `-* 27 | , `-[] 28 | * 29 | ` ,-[] 30 | ` ,-* 31 | ` , `-[] 32 | `-* 33 | ` ,-[] 34 | `-* 35 | `-[] 36 | ``` 37 | -------------------------------------------------------------------------------- /reflections-out/day01.md: -------------------------------------------------------------------------------- 1 | Day 1 2 | === 3 | 4 | 9 | 10 | *[all][reflections]* / *1* / *[2][day02]* 11 | 12 | [reflections]: https://github.com/mstksg/advent-of-code-2021/blob/master/reflections.md 13 | [day02]: https://github.com/mstksg/advent-of-code-2021/blob/master/reflections-out/day02.md 14 | 15 | [Available as an RSS Feed][rss] 16 | 17 | [rss]: http://feeds.feedburner.com/jle-advent-of-code-2021 18 | 19 | *[Prompt][d01p]* / *[Code][d01g]* / *[Rendered][d01h]* 20 | 21 | [d01p]: https://adventofcode.com/2021/day/1 22 | [d01g]: https://github.com/mstksg/advent-of-code-2021/blob/master/src/AOC/Challenge/Day01.hs 23 | [d01h]: https://mstksg.github.io/advent-of-code-2021/src/AOC.Challenge.Day01.html 24 | 25 | As a simple data processing thing, this one shines pretty well in Haskell :) 26 | 27 | Assuming we have a list, we can get the consecutive items with a combination of 28 | `zipWith` and `drop`. Then we can just count how many pairs of items match the 29 | predicate (strictly increasing): 30 | 31 | ```haskell 32 | countIncreasesPart1 :: [Int] -> Int 33 | countIncreasesPart1 xs = length (filter (== True) (zipWith (<) xs (drop 1 xs))) 34 | ``` 35 | 36 | Yes, `filter (== True)` is the same as `filter id`, but it's a bit easier to 37 | read this way :) 38 | 39 | Remember that if `xs` is `[2,4,6,5]`, then `drop 1 xs` is `[4,6,5]`, and so 40 | `zip xs (drop 1 xs)` is `[(2,4), (4,6), (6,5)]` So `zipWith (<) xs (drop 1 41 | xs)` is `[True, True, False]`. So counting all of the `True` items yields the 42 | right answer! 43 | 44 | Part 2 is very similar, but we need to check if items *three* positions apart 45 | are increasing. That's because for each window, the sum of the window is 46 | increasing if the new item gained is bigger than the item that was just lost. 47 | So for an example like `[3,5,6,4,7,8]`, as we move from `[3,5,6]` to `[5,6,4]`, 48 | we only need to check if `4` is greater than `3`. So we only need to compare 4 49 | and 3, 7 and 5, and then 8 and 6. 50 | 51 | ```haskell 52 | countIncreasesPart2 :: [Int] -> Int 53 | countIncreasesPart2 xs = length (filter (== True) (zipWith (<) xs (drop 3 xs))) 54 | ``` 55 | 56 | We just need to replace `drop 1 xs` with `drop 3 xs` to compare three-away 57 | items. 58 | 59 | Anyway the parsing in Haskell is straightforward, at least -- we can just do 60 | `map read . lines`, to split our input into lines and then map `read :: String 61 | -> Int` over each line. Ta dah! Fun start to the year :) 62 | 63 | 64 | *[Back to all reflections for 2021][reflections]* 65 | 66 | ## Day 1 Benchmarks 67 | 68 | ``` 69 | >> Day 01a 70 | benchmarking... 71 | time 26.81 μs (26.45 μs .. 27.29 μs) 72 | 0.996 R² (0.991 R² .. 1.000 R²) 73 | mean 26.75 μs (26.43 μs .. 27.57 μs) 74 | std dev 1.478 μs (130.1 ns .. 2.625 μs) 75 | variance introduced by outliers: 62% (severely inflated) 76 | 77 | * parsing and formatting times excluded 78 | 79 | >> Day 01b 80 | benchmarking... 81 | time 27.02 μs (25.16 μs .. 28.74 μs) 82 | 0.966 R² (0.956 R² .. 0.979 R²) 83 | mean 26.40 μs (25.02 μs .. 27.78 μs) 84 | std dev 4.752 μs (3.640 μs .. 6.699 μs) 85 | variance introduced by outliers: 95% (severely inflated) 86 | 87 | * parsing and formatting times excluded 88 | ``` 89 | 90 | -------------------------------------------------------------------------------- /reflections/day01.md: -------------------------------------------------------------------------------- 1 | As a simple data processing thing, this one shines pretty well in Haskell :) 2 | 3 | Assuming we have a list, we can get the consecutive items with a combination of 4 | `zipWith` and `drop`. Then we can just count how many pairs of items match the 5 | predicate (strictly increasing): 6 | 7 | ```haskell 8 | countIncreasesPart1 :: [Int] -> Int 9 | countIncreasesPart1 xs = length (filter (== True) (zipWith (<) xs (drop 1 xs))) 10 | ``` 11 | 12 | Yes, `filter (== True)` is the same as `filter id`, but it's a bit easier to 13 | read this way :) 14 | 15 | Remember that if `xs` is `[2,4,6,5]`, then `drop 1 xs` is `[4,6,5]`, and so 16 | `zip xs (drop 1 xs)` is `[(2,4), (4,6), (6,5)]` So `zipWith (<) xs (drop 1 17 | xs)` is `[True, True, False]`. So counting all of the `True` items yields the 18 | right answer! 19 | 20 | Part 2 is very similar, but we need to check if items *three* positions apart 21 | are increasing. That's because for each window, the sum of the window is 22 | increasing if the new item gained is bigger than the item that was just lost. 23 | So for an example like `[3,5,6,4,7,8]`, as we move from `[3,5,6]` to `[5,6,4]`, 24 | we only need to check if `4` is greater than `3`. So we only need to compare 4 25 | and 3, 7 and 5, and then 8 and 6. 26 | 27 | ```haskell 28 | countIncreasesPart2 :: [Int] -> Int 29 | countIncreasesPart2 xs = length (filter (== True) (zipWith (<) xs (drop 3 xs))) 30 | ``` 31 | 32 | We just need to replace `drop 1 xs` with `drop 3 xs` to compare three-away 33 | items. 34 | 35 | Anyway the parsing in Haskell is straightforward, at least -- we can just do 36 | `map read . lines`, to split our input into lines and then map `read :: String 37 | -> Int` over each line. Ta dah! Fun start to the year :) 38 | -------------------------------------------------------------------------------- /reflections/day02.md: -------------------------------------------------------------------------------- 1 | Day 2 has a satisfying "unified" solution for both parts that can be derived 2 | from group theory! The general group (or monoid) design pattern that I've gone 3 | over [in many Advent of Code blog posts][day2-monoid-posts] is that we can 4 | think of our "final action" as simply a "squishing" of individual smaller 5 | actions. The revelation is that our individual smaller actions are 6 | "combinable" to yield something of the *same type*, so solving the puzzle is 7 | generating all of the smaller actions repeatedly combining them to yield the 8 | final action. 9 | 10 | [day2-monoid-posts]: https://blog.jle.im/entries/series/+advent-of-code.html 11 | 12 | In both of these parts, we can think of squishing a bunch of small actions 13 | (`forward`, `up`, `down`) into a mega-action, which represents the final trip 14 | as one big step. So here is our general solver: 15 | 16 | ```haskell 17 | -- | A type for x-y coordinates/2d vectors 18 | data Point = P { pX :: Int, pY :: Int } 19 | 20 | day02 21 | :: Monoid r 22 | => (Int -> r) -- ^ construct a forward action 23 | -> (Int -> r) -- ^ construct an up/down action 24 | -> (r -> Point -> Point) -- ^ how to apply an action to a point 25 | -> String 26 | -> Point -- ^ the final point 27 | day02 mkForward mkUpDown applyAct = 28 | (`applyAct` P 0 0) -- get the answer by applying from 0,0 29 | . foldMap (parseAsDir . words) -- convert each line into the action and merge 30 | . lines -- split up lines 31 | where 32 | parseAsDir (dir:n:_) = case dir of 33 | "forward" -> mkForward amnt 34 | "down" -> mkUpDown amnt 35 | "up" -> mkUpDown (-amnt) 36 | where 37 | amnt = read n 38 | ``` 39 | 40 | And there we have it! A solver for both parts 1 and 2. Now we just need to 41 | pick the Monoid :) 42 | 43 | For part 1, the choice of monoid is simple: our final action is a translation 44 | by a vector, so it makes sense that the intermediate actions would be vectors as 45 | well -- composing actions means adding those vectors together. 46 | 47 | ```haskell 48 | data Vector = V { dX :: Int, dY :: Int } 49 | 50 | instance Semigroup Vector where 51 | V dx dy <> V dx' dy' = V (dx + dx') (dy + dy') 52 | instance Monoid Vector where 53 | mempty = V 0 0 54 | 55 | day02a :: String -> Int 56 | day02a = day02 57 | (\dx -> V dx 0) -- forward is just a horizontal vector 58 | (\dy -> V 0 dy) -- up/down is a vertical vector 59 | (\(V dx dy) (P x0 y0) -> P (x0 + dx) (y0 + dy)) 60 | ``` 61 | 62 | Part 2 is a little trickier because we have to keep track of dx, dy *and* aim. 63 | So we can think of our action as manipulating a `Point` as well as an `Aim`, 64 | and combining them together. 65 | 66 | ```haskell 67 | newtype Aim = Aim Int 68 | 69 | instance Semigroup Aim where 70 | Aim a <> Aim b = Aim (a + b) 71 | instance Monoid Aim where 72 | mempty = Aim 0 73 | ``` 74 | 75 | So our "action" looks like: 76 | 77 | ```haskell 78 | data Part2Action = P2A { p2Vector :: Vector, p2Aim :: Aim } 79 | ``` 80 | 81 | However, it's not exactly obvious how to turn this into a monoid. How do we 82 | combine two `Part2Action`s to create a new one, in a way that respects the 83 | logic of part 2? Simply adding them point-wise does not do the trick, because 84 | we have to somehow also get the `Aim` to factor into the new y value. 85 | 86 | Group theory to the rescue! Using the [monoid-extras][] library, we can 87 | can say that `Aim` encodes a "vector transformer". Applying an aim means adding 88 | the dy value by the aim value multiplied the dx component. 89 | 90 | [monoid-extras]: https://hackage.haskell.org/package/monoid-extras-0.6.1 91 | 92 | ```haskell 93 | instance Action Aim Vector where 94 | act (Aim a) = moveDownByAimFactor 95 | where 96 | moveDownByAimFactor (V dx dy) = V dx (dy + a * dx) 97 | ``` 98 | 99 | Because of this, we can now pair together `Vector` and `Aim` as a [semi-direct 100 | product][]: If we pair up our monoid (`Vector`) with a "point transformer" 101 | (`Aim`), then `Semi Vector Aim` is a monoid that contains both (like our 102 | `Part2Action` above) but also provides a `Monoid` instance that "does the right 103 | thing" (adds vector, adds aim, and also makes sure the aim action gets applied 104 | correctly when adding vectors) thanks to group theory. 105 | 106 | [semi-direct product]: https://hackage.haskell.org/package/monoid-extras-0.6.1/docs/Data-Monoid-SemiDirectProduct.html 107 | 108 | ```haskell 109 | -- constructors/deconstructors that monoid-extras gives us 110 | inject :: Vector -> Semi Vector Aim 111 | embed :: Aim -> Semi Vector Aim 112 | untag :: Semi Vector Aim -> Vector 113 | 114 | day02b :: String -> Int 115 | day02b = day02 116 | (\dx -> inject $ V dx 0) -- forward just contributs a vector motion 117 | (\a -> embed $ Aim a ) -- up/down just adjusts the aim 118 | (\sdp (P x0 y0) -> 119 | let V dx dy = untag sdp 120 | in P (x0 + dx) (y0 + dy) 121 | ) 122 | ``` 123 | 124 | And that's it, we're done, thanks to the power of group theory! We identified 125 | that our final monoid must somehow contain both components (`Vector`, and 126 | `Aim`), but did not know how the two could come together to form a mega-monoid 127 | of both. However, because we saw that `Aim` also gets accumulated while also 128 | acting as a "point transformer", we can describe how it transforms points (with 129 | the `Action` instance) and so we can use `Semi` (semi-direct product) to encode 130 | our action with a `Monoid` instance that does the right thing. 131 | 132 | What was the point of this? Well, we were able to unify both parts 1 and 2 to 133 | be solved in the same overall method, just by picking a different monoid for 134 | each part. With only two parts, it might not seem that worth it to abstract, 135 | but maybe if there were more we could experiment with what other neat monoids 136 | we could express our solution as! But, a major advantage we reap now is that, 137 | because each action combines into other actions (associatively), we could do 138 | all of this in parallel! If our list of actions was very long, we could 139 | distribute the work over multiple cores or computers and re-combine like a 140 | map-reduce. There's just something very satisfying about having the "final 141 | action" be of the same type as our intermediate actions. With that 142 | revelation, we open the door to the entire field of monoid-based optimizations 143 | and pre-made algorithms (like `Semi`) 144 | 145 | (Thanks to `mniip` in libera irc's *#adventofcode* channel for helping me 146 | express this in terms of a semi-direct product! My original attempt used a 4x4 147 | matrix that ended up doing the same thing after some symbolic analysis.) 148 | 149 | (Thanks too to \@lysxia on twitter for pointing out a nicer way of interpreting 150 | the action in terms of how it acts on points!) 151 | -------------------------------------------------------------------------------- /script/generate_days.hs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stack 2 | -- stack --install-ghc runghc --resolver lts-16 --package template --package text --package filepath --package directory -- -Wall 3 | 4 | {-# LANGUAGE LambdaCase #-} 5 | {-# LANGUAGE OverloadedStrings #-} 6 | 7 | import Control.Monad 8 | import Data.Text.Template 9 | import System.FilePath 10 | import System.Directory 11 | import Text.Printf 12 | import qualified Data.Text as T 13 | import qualified Data.Text.IO as T 14 | import qualified Data.Text.Lazy.IO as TL 15 | 16 | outRoot :: FilePath 17 | outRoot = "src/AOC/Challenge" 18 | 19 | main :: IO () 20 | main = do 21 | temp <- template <$> T.readFile "template/DayXX.hs" 22 | forM_ [1..25] $ \i -> do 23 | let newFilePath = outRoot printf "Day%02d.hs" i 24 | Just newFile = renderA temp (ctx i) 25 | skip <- doesFileExist newFilePath 26 | unless skip $ 27 | TL.writeFile newFilePath newFile 28 | 29 | ctx :: Int -> ContextA Maybe 30 | ctx i = \case 31 | "day" -> Just . T.pack $ printf "%02d" i 32 | "day_short" -> Just . T.pack $ printf "%d" i 33 | _ -> Nothing 34 | -------------------------------------------------------------------------------- /script/open_files.vim: -------------------------------------------------------------------------------- 1 | " 2 | " open_files.vim 3 | " ============== 4 | " 5 | " use: 6 | " 7 | " :source script/open_files.vim 8 | " 9 | " to load the function into scope, where you can call with: 10 | " 11 | " :call OpenAoC(day) 12 | " 13 | " If you use the :source command in a buffer where the filename has a number 14 | " in it (like Day16.hs), this will automatically open all the files associated 15 | " with that day. 16 | " 17 | " Change s:year below to open test data for a different year 18 | " 19 | 20 | let s:year = 2021 21 | 22 | function! OpenAoC(day) 23 | let l:daystr = printf("%02d",a:day) 24 | let l:yearstr = printf("%04d",s:year) 25 | let l:files = ["src/AOC/Challenge/Day" . l:daystr . ".hs", 26 | \"data/" . l:daystr . ".txt", 27 | \"prompt/" . l:daystr . "a.md", 28 | \"prompt/" . l:daystr . "b.md", 29 | \"data/code-blocks/" . l:daystr . "a.txt", 30 | \"data/code-blocks/" . l:daystr . "b.txt", 31 | \"test-data/" . l:yearstr . "/" . l:daystr . "a.txt", 32 | \"test-data/" . l:yearstr . "/" . l:daystr . "b.txt", 33 | \"reflections/day" . l:daystr . ".md", 34 | \"bench-out/day" . l:daystr . ".txt" 35 | \] 36 | 37 | for fn in reverse(l:files) 38 | execute "e " . fnameescape(fn) 39 | endfor 40 | endfunction 41 | 42 | let s:buffday = str2nr(matchstr(expand('%:t:r'), '\d\+')) 43 | 44 | if (s:buffday == 0) 45 | echo "no valid file found in buffer; use :call OpenAoC(day) to open a day" 46 | else 47 | echo "found day" . string(s:buffday) 48 | call OpenAoC(s:buffday) 49 | endif 50 | -------------------------------------------------------------------------------- /src/AOC.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC 3 | -- Copyright : (c) Justin Le 2021 4 | -- License : BSD3 5 | -- 6 | -- Maintainer : justin@jle.im 7 | -- Stability : experimental 8 | -- Portability : non-portable 9 | -- 10 | -- Single-stop entry point for the library's functionality and all 11 | -- challenge solutions. 12 | -- 13 | 14 | module AOC ( 15 | module AOC 16 | ) where 17 | 18 | import AOC.Challenge as AOC 19 | import AOC.Run as AOC 20 | import AOC.Run.Config as AOC 21 | import AOC.Run.Interactive as AOC 22 | import AOC.Run.Load as AOC 23 | import AOC.Solver as AOC 24 | import AOC.Util as AOC 25 | 26 | -------------------------------------------------------------------------------- /src/AOC/Challenge.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | {-# OPTIONS_GHC -Wno-dodgy-exports #-} 3 | {-# OPTIONS_GHC -Wno-unused-imports #-} 4 | 5 | -- | 6 | -- Module : AOC.Challenge 7 | -- Copyright : (c) Justin Le 2021 8 | -- License : BSD3 9 | -- 10 | -- Maintainer : justin@jle.im 11 | -- Stability : experimental 12 | -- Portability : non-portable 13 | -- 14 | -- Gather together all challenges and collect them into a single map. 15 | -- 16 | 17 | module AOC.Challenge ( 18 | module AOC 19 | , ChallengeMap 20 | , ChallengeSpec(..), Part(..) 21 | , challengeMap 22 | , lookupSolution 23 | , Day(..), dayInt, mkDay, mkDay_ 24 | , solSpec 25 | , charPart 26 | ) where 27 | 28 | import AOC.Challenge.Day01 as AOC 29 | import AOC.Challenge.Day02 as AOC 30 | import AOC.Challenge.Day03 as AOC 31 | import AOC.Challenge.Day04 as AOC 32 | import AOC.Challenge.Day05 as AOC 33 | import AOC.Challenge.Day06 as AOC 34 | import AOC.Challenge.Day07 as AOC 35 | import AOC.Challenge.Day08 as AOC 36 | import AOC.Challenge.Day09 as AOC 37 | import AOC.Challenge.Day10 as AOC 38 | import AOC.Challenge.Day11 as AOC 39 | import AOC.Challenge.Day12 as AOC 40 | import AOC.Challenge.Day13 as AOC 41 | import AOC.Challenge.Day14 as AOC 42 | import AOC.Challenge.Day15 as AOC 43 | import AOC.Challenge.Day16 as AOC 44 | import AOC.Challenge.Day17 as AOC 45 | import AOC.Challenge.Day18 as AOC 46 | import AOC.Challenge.Day19 as AOC 47 | import AOC.Challenge.Day20 as AOC 48 | import AOC.Challenge.Day21 as AOC 49 | import AOC.Challenge.Day22 as AOC 50 | import AOC.Challenge.Day23 as AOC 51 | import AOC.Challenge.Day24 as AOC 52 | import AOC.Challenge.Day25 as AOC 53 | 54 | import AOC.Discover 55 | import AOC.Solver 56 | import Advent 57 | import Control.Monad 58 | import Data.Finite 59 | import Data.Map (Map) 60 | import qualified Data.Map as M 61 | 62 | -- | A map of all challenges. 63 | challengeMap :: ChallengeMap 64 | challengeMap = mkChallengeMap $$(solutionList "src/AOC/Challenge") 65 | 66 | -- | Lookup up a solution from a 'ChallengeMap' 67 | lookupSolution :: ChallengeSpec -> Map Day (Map Part a) -> Maybe a 68 | lookupSolution CS{..} = M.lookup _csPart <=< M.lookup _csDay 69 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day01.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day01 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 1. See "AOC.Solver" for the types used in this module! 9 | -- 10 | 11 | module AOC.Challenge.Day01 ( 12 | day01a 13 | , day01b 14 | ) where 15 | 16 | import AOC.Solver ((:~>)(..)) 17 | import AOC.Common (laggedPairs, countTrue) 18 | import Text.Read (readMaybe) 19 | 20 | parseInput :: String -> Maybe [Int] 21 | parseInput = traverse readMaybe . lines 22 | 23 | countIncreases :: Int -> [Int] -> Int 24 | countIncreases n = countTrue (uncurry (<)) . laggedPairs n 25 | 26 | day01a :: [Int] :~> Int 27 | day01a = MkSol 28 | { sParse = parseInput 29 | , sShow = show 30 | , sSolve = Just . countIncreases 1 31 | } 32 | 33 | day01b :: [Int] :~> Int 34 | day01b = MkSol 35 | { sParse = parseInput 36 | , sShow = show 37 | , sSolve = Just . countIncreases 3 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day02.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day02 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 2. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day02 ( 11 | day02a 12 | , day02b 13 | ) where 14 | 15 | import AOC.Common.Point (Point) 16 | import AOC.Solver ((:~>)(..)) 17 | import Data.Semigroup (Sum(..)) 18 | import Linear.V2 (V2(..)) 19 | import Text.Read (readMaybe) 20 | import qualified Data.Monoid.Action as Mo 21 | import qualified Data.Monoid.SemiDirectProduct.Strict as Mo 22 | 23 | day02a :: [Sum Point] :~> Int 24 | day02a = day02 25 | (\x -> Sum $ V2 x 0) 26 | (\y -> Sum $ V2 0 y) 27 | getSum 28 | 29 | day02b :: [Mo.Semi (Sum Point) Aim] :~> Int 30 | day02b = day02 31 | (\x -> Mo.inject (Sum (V2 x 0))) 32 | (\a -> Mo.embed (Aim a)) 33 | (getSum . Mo.untag) 34 | 35 | 36 | 37 | -- | The difference between Part 1 and Part 2 is just a different monoid 38 | day02 39 | :: Monoid r 40 | => (Int -> r) -- ^ forward 41 | -> (Int -> r) -- ^ up/down 42 | -> (r -> Point) -- ^ re-extract position 43 | -> [r] :~> Int 44 | day02 f g ext = MkSol 45 | { sParse = traverse parseAsDir . lines 46 | , sShow = show 47 | , sSolve = Just . product @V2 . ext . mconcat 48 | } 49 | where 50 | parseAsDir ln = do 51 | dir:n:_ <- Just $ words ln 52 | amnt <- readMaybe n 53 | case dir of 54 | "forward" -> Just $ f amnt 55 | "down" -> Just $ g amnt 56 | "up" -> Just $ g (-amnt) 57 | _ -> Nothing 58 | 59 | 60 | newtype Aim = Aim Int 61 | deriving via Sum Int instance Semigroup Aim 62 | deriving via Sum Int instance Monoid Aim 63 | 64 | instance Mo.Action Aim (Sum Point) where 65 | act (Aim a) (Sum (V2 x y)) = Sum (V2 x (y + a * x)) 66 | 67 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day03.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day03 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 3. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day03 ( 11 | day03a 12 | , day03b 13 | ) where 14 | 15 | import AOC.Common (traverseLines) 16 | import AOC.Solver ((:~>)(..)) 17 | import Control.DeepSeq (NFData) 18 | import Control.Lens (Prism', (^?!), review, preview, prism') 19 | import Control.Monad ((<=<)) 20 | import Data.Coerce (coerce) 21 | import Data.Functor.Foldable (hylo) 22 | import Data.Functor.Foldable.TH (makeBaseFunctor) 23 | import Data.List.NonEmpty (NonEmpty(..)) 24 | import Data.Semigroup (Sum(..)) 25 | import Data.These (mergeThese) 26 | import GHC.Generics (Generic) 27 | import Linear.V2 (V2(..)) 28 | import Numeric.Lens (base) 29 | import qualified Data.DList as DL 30 | import qualified Data.List.NonEmpty as NE 31 | import qualified Data.Zip as Z 32 | 33 | data Bit = Zero | One 34 | deriving stock (Eq, Ord, Show, Generic) 35 | deriving anyclass NFData 36 | 37 | _Bit :: Prism' Char Bit 38 | _Bit = prism' (\case Zero -> '0'; One -> '1') 39 | (`lookup` [('0', Zero), ('1', One)]) 40 | 41 | flipBit :: Bit -> Bit 42 | flipBit Zero = One 43 | flipBit One = Zero 44 | 45 | data BinTrie = 46 | BTLeaf [Bit] 47 | | BTNode (Maybe BinTrie) (Maybe BinTrie) 48 | deriving stock Show 49 | makeBaseFunctor ''BinTrie 50 | 51 | day03a :: NonEmpty [Bit] :~> _ 52 | day03a = MkSol 53 | { sParse = NE.nonEmpty <=< traverseLines (traverse (preview _Bit)) 54 | , sShow = \xs -> show @Int 55 | let ys = map flipBit xs 56 | toBin str = map (review _Bit) str ^?! base 2 57 | in toBin xs * toBin ys 58 | , sSolve = Just . map pickMost . snd . hylo part1Alg buildTrieCoalg 59 | } 60 | where 61 | pickMost (V2 x y) 62 | | x > y = Zero 63 | | otherwise = One 64 | 65 | day03b :: NonEmpty [Bit] :~> ([Bit], [Bit]) 66 | day03b = MkSol 67 | { sParse = NE.nonEmpty <=< traverseLines (traverse (preview _Bit)) 68 | , sShow = \(o2, co2) -> show @Int 69 | let toBin str = map (review _Bit) str ^?! base 2 70 | in toBin o2 * toBin co2 71 | , sSolve = Just . snd . hylo part2Alg buildTrieCoalg 72 | } 73 | 74 | buildTrieCoalg :: NonEmpty [Bit] -> BinTrieF (NonEmpty [Bit]) 75 | buildTrieCoalg (theOne :| theRest) 76 | | null theRest = BTLeafF theOne 77 | | otherwise = 78 | let V2 zeroes ones = peelOff (theOne : theRest) 79 | in BTNodeF (NE.nonEmpty zeroes) (NE.nonEmpty ones) 80 | 81 | part1Alg 82 | :: BinTrieF (Int, [V2 Int]) 83 | -> (Int, [V2 Int]) 84 | part1Alg = \case 85 | BTLeafF xs -> (1, map singleCount xs) 86 | BTNodeF zeroes ones -> 87 | let (Sum numZeroes, zeroAmts) = foldMap coerce zeroes 88 | (Sum numOnes , oneAmts ) = foldMap coerce ones 89 | newNum = numZeroes + numOnes 90 | newAmts = V2 numZeroes numOnes 91 | : Z.alignWith (mergeThese (+)) zeroAmts oneAmts 92 | in (newNum, newAmts) 93 | where 94 | singleCount Zero = V2 1 0 95 | singleCount One = V2 0 1 96 | 97 | -- | Collect both the oxygen (fst) and co2 (snd) answers at the same time 98 | -- 99 | -- The first item int he tuple is the number of items under the given 100 | -- branch 101 | part2Alg 102 | :: BinTrieF (Int, ([Bit], [Bit])) 103 | -> (Int, ([Bit], [Bit])) 104 | part2Alg = \case 105 | BTLeafF xs -> (1, (xs, xs)) 106 | BTNodeF zeroes ones -> 107 | let numZeroes = maybe 0 fst zeroes 108 | numOnes = maybe 0 fst ones 109 | keepForO2 110 | | numZeroes > numOnes = Zero 111 | | otherwise = One 112 | keepFunc fstOrSnd = \case 113 | Zero -> Zero : foldMap (fstOrSnd . snd) zeroes 114 | One -> One : foldMap (fstOrSnd . snd) ones 115 | newO2 = keepFunc fst keepForO2 116 | newCO2 = keepFunc snd (flipBit keepForO2) 117 | in (numZeroes + numOnes, (newO2, newCO2)) 118 | 119 | peelOff 120 | :: [[Bit]] 121 | -> V2 [[Bit]] -- ^ x is zeros, y is ones 122 | peelOff = fmap DL.toList . foldMap go 123 | where 124 | go = \case 125 | [] -> mempty 126 | Zero:xs -> V2 (DL.singleton xs) mempty 127 | One :ys -> V2 mempty (DL.singleton ys) 128 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day04.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day04 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 4. See "AOC.Solver" for the types used in this module! 9 | -- 10 | -- After completing the challenge, it is recommended to: 11 | 12 | module AOC.Challenge.Day04 ( 13 | day04a 14 | , day04b 15 | , wins 16 | ) where 17 | 18 | import AOC.Common (loopEither) 19 | import AOC.Solver ((:~>)(..)) 20 | import Data.Bifunctor (first) 21 | import Data.Finite (Finite, finites, combineProduct) 22 | import Data.Foldable (toList) 23 | import Data.Functor.Foldable (ana, hylo) 24 | import Data.Functor.Foldable.TH (makeBaseFunctor) 25 | import Data.IntMap (IntMap) 26 | import Data.IntSet (IntSet) 27 | import Data.List.NonEmpty (NonEmpty) 28 | import Data.List.Split (splitOn) 29 | import Data.Maybe (catMaybes, mapMaybe) 30 | import Data.Ord (comparing) 31 | import Data.Traversable (for) 32 | import Data.Tuple (swap) 33 | import Safe.Foldable (maximumByMay) 34 | import Text.Read (readMaybe) 35 | import qualified Data.IntMap as IM 36 | import qualified Data.IntSet as IS 37 | import qualified Data.List.NonEmpty as NE 38 | 39 | type Board = IntMap Int 40 | 41 | data Game = Step Game 42 | | Win Int -- ^ checksum: sum of unseen numbers * last called 43 | | Loss 44 | 45 | makeBaseFunctor ''Game 46 | 47 | day04a :: ([Int], [Board]) :~> Int 48 | day04a = MkSol 49 | { sParse = parseCards . splitOn "\n\n" 50 | , sShow = show 51 | , sSolve = \(picks, bs) -> 52 | let initGames = map (\b -> ana (gameCoalg b) (GameState picks IS.empty)) bs 53 | in loopEither stepAndWin initGames 54 | } 55 | 56 | day04b :: ([Int], [Board]) :~> Int 57 | day04b = MkSol 58 | { sParse = parseCards . splitOn "\n\n" 59 | , sShow = show 60 | , sSolve = \(picks, bs) -> 61 | fmap snd 62 | . maximumByMay (comparing fst) 63 | . mapMaybe (\b -> hylo gameAlg (gameCoalg b) (GameState picks IS.empty)) 64 | $ bs 65 | } 66 | 67 | -- | All winning lines 68 | wins :: [IntSet] 69 | wins = IS.fromList . map (fromIntegral . combineProduct) <$> (verts ++ map (map swap) verts) 70 | where 71 | verts :: [[(Finite 5, Finite 5)]] 72 | verts = 73 | [ [(i, j) | j <- finites] 74 | | i <- finites 75 | ] 76 | 77 | parseCards :: [String] -> Maybe ([Int], [Board]) 78 | parseCards str = do 79 | pics:rest <- Just str 80 | picNums <- traverse readMaybe $ splitOn "," pics 81 | boards <- for rest $ 82 | fmap (IM.fromList . flip zip [0..]) 83 | . traverse readMaybe . words . unwords . lines 84 | pure (picNums, boards) 85 | 86 | data GameState = GameState 87 | { gsQueue :: [Int] 88 | , gsMarked :: IntSet 89 | } 90 | 91 | gameCoalg 92 | :: Board 93 | -> GameState 94 | -> GameF GameState 95 | gameCoalg board GameState{..} = 96 | case gsQueue of 97 | [] -> LossF 98 | p:ps -> 99 | let foundIx = p `IM.lookup` board 100 | newMarked = maybe id IS.insert foundIx gsMarked 101 | boardWon = any (`IS.isSubsetOf` newMarked) wins 102 | unMarked = sum . IM.keys $ 103 | IM.filter (`IS.notMember` newMarked) board 104 | in if boardWon 105 | then WinF $ unMarked * p 106 | else StepF $ GameState ps newMarked 107 | 108 | -- | returns number of turns taken, and the checksum 109 | gameAlg 110 | :: GameF (Maybe (Int, Int)) 111 | -> Maybe (Int, Int) 112 | gameAlg = \case 113 | StepF x -> first (+1) <$> x 114 | WinF i -> Just (1, i) 115 | LossF -> Nothing 116 | 117 | -- | Step all games and quit on the first won 118 | stepAndWin :: [Game] -> Either (Maybe Int) [Game] 119 | stepAndWin = traverse $ \case 120 | Step g' -> Right g' 121 | Win i -> Left (Just i) 122 | Loss -> Left Nothing -- assume that one loss means all remaining have lost 123 | 124 | -- | Filter all won & lost games. If none are remaining, show the wins 125 | -- emitted that final round. 126 | _stepAndFilter :: NonEmpty Game -> Either [Int] (NonEmpty Game) 127 | _stepAndFilter gs = case NE.nonEmpty (catMaybes remaining) of 128 | Nothing -> Left emitted 129 | Just gs' -> Right gs' 130 | where 131 | (emitted, remaining) = for (toList gs) $ \case 132 | Step g' -> ([], Just g') 133 | Win i -> ([i], Nothing) 134 | Loss -> ([], Nothing) 135 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day05.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day05 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 5. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day05 ( 11 | day05a 12 | , day05b 13 | ) where 14 | 15 | import AOC.Common (traverseLines, listV2) 16 | import AOC.Common.Point (Point) 17 | import AOC.Solver ((:~>)(..)) 18 | import Control.DeepSeq (NFData) 19 | import Control.Monad ((<=<)) 20 | import Data.Foldable (foldMap') 21 | import Data.List (tails) 22 | import Data.List.Split (splitOn) 23 | import Data.Maybe (mapMaybe, fromJust) 24 | import GHC.Generics (Generic) 25 | import Linear.Matrix (transpose) 26 | import Linear.V2 (V2(..)) 27 | import Text.Read (readMaybe) 28 | import qualified Data.ExtendedReal as ER 29 | import qualified Data.IntegerInterval as I 30 | import qualified Data.Set as S 31 | 32 | data LineType = Horiz | Vert | Upwards | Downwards 33 | deriving stock (Show, Generic) 34 | deriving anyclass NFData 35 | data Line = Line 36 | { lType :: LineType 37 | , lAxis :: Integer -- ^ y for horiz, x for vert, x+y for up, x-y for down 38 | , lRange :: I.IntegerInterval 39 | } 40 | deriving stock (Show, Generic) 41 | deriving anyclass NFData 42 | 43 | classify :: V2 Point -> Maybe Line 44 | classify ((fmap . fmap) fromIntegral->V2 (V2 x1 y1) (V2 x2 y2)) 45 | | x1 == x2 = Just $ Line Vert x1 (fromTo y1 y2) 46 | | y1 == y2 = Just $ Line Horiz y1 (fromTo x1 x2) 47 | | (x1+y1) == (x2+y2) = Just $ Line Upwards (x1+y1) (fromTo y1 y2) 48 | | (x1-y1) == (x2-y2) = Just $ Line Downwards (x1-y1) (fromTo y1 y2) 49 | | otherwise = Nothing 50 | where 51 | fromTo a b 52 | | a <= b = ER.Finite a I.<=..<= ER.Finite b 53 | | otherwise = ER.Finite b I.<=..<= ER.Finite a 54 | 55 | _unclassify :: Line -> V2 Point 56 | _unclassify (Line t a r) = fmap (fmap fromInteger) case t of 57 | Horiz -> V2 (V2 lb a) (V2 ub a) 58 | Vert -> V2 (V2 a lb) (V2 a ub) 59 | Upwards -> V2 (V2 (a-lb) lb) (V2 (a-ub) ub) 60 | Downwards -> V2 (V2 (a+lb) lb) (V2 (a+ub) ub) 61 | where 62 | (lb, ub) = fromJust $ intervalBounds r 63 | 64 | parseLine :: String -> Maybe (V2 Point) 65 | parseLine = (traverse . traverse) readMaybe 66 | <=< listV2 . mapMaybe (listV2 . splitOn ",") . words 67 | 68 | day05 :: ([V2 Point] -> [V2 Point]) -> [V2 Point] :~> Int 69 | day05 preFilter = MkSol 70 | { sParse = traverseLines parseLine 71 | , sShow = show 72 | , sSolve = \xs -> do 73 | ls <- traverse classify (preFilter xs) 74 | let bigONSquaredSearch = foldMap' S.fromList do 75 | l:ls' <- tails ls 76 | overlaps l <$> ls' 77 | pure $ S.size bigONSquaredSearch 78 | } 79 | 80 | day05a :: [V2 Point] :~> Int 81 | day05a = day05 (filter sameLine) 82 | where 83 | sameLine = any (\(V2 a b) -> a == b) . transpose 84 | 85 | day05b :: [V2 Point] :~> Int 86 | day05b = day05 id 87 | 88 | -- upwards points: z = x+y is constant, so range t=(a,b) 89 | -- = to 90 | -- ie, z = 10, from (3,6) 91 | -- goes from <7,3> to <4,6> 92 | -- to find t coord, 93 | -- so = gives us the horiz and vert intercepts 94 | -- as in x=z-t, t=z-x, also y = t 95 | -- 96 | -- downwards points: z = x-y is constant, so range t=(a,b) 97 | -- = to 98 | -- to find t coord, 99 | -- so = gives us horiz and vert intercepts 100 | -- as in x=z+t, t=x-z, also y = t 101 | -- 102 | -- upwards-downwards intersection 103 | -- solve = 104 | -- so first y = t1 = t2, and t's are in range 105 | -- so we just need to solve z1-y == z2+y 106 | -- or 2y = z1-z2, y = (z1-z2) `div` 2 107 | -- ie, let's say upwards z1 = 4, downwards z2=0 108 | -- solve y = (4-0)`div`2 = 2 109 | -- and so check if 2 is in range 110 | 111 | -- sloped: 112 | -- intersect with other slope: 113 | -- == (t = x = equal) 114 | -- solve: z1 + m1 * t == z2 + m2 * t 115 | -- m1 * t = m2 * t + (z2 - z1) 116 | -- t = (m2 * t + z2 - z1) / m1 117 | -- 118 | 119 | overlaps :: Line -> Line -> [V2 Integer] 120 | overlaps = \case 121 | Line Horiz y1 rx1 -> \case 122 | Line Horiz y2 rx2 123 | | y1 == y2 124 | , Just (a, b) <- intervalBounds (I.intersection rx1 rx2) -> (`V2` y1) <$> [a..b] 125 | Line Vert x2 ry2 126 | | y1 `I.member` ry2 && x2 `I.member` rx1 -> [V2 x2 y1] 127 | Line Upwards z2 rt2 128 | | y1 `I.member` rt2 && (z2 - y1) `I.member` rx1 -> [V2 (z2-y1) y1] 129 | Line Downwards z2 rt2 130 | | y1 `I.member` rt2 && (z2 + y1) `I.member` rx1 -> [V2 (z2+y1) y1] 131 | _ -> [] 132 | Line Vert x1 ry1 -> \case 133 | Line Horiz y2 rx2 134 | | x1 `I.member` rx2 && y2 `I.member` ry1 -> [V2 x1 y2] 135 | Line Vert x2 ry2 136 | | x1 == x2 137 | , Just (a, b) <- intervalBounds (I.intersection ry1 ry2) -> V2 x1 <$> [a..b] 138 | Line Upwards z2 rt2 139 | | (z2 - x1) `I.member` rt2 && (z2 - x1) `I.member` ry1 -> [V2 x1 (z2-x1)] 140 | Line Downwards z2 rt2 141 | | (x1 - z2) `I.member` rt2 && (x1 - z2) `I.member` ry1 -> [V2 x1 (x1-z2)] 142 | _ -> [] 143 | Line Upwards z1 rt1 -> \case 144 | Line Horiz y2 rx2 145 | | y2 `I.member` rt1 && (z1 - y2) `I.member` rx2 -> [V2 (z1-y2) y2] 146 | Line Vert x2 ry2 147 | | (z1 - x2) `I.member` rt1 && (z1 - x2) `I.member` ry2 -> [V2 x2 (z1-x2)] 148 | Line Upwards z2 rt2 149 | | z1 == z2 150 | , Just (a, b) <- intervalBounds (I.intersection rt1 rt2) -> (\t -> V2 (z1-t) t) <$> [a..b] 151 | Line Downwards z2 rt2 152 | | let (t, rm) = (z1 - z2) `divMod` 2 153 | , rm == 0, t `I.member` rt1, t `I.member` rt2 -> [V2 (z1-t) t] 154 | _ -> [] 155 | Line Downwards z1 rt1 -> \case 156 | Line Horiz y2 rx2 157 | | y2 `I.member` rt1 && (z1 + y2) `I.member` rx2 -> [V2 (z1+y2) y2] 158 | Line Vert x2 ry2 159 | | (x2 - z1) `I.member` rt1 && (x2 - z1) `I.member` ry2 -> [V2 x2 (x2-z1)] 160 | Line Upwards z2 rt2 161 | | let (t, rm) = (z2 - z1) `divMod` 2 162 | , rm == 0, t `I.member` rt1, t `I.member` rt2 -> [V2 (z1+t) t] 163 | Line Downwards z2 rt2 164 | | z1 == z2 165 | , Just (a, b) <- intervalBounds (I.intersection rt1 rt2) -> (\t -> V2 (z1+t) t) <$> [a..b] 166 | _ -> [] 167 | 168 | intervalBounds :: I.IntegerInterval -> Maybe (Integer, Integer) 169 | intervalBounds i = do 170 | ER.Finite a <- pure $ I.lowerBound i 171 | ER.Finite b <- pure $ I.upperBound i 172 | pure (a, b) 173 | 174 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day06.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day06 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 6. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day06 ( 11 | day06a 12 | , day06b 13 | ) where 14 | 15 | import AOC.Common () 16 | import AOC.Solver ((:~>)(..)) 17 | import Control.Monad ((<=<)) 18 | import Control.Monad.ST (runST) 19 | import Data.Finite (Finite, packFinite) 20 | import Data.Foldable (traverse_) 21 | import Data.List.Split (splitOn) 22 | import Data.Semigroup (stimes) 23 | import GHC.TypeNats (KnownNat) 24 | import Text.Read (readMaybe) 25 | import qualified Data.Vector.Mutable.Sized as SMV 26 | import qualified Data.Vector.Sized as SV 27 | import qualified Linear as L 28 | 29 | newtype SquareMat n = SquareMat (SV.Vector n (SV.Vector n Int)) 30 | deriving stock (Show) 31 | 32 | applyMat :: KnownNat n => SquareMat n -> SV.Vector n Int -> SV.Vector n Int 33 | applyMat (SquareMat xs) x = xs L.!* x 34 | 35 | step :: SquareMat 9 36 | step = SquareMat $ SV.fromTuple ( 37 | SV.fromTuple (0,1,0,0,0,0,0,0,0) 38 | , SV.fromTuple (0,0,1,0,0,0,0,0,0) 39 | , SV.fromTuple (0,0,0,1,0,0,0,0,0) 40 | , SV.fromTuple (0,0,0,0,1,0,0,0,0) 41 | , SV.fromTuple (0,0,0,0,0,1,0,0,0) 42 | , SV.fromTuple (0,0,0,0,0,0,1,0,0) 43 | , SV.fromTuple (1,0,0,0,0,0,0,1,0) 44 | , SV.fromTuple (0,0,0,0,0,0,0,0,1) 45 | , SV.fromTuple (1,0,0,0,0,0,0,0,0) 46 | ) 47 | 48 | instance KnownNat n => Semigroup (SquareMat n) where 49 | SquareMat x <> SquareMat y = SquareMat (x L.!*! y) 50 | 51 | stepN :: Int -> SquareMat 9 52 | stepN n = stimes n step 53 | 54 | allocate :: forall n. KnownNat n => [Finite n] -> SV.Vector n Int 55 | allocate xs = runST do 56 | v <- SMV.replicate 0 57 | traverse_ (SMV.modify v (+1)) xs 58 | SV.freeze v 59 | 60 | day06 :: Int -> [Finite 9] :~> Int 61 | day06 n = MkSol 62 | { sParse = traverse (packFinite <=< readMaybe) . splitOn "," 63 | , sShow = show 64 | -- this isn't going to bench very accurately because stepN n is auto-cached 65 | , sSolve = Just . sum . (stepN n `applyMat`) . allocate 66 | } 67 | 68 | day06a :: [Finite 9] :~> Int 69 | day06a = day06 80 70 | 71 | day06b :: [Finite 9] :~> Int 72 | day06b = day06 256 73 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day07.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day07 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 7. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day07 ( 11 | day07a 12 | , day07b 13 | ) where 14 | 15 | import AOC.Common (freqs, triangleNumber) 16 | import AOC.Common.Search (binaryFindMin) 17 | import AOC.Solver ((:~>)(..)) 18 | import Control.Monad (guard) 19 | import Data.List.Split (splitOn) 20 | import Data.Semigroup (Sum(..)) 21 | import Text.Read (readMaybe) 22 | import qualified Data.Map as M 23 | import qualified Data.Vector as V 24 | 25 | day07 26 | :: (Int -> Int) -- ^ loss function 27 | -> [Int] :~> Int 28 | day07 f = MkSol 29 | { sParse = traverse readMaybe . splitOn "," 30 | , sShow = show 31 | , sSolve = \xs -> do 32 | let xsMap = freqs xs 33 | findFuelFor targ = getSum $ M.foldMapWithKey (\x n -> Sum $ f (abs (targ - x)) * n) xsMap 34 | (minX, _) <- M.lookupMin xsMap 35 | (maxX, _) <- M.lookupMax xsMap 36 | let fuelVector = V.generate (maxX + 1 - minX) $ \i -> findFuelFor (i + minX) 37 | binaryFindMin (\x -> 38 | let fX = fuelVector V.! x 39 | fX1 = fuelVector V.! (x+1) 40 | in fX <$ guard (fX1 > fX) 41 | ) 42 | minX maxX 43 | } 44 | 45 | day07a :: [Int] :~> Int 46 | day07a = day07 id 47 | 48 | day07b :: [Int] :~> Int 49 | day07b = day07 triangleNumber 50 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day08.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-redundant-constraints #-} 2 | 3 | -- | 4 | -- Module : AOC.Challenge.Day08 5 | -- License : BSD3 6 | -- 7 | -- Stability : experimental 8 | -- Portability : non-portable 9 | -- 10 | -- Day 8. See "AOC.Solver" for the types used in this module! 11 | 12 | module AOC.Challenge.Day08 ( 13 | day08a 14 | , day08b 15 | ) where 16 | 17 | import AOC.Common (listTup, traverseLines, countTrue) 18 | import AOC.Common.FinitarySet (FinitarySet) 19 | import AOC.Solver ((:~>)(..)) 20 | import Control.Lens (Prism', prism', preview) 21 | import Data.Bifunctor (first) 22 | import Data.Char (chr, ord) 23 | import Data.Finitary (Finitary) 24 | import Data.Finite (Finite, finites, packFinite, getFinite) 25 | import Data.Foldable (toList) 26 | import Data.List (permutations) 27 | import Data.List.Split (splitOn) 28 | import Data.Map (Map) 29 | import Data.Maybe (mapMaybe) 30 | import qualified AOC.Common.FinitarySet as FS 31 | import qualified Data.Map as M 32 | import qualified Data.Set as S 33 | 34 | -- way too much type safety 35 | 36 | -- | Actual physical segment on the display 37 | newtype Segment = Segment { getSegment :: Finite 7 } 38 | deriving stock (Eq, Ord, Show) 39 | deriving newtype Finitary 40 | type Display = FinitarySet Segment 41 | 42 | -- | abcdefg 43 | newtype Wire = Wire { getWire :: Finite 7 } 44 | deriving stock (Eq, Ord, Show) 45 | deriving newtype Finitary 46 | type Wires = FinitarySet Wire 47 | 48 | -- | Map of wire displays to the digit they represent 49 | type OutputMap = Map Wires Int 50 | 51 | day08a :: [(FinitarySet Wires, [Wires])] :~> Int 52 | day08a = MkSol 53 | { sParse = traverseLines parseLine 54 | , sShow = show 55 | , sSolve = Just . sum . map (countTrue isUnique . snd) 56 | } 57 | where 58 | isUnique xs = FS.length xs `S.member` uniques 59 | where 60 | uniques = S.fromList [2,4,3,7] 61 | 62 | -- | Map of all 9-digit observations to OutputMap they represent 63 | observationsMap :: Map (FinitarySet Wires) OutputMap 64 | observationsMap = M.fromList do 65 | perm <- permutations $ Wire <$> finites 66 | let mp = M.fromList $ zip (Segment <$> finites) perm 67 | visible = (FS.map . FS.map) (mp M.!) signalSet 68 | outputMap = M.fromList do 69 | (a, sig) <- M.toList signals 70 | pure (FS.map (mp M.!) sig, a) 71 | pure (visible, outputMap) 72 | where 73 | signalSet = FS.fromList (toList signals) 74 | 75 | signals :: Map Int Display 76 | signals = M.fromList . zip [0..] . map (FS.fromList . map Segment) $ 77 | [ [0,1,2,4,5,6] 78 | , [2,5] 79 | , [0,2,3,4,6] 80 | , [0,2,3,5,6] 81 | , [1,2,3,5] 82 | , [0,1,3,5,6] 83 | , [0,1,3,4,5,6] 84 | , [0,2,5] 85 | , [0,1,2,3,4,5,6] 86 | , [0,1,2,3,5,6] 87 | ] 88 | 89 | day08b :: [(FinitarySet Wires, [Wires])] :~> Int 90 | day08b = MkSol 91 | { sParse = traverseLines parseLine 92 | , sShow = show 93 | , sSolve = fmap (fmap sum) . traverse $ \(xs, ys) -> do 94 | outputMap <- M.lookup xs observationsMap 95 | [a,b,c,d] <- traverse (`M.lookup` outputMap) ys 96 | pure (a*1000+b*100+c*10+d) 97 | } 98 | 99 | 100 | 101 | 102 | 103 | parseLine :: String -> Maybe (FinitarySet Wires, [Wires]) 104 | parseLine = fmap (first FS.fromList) 105 | . listTup 106 | . map (map toWires . words) 107 | . splitOn " | " 108 | where 109 | toWires :: String -> Wires 110 | toWires = FS.fromList . mapMaybe (preview _CharWire) 111 | -- | Parse a Char as a Wire, for type safety 112 | _CharWire :: Prism' Char Wire 113 | _CharWire = prism' 114 | (\(Wire w) -> chr $ fromIntegral (getFinite w) + ord 'a') 115 | (\c -> Wire <$> packFinite (fromIntegral (ord c - ord 'a'))) 116 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day09.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day09 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 9. See "AOC.Solver" for the types used in this module! 9 | -- 10 | -- After completing the challenge, it is recommended to: 11 | 12 | module AOC.Challenge.Day09 ( 13 | day09a 14 | , day09b 15 | ) where 16 | 17 | import AOC.Common (freqs, digitToIntSafe) 18 | import AOC.Common.Point (Point, cardinalNeighbs, parseAsciiMap) 19 | import AOC.Solver ((:~>)(..)) 20 | import Control.Monad (mfilter) 21 | import Data.Foldable (toList, find) 22 | import Data.List (sortBy) 23 | import Data.Map (Map) 24 | import qualified Data.Map as M 25 | 26 | -- | Find low points and their heights 27 | findLows :: Map Point Int -> [(Point, Int)] 28 | findLows mp = filter go . M.toList $ mp 29 | where 30 | go (p, i) = all isLow (cardinalNeighbs p) 31 | where 32 | isLow = maybe True (> i) . (`M.lookup` mp) 33 | 34 | day09a :: Map Point Int :~> Int 35 | day09a = MkSol 36 | { sParse = Just . parseAsciiMap (mfilter (< 9) . digitToIntSafe) 37 | , sShow = show 38 | , sSolve = Just . sum . map ((+ 1) . snd) . findLows 39 | } 40 | 41 | day09b :: Map Point Int :~> Int 42 | day09b = MkSol 43 | { sParse = Just . parseAsciiMap (mfilter (< 9) . digitToIntSafe) 44 | , sShow = show 45 | , sSolve = \xs -> Just 46 | let -- map of points to their associated low points after flowing 47 | -- all the way downhill 48 | res = flip M.mapWithKey (flowMap xs) \p -> \case 49 | Nothing -> p 50 | Just q -> res M.! q 51 | in product . take 3 . sortBy (flip compare) . toList $ freqs res 52 | } 53 | 54 | -- | Map of each point to the next point downhill. If Nothing, then it's a low point. 55 | flowMap :: Map Point Int -> Map Point (Maybe Point) 56 | flowMap mp = M.mapWithKey go mp 57 | where 58 | go p i = find getGrad (cardinalNeighbs p) 59 | where 60 | getGrad = maybe False (< i) . (`M.lookup` mp) 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day10.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day10 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 10. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day10 ( 11 | day10a 12 | , day10b 13 | ) where 14 | 15 | import AOC.Solver ((:~>)(..)) 16 | import Control.DeepSeq (NFData) 17 | import GHC.Generics (Generic) 18 | import Data.Maybe (mapMaybe) 19 | import AOC.Common (traverseLines) 20 | import Data.List (sort) 21 | 22 | data Bracket = Round | Square | Curly | Angle 23 | deriving stock (Eq, Show, Ord, Generic) 24 | deriving anyclass (NFData) 25 | data Direction = Open | Close 26 | deriving stock (Eq, Show, Ord, Generic) 27 | deriving anyclass (NFData) 28 | 29 | data Symbol = Symbol { direction :: Direction, bracket :: Bracket } 30 | deriving stock (Eq, Show, Ord, Generic) 31 | deriving anyclass (NFData) 32 | 33 | -- | Left: error (with offending bracket) 34 | -- Right: no error, but a list of leftover incompletes 35 | runSymbols :: [Symbol] -> Either Bracket [Bracket] 36 | runSymbols = go [] 37 | where 38 | go stk = \case 39 | Symbol Close b:xs -> case stk of 40 | s:ss | s == b -> go ss xs 41 | _ -> Left b 42 | Symbol Open b:xs -> go (b:stk) xs 43 | [] -> Right stk 44 | 45 | parseString :: String -> Maybe [Symbol] 46 | parseString = traverse lookupSymbol 47 | where 48 | lookupSymbol = \case 49 | '(' -> Just $ Symbol Open Round 50 | '[' -> Just $ Symbol Open Square 51 | '{' -> Just $ Symbol Open Curly 52 | '<' -> Just $ Symbol Open Angle 53 | ')' -> Just $ Symbol Close Round 54 | ']' -> Just $ Symbol Close Square 55 | '}' -> Just $ Symbol Close Curly 56 | '>' -> Just $ Symbol Close Angle 57 | _ -> Nothing 58 | 59 | day10a :: [[Symbol]] :~> Int 60 | day10a = MkSol 61 | { sParse = traverseLines parseString 62 | , sShow = show 63 | , sSolve = Just . sum . map (either bracketScore (const 0) . runSymbols) 64 | } 65 | where 66 | bracketScore :: Bracket -> Int 67 | bracketScore = \case 68 | Round -> 3 69 | Square -> 57 70 | Curly -> 1197 71 | Angle -> 25137 72 | 73 | day10b :: [[Symbol]] :~> Int 74 | day10b = MkSol 75 | { sParse = sParse day10a 76 | , sShow = show 77 | , sSolve = takeMid . sort . mapMaybe (either (const Nothing) (Just . getScore) . runSymbols) 78 | } 79 | where 80 | getScore :: [Bracket] -> Int 81 | getScore = go 0 82 | where 83 | go !n (b:xs) = go (n * 5 + bracketScore b) xs 84 | go !n [] = n 85 | bracketScore = \case 86 | Round -> 1 87 | Square -> 2 88 | Curly -> 3 89 | Angle -> 4 90 | 91 | -- | Return the middle item in a list. Step through the list at two 92 | -- different speeds and return when the double-speed one hits the end. 93 | takeMid :: [a] -> Maybe a 94 | takeMid qs = go qs qs 95 | where 96 | go (_:xs) (_:_:ys) = go xs ys 97 | go (x:_) _ = Just x 98 | go [] _ = Nothing 99 | 100 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day11.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day11 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 11. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day11 ( 11 | day11a 12 | , day11b 13 | ) where 14 | 15 | import AOC.Common (firstJust, freqs, (!!!), digitToIntSafe) 16 | import AOC.Common.Point (Point, fullNeighbs, parseAsciiMap) 17 | import AOC.Solver ((:~>)(..)) 18 | import Control.Monad (guard) 19 | import Data.List (unfoldr, scanl') 20 | import Data.Map (Map) 21 | import Data.Set (Set) 22 | import qualified Data.Map as M 23 | import qualified Data.Set as S 24 | 25 | type SquidGame = Map Point Int 26 | type FreqMap = Map Point Int 27 | 28 | day11a :: SquidGame :~> Int 29 | day11a = MkSol 30 | { sParse = Just . parseAsciiMap digitToIntSafe 31 | , sShow = show 32 | , sSolve = Just . (!!! 100) . scanl' (+) 0 . map sum . doTheThing 33 | } 34 | 35 | doTheThing :: SquidGame -> [FreqMap] 36 | doTheThing = unfoldr (Just . fullStep) 37 | 38 | fullStep :: SquidGame -> (FreqMap, SquidGame) 39 | fullStep mp = (fl, mp'') 40 | where 41 | (fl, mp') = runAllFlashes $ fmap (+1) mp 42 | mp'' = (0 <$ fl) `M.union` mp' 43 | 44 | runAllFlashes :: SquidGame -> (FreqMap, SquidGame) 45 | runAllFlashes = go M.empty 46 | where 47 | go n mp 48 | | S.null fl = (n, mp') 49 | | otherwise = go (M.unionWith (+) (M.fromSet (const 1) fl) n) mp' 50 | where 51 | (fl, mp') = runFlash mp 52 | 53 | runFlash :: SquidGame -> (Set Point, SquidGame) 54 | runFlash mp = (readyToFlash, M.unionWith (+) neighbs noFlash) 55 | where 56 | (yesFlash, noFlash) = M.partition (> 9) mp 57 | readyToFlash = M.keysSet yesFlash 58 | neighbs = (`M.restrictKeys` M.keysSet noFlash) . freqs $ foldMap fullNeighbs readyToFlash 59 | 60 | day11b :: SquidGame :~> Int 61 | day11b = MkSol 62 | { sParse = sParse day11a 63 | , sShow = show 64 | , sSolve = \mp -> 65 | firstJust (\(i, q) -> i <$ guard (M.keysSet q == M.keysSet mp)) 66 | . zip [1..] 67 | . doTheThing 68 | $ mp 69 | } 70 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day12.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day12 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 12. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day12 ( 25 | day12a 26 | , day12b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import Data.Bitraversable 32 | import Data.Finitary 33 | import qualified AOC.Common.FinitarySet as FS 34 | import qualified Data.Graph.Inductive as G 35 | import qualified Data.IntMap as IM 36 | import qualified Data.IntSet as IS 37 | import qualified Data.List.NonEmpty as NE 38 | import qualified Data.List.PointedList as PL 39 | import qualified Data.List.PointedList.Circular as PLC 40 | import qualified Data.Map as M 41 | import qualified Data.OrdPSQ as PSQ 42 | import qualified Data.Sequence as Seq 43 | import qualified Data.Set as S 44 | import qualified Data.Text as T 45 | import qualified Data.Vector as V 46 | import qualified Linear as L 47 | import qualified Text.Megaparsec as P 48 | import qualified Text.Megaparsec.Char as P 49 | import qualified Text.Megaparsec.Char.Lexer as PP 50 | 51 | data SrcNode = StartNode 52 | | SrcCave Cave 53 | deriving stock (Generic, Eq, Ord, Show) 54 | deriving anyclass (Finitary) 55 | 56 | data DestNode = DestCave Cave 57 | | EndNode 58 | deriving stock (Generic, Eq, Ord, Show) 59 | deriving anyclass (Finitary) 60 | 61 | data FullNode = FullStart 62 | | FullCave Cave 63 | | FullEnd 64 | deriving stock (Generic, Eq, Ord, Show) 65 | deriving anyclass (Finitary) 66 | 67 | data Ident = Ident (Finite 26) (Maybe (Finite 26)) 68 | deriving stock (Generic, Eq, Ord, Show) 69 | deriving anyclass (Finitary) 70 | 71 | data Cave = Big Ident 72 | | Small Ident 73 | deriving stock (Generic, Eq, Ord, Show) 74 | deriving anyclass (Finitary) 75 | 76 | classify :: String -> Maybe FullNode 77 | classify "start" = Just FullStart 78 | classify "end" = Just FullEnd 79 | classify [a] = charFinite a <&> \case 80 | (False, x) -> FullCave $ Small (Ident x Nothing) 81 | (True , x) -> FullCave $ Big (Ident x Nothing) 82 | classify [a,b] = do 83 | (cx, x) <- charFinite a 84 | (cy, y) <- charFinite b 85 | let ident = Ident x (Just y) 86 | case (cx, cy) of 87 | (False, False) -> Just . FullCave $ Small ident 88 | (True , True ) -> Just . FullCave $ Big ident 89 | _ -> Nothing 90 | classify _ = Nothing 91 | 92 | srcDest :: FullNode -> (Maybe SrcNode, Maybe DestNode) 93 | srcDest = \case 94 | FullStart -> (Just StartNode, Nothing) 95 | FullCave c -> (Just (SrcCave c), Just (DestCave c)) 96 | FullEnd -> (Nothing, Just EndNode) 97 | 98 | toAdjMatrix :: [(FullNode, FullNode)] -> Map SrcNode [DestNode] 99 | toAdjMatrix = M.fromListWith (++) 100 | . concatMap (uncurry buildLinks) 101 | where 102 | toLink a b = (a, [b]) 103 | buildLinks a b = catMaybes 104 | [ toLink <$> srcA <*> destB 105 | , toLink <$> srcB <*> destA 106 | ] 107 | where 108 | (srcA, destA) = srcDest a 109 | (srcB, destB) = srcDest b 110 | 111 | day12a :: [(FullNode, FullNode)] :~> Int 112 | day12a = MkSol 113 | { sParse = traverseLines $ bitraverse classify classify <=< listTup . splitOn "-" 114 | , sShow = show 115 | , sSolve = Just . length . findPaths . toAdjMatrix 116 | } 117 | 118 | findPaths :: Map SrcNode [DestNode] -> [[Cave]] 119 | findPaths mp = do 120 | nextBranch <- mp M.! StartNode 121 | case nextBranch of 122 | EndNode -> pure [] 123 | DestCave v@(Big _) -> go FS.empty v 124 | DestCave v@(Small c) -> go (FS.singleton c) v 125 | where 126 | go :: FS.FinitarySet Ident -> Cave -> [[Cave]] 127 | go seen currPos = do 128 | nextBranch <- mp M.! SrcCave currPos 129 | case nextBranch of 130 | EndNode -> pure [currPos] 131 | DestCave v@(Big _) -> (currPos:) <$> go seen v 132 | DestCave v@(Small c) -> do 133 | guard $ c `FS.notMember` seen 134 | (currPos:) <$> go (c `FS.insert` seen) v 135 | 136 | day12b :: [(FullNode, FullNode)] :~> Int 137 | day12b = MkSol 138 | { sParse = sParse day12a 139 | , sShow = show 140 | , sSolve = Just . length . findPaths2 . toAdjMatrix 141 | } 142 | 143 | findPaths2 :: Map SrcNode [DestNode] -> [[Cave]] 144 | findPaths2 mp = do 145 | nextBranch <- mp M.! StartNode 146 | case nextBranch of 147 | EndNode -> pure [] 148 | DestCave v@(Big _) -> go FS.empty Nothing v 149 | DestCave v@(Small c) -> go (FS.singleton c) Nothing v 150 | where 151 | go :: FS.FinitarySet Ident -> Maybe Ident -> Cave -> [[Cave]] 152 | go seen seenTwice currPos = do 153 | nextBranch <- mp M.! SrcCave currPos 154 | case nextBranch of 155 | EndNode -> pure [currPos] 156 | DestCave v@(Big _) -> (currPos:) <$> go seen seenTwice v 157 | DestCave v@(Small c) -> do 158 | newSeenTwice <- if c `FS.member` seen 159 | then Just c <$ guard (isNothing seenTwice) 160 | else pure seenTwice 161 | (currPos:) <$> go (c `FS.insert` seen) newSeenTwice v 162 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day13.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day13 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 13. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day13 ( 25 | day13a 26 | , day13b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import Data.Bitraversable 32 | import qualified Data.Graph.Inductive as G 33 | import qualified Data.IntMap as IM 34 | import qualified Data.IntSet as IS 35 | import qualified Data.List.NonEmpty as NE 36 | import qualified Data.List.PointedList as PL 37 | import qualified Data.List.PointedList.Circular as PLC 38 | import qualified Data.Map as M 39 | import qualified Data.OrdPSQ as PSQ 40 | import qualified Data.Sequence as Seq 41 | import qualified Data.Set as S 42 | import qualified Data.Set.NonEmpty as NES 43 | import qualified Data.Text as T 44 | import qualified Data.Vector as V 45 | import qualified Linear as L 46 | import qualified Text.Megaparsec as P 47 | import qualified Text.Megaparsec.Char as P 48 | import qualified Text.Megaparsec.Char.Lexer as PP 49 | 50 | day13a :: ([Point], [Point]) :~> _ 51 | day13a = MkSol 52 | { sParse = bitraverse 53 | (traverseLines $ traverse readMaybe <=< listV2 . splitOn ",") 54 | (traverseLines $ uncurry parseFold <=< listTup . splitOn "=") 55 | <=< listTup . splitOn "\n\n" 56 | , sShow = show 57 | , sSolve = \(ptList, folds) -> Just 58 | let ptSet = S.fromList ptList 59 | in S.size $ foldr go ptSet [head folds] 60 | } 61 | where 62 | parseFold ax v = do 63 | xy <- listToMaybe (reverse ax) 64 | vv <- readMaybe v 65 | pure $ 66 | if xy == 'x' 67 | then V2 vv 0 68 | else V2 0 vv 69 | go axis = S.map $ \p -> abs (p - axis) 70 | 71 | day13b :: ([Point], [(Bool, Int)]) :~> _ 72 | day13b = MkSol 73 | { sParse = bitraverse 74 | (traverseLines $ traverse readMaybe <=< listV2 . splitOn ",") 75 | (traverseLines $ uncurry parseFold <=< listTup . splitOn "=") 76 | <=< listTup . splitOn "\n\n" 77 | -- , sShow = unlines . map (displayAsciiSet '.' '#' . NES.toSet) . toList . contiguousShapes 78 | -- , sShow = show 79 | , sShow = parseLetters 80 | , sSolve = \(ptList, folds) -> Just 81 | let ptSet = S.fromList ptList 82 | in foldl' (flip go) ptSet folds 83 | } 84 | where 85 | parseFold ax v = do 86 | xy <- listToMaybe (reverse ax) 87 | vv <- readMaybe v 88 | pure (xy == 'x', vv) 89 | go (isX, a) = S.map $ 90 | let axFunc | isX = over _x 91 | | otherwise = over _y 92 | in axFunc $ \i -> negate (abs (i - a)) + a 93 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day14.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | -- | 3 | -- Module : AOC.Challenge.Day14 4 | -- License : BSD3 5 | -- 6 | -- Stability : experimental 7 | -- Portability : non-portable 8 | -- 9 | -- Day 14. See "AOC.Solver" for the types used in this module! 10 | -- 11 | -- After completing the challenge, it is recommended to: 12 | -- 13 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 14 | -- imports) for readability. 15 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 16 | -- pragmas. 17 | -- * Replace the partial type signatures underscores in the solution 18 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 19 | -- solution. You can delete the type signatures completely and GHC 20 | -- will recommend what should go in place of the underscores. 21 | 22 | module AOC.Challenge.Day14 ( 23 | day14a 24 | , day14b 25 | ) where 26 | 27 | import AOC.Prelude 28 | 29 | import qualified Data.Graph.Inductive as G 30 | import qualified Data.IntMap as IM 31 | import qualified Data.IntSet as IS 32 | import qualified Data.List.NonEmpty as NE 33 | import qualified Data.List.PointedList as PL 34 | import qualified Data.List.PointedList.Circular as PLC 35 | import qualified Data.Map as M 36 | import qualified Data.OrdPSQ as PSQ 37 | import qualified Data.Sequence as Seq 38 | import qualified Data.Set as S 39 | import qualified Data.Text as T 40 | import qualified Data.Vector as V 41 | import qualified Linear as L 42 | import qualified Text.Megaparsec as P 43 | import qualified Text.Megaparsec.Char as P 44 | import Data.Bitraversable 45 | import qualified Data.Map.NonEmpty as NEM 46 | import Safe 47 | import qualified Text.Megaparsec.Char.Lexer as PP 48 | 49 | day14 :: Int -> (String, [((Char, Char), Char)]) :~> Int 50 | day14 n = MkSol 51 | { sParse = traverse (traverseLines (bitraverse listTup listToMaybe <=< listTup . splitOn " -> ")) 52 | <=< listTup . splitOn "\n\n" 53 | , sShow = show 54 | , sSolve = \(str, rs) -> do 55 | firstChar <- headMay str 56 | lastChar <- lastMay str 57 | let rMap = M.fromList rs 58 | strPairs = freqs $ slidingPairs str 59 | res = iterate (expand rMap) strPairs !!! n 60 | compensate = M.fromList [(firstChar, 1), (lastChar, 1)] 61 | resFreqs = M.unionWith (+) compensate . fmap (`div` 2) $ M.fromListWith (+) 62 | [ (k, v) 63 | | ((x, y), r) <- M.toList res 64 | , (k, v) <- [(x,r),(y,r)] 65 | ] 66 | resFreqList = sort $ toList resFreqs 67 | lowFreq <- headMay resFreqList 68 | highFreq <- lastMay resFreqList 69 | pure $ highFreq - lowFreq 70 | } 71 | where 72 | expand rMap strPairs = M.fromListWith (+) 73 | [ (k, v) 74 | | ((a, b), r) <- M.toList strPairs 75 | , (k, v) <- case M.lookup (a, b) rMap of 76 | Nothing -> [ ((a, b), r) ] 77 | Just q -> [ ((a, q), r), ((q, b), r) ] 78 | ] 79 | 80 | day14a :: (String, [((Char, Char), Char)]) :~> Int 81 | day14a = day14 10 82 | 83 | day14b :: (String, [((Char, Char), Char)]) :~> Int 84 | day14b = day14 40 85 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day15.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day15 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 15. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day15 ( 11 | day15a 12 | , day15b 13 | ) where 14 | 15 | import AOC.Common (digitToIntSafe) 16 | import AOC.Common.Point (Point, V2(..), parseAsciiMap, mannDist, cardinalNeighbsSet) 17 | import AOC.Common.Search (aStar) 18 | import AOC.Solver ((:~>)(..)) 19 | import Control.Monad ((<=<)) 20 | import Data.Finite (Finite, packFinite) 21 | import Data.Map (Map) 22 | import qualified Data.Map as M 23 | import qualified Data.Set as S 24 | 25 | type Risk = Finite 9 26 | 27 | day15 :: (Map Point Risk -> Map Point Risk) -> Map Point Risk :~> Int 28 | day15 reMap = MkSol 29 | { sParse = Just . parseAsciiMap (packFinite . subtract 1 . fromIntegral <=< digitToIntSafe) 30 | , sShow = show 31 | , sSolve = \mp0 -> 32 | let mp = reMap mp0 33 | (targ, _) = M.findMax mp 34 | cost p = fromIntegral (mp M.! p) + 1 35 | in fst <$> aStar 36 | (mannDist targ) 37 | (M.fromSet cost . S.intersection (M.keysSet mp) . cardinalNeighbsSet) 38 | 0 39 | (== targ) 40 | } 41 | 42 | 43 | day15a :: Map Point Risk :~> Int 44 | day15a = day15 id 45 | 46 | day15b :: Map Point Risk :~> Int 47 | day15b = day15 \mp0 -> 48 | let (corner, _) = M.findMax mp0 49 | shifter = corner + 1 50 | in M.fromList 51 | [ (k', v + dx + dy) 52 | | (k, v) <- M.toList mp0 53 | , dx <- [0,1,2,3,4] 54 | , dy <- [0,1,2,3,4] 55 | , let k' = k + (shifter * (fromIntegral <$> V2 dx dy)) 56 | ] 57 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day16.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge.Day16 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Day 16. See "AOC.Solver" for the types used in this module! 9 | 10 | module AOC.Challenge.Day16 ( 11 | day16a 12 | , day16b 13 | ) where 14 | 15 | import AOC.Common (TokStream(..), digitToIntSafe, parseBinary, toBinaryFixed) 16 | import AOC.Solver ((:~>)(..)) 17 | import Control.Applicative (empty) 18 | import Control.DeepSeq (NFData) 19 | import Control.Lens (preview, _Right) 20 | import Control.Monad (replicateM) 21 | import Data.Bifunctor (bimap) 22 | import Data.Functor.Foldable (cata) 23 | import Data.Functor.Foldable.TH (makeBaseFunctor) 24 | import Data.Void (Void) 25 | import GHC.Generics (Generic) 26 | import qualified Text.Megaparsec as P 27 | 28 | type Version = Int 29 | data Packet = Operator Version Op [Packet] 30 | | Literal Version Int 31 | deriving stock (Show, Generic) 32 | deriving anyclass NFData 33 | 34 | data Op = OSum 35 | | OProd 36 | | OMin 37 | | OMax 38 | | OGT 39 | | OLT 40 | | OEQ 41 | deriving stock (Show, Generic) 42 | deriving anyclass NFData 43 | 44 | makeBaseFunctor ''Packet 45 | 46 | day16 :: Show a => (PacketF a -> a) -> TokStream Bool :~> a 47 | day16 alg = MkSol 48 | { sParse = Just . TokStream 49 | . concatMap (maybe [] id . fmap (toBinaryFixed 4) . digitToIntSafe) 50 | , sShow = show 51 | , sSolve = fmap (cata alg . snd) . preview _Right . P.runParser parsePacket "" 52 | } 53 | 54 | day16a :: TokStream Bool :~> Int 55 | day16a = day16 \case 56 | OperatorF v _ ps -> v + sum ps 57 | LiteralF v _ -> v 58 | 59 | day16b :: TokStream Bool :~> Int 60 | day16b = day16 evalPackF 61 | 62 | evalPackF :: PacketF Int -> Int 63 | evalPackF = \case 64 | OperatorF _ o is -> evalOp o is 65 | LiteralF _ i -> i 66 | where 67 | evalOp = \case 68 | OSum -> sum 69 | OProd -> product 70 | OMin -> minimum 71 | OMax -> maximum 72 | OGT -> \case [a,b] -> if a > b then 1 else 0 73 | _ -> -1 74 | OLT -> \case [a,b] -> if a < b then 1 else 0 75 | _ -> -1 76 | OEQ -> \case [a,b] -> if a == b then 1 else 0 77 | _ -> -1 78 | 79 | type Parser = P.Parsec Void (TokStream Bool) 80 | 81 | -- includes the length of items parsed 82 | parsePacket :: Parser (Int, Packet) 83 | parsePacket = do 84 | v <- parseBinary <$> replicateM 3 P.anySingle 85 | t <- parseType 86 | case t of 87 | Nothing -> bimap (+ 6) (Literal v) <$> parseLiteral 88 | Just o -> bimap (+ 6) (Operator v o) <$> parseOperator 89 | where 90 | parseType :: Parser (Maybe Op) 91 | parseType = do 92 | t <- parseBinary <$> replicateM 3 P.anySingle 93 | case t of 94 | 0 -> pure $ Just OSum 95 | 1 -> pure $ Just OProd 96 | 2 -> pure $ Just OMin 97 | 3 -> pure $ Just OMax 98 | 4 -> pure Nothing 99 | 5 -> pure $ Just OGT 100 | 6 -> pure $ Just OLT 101 | 7 -> pure $ Just OEQ 102 | _ -> empty 103 | parseLiteral :: Parser (Int, Int) 104 | parseLiteral = do 105 | n <- parseLitChunks 106 | pure (length n * 5, parseBinary (concat n)) 107 | parseLitChunks :: Parser [[Bool]] 108 | parseLitChunks = do 109 | goOn <- P.anySingle 110 | digs <- replicateM 4 P.anySingle 111 | if goOn 112 | then (digs:) <$> parseLitChunks 113 | else pure [digs] 114 | parseOperator :: Parser (Int, [Packet]) 115 | parseOperator = do 116 | lt <- P.anySingle 117 | if lt 118 | then do 119 | n <- parseBinary <$> replicateM 11 P.anySingle 120 | (len, ps) <- unzip <$> replicateM n parsePacket 121 | pure (sum len + 11 + 1, ps) 122 | else do 123 | n <- parseBinary <$> replicateM 15 P.anySingle 124 | (n+1+15,) <$> parsePacketsLength n 125 | parsePacketsLength :: Int -> Parser [Packet] 126 | parsePacketsLength n = do 127 | (ln, p) <- parsePacket 128 | if ln == n 129 | then pure [p] 130 | else (p:) <$> parsePacketsLength (n-ln) 131 | 132 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day17.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day17 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 17. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day17 ( 25 | day17a 26 | , day17b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import Safe 45 | import qualified Text.Megaparsec as P 46 | import Data.Complex 47 | import qualified Text.Megaparsec.Char as P 48 | import qualified Text.Megaparsec.Char.Lexer as PP 49 | 50 | parseBox :: String -> Maybe (V2 Point) 51 | parseBox = getBounds <=< traverse readMaybe . words . clearOut valid 52 | where 53 | valid k = not (isDigit k || k == '-') 54 | getBounds = \case 55 | [x1,x2,y1,y2] -> do 56 | [xMin, xMax] <- pure $ sort [x1,x2] 57 | [yMin, yMax] <- pure $ sort [y1,y2] 58 | pure $ V2 (V2 xMin yMin) (V2 xMax yMax) 59 | _ -> Nothing 60 | 61 | -- | Independent tight bounds for each axis 62 | velBounds :: V2 Point -> V2 Point 63 | velBounds (V2 (V2 x1 y1) (V2 x2 _)) = V2 (V2 vx1 y1) (V2 x2 (abs y1)) 64 | where 65 | vx1 = case quadraticEq (-fromIntegral x1) 0.5 0.5 of 66 | QReal a b -> ceiling (max a b) 67 | QSimul a -> ceiling a 68 | QComplex _ _ -> error "invalid box" 69 | 70 | data QuadraticSol = QReal Double Double 71 | | QSimul Double 72 | | QComplex (Complex Double) (Complex Double) 73 | 74 | quadraticEq :: Double -> Double -> Double -> QuadraticSol 75 | quadraticEq a b c = case compare discr 0 of 76 | LT -> QComplex (term1 :+ term2) (term1 :+ (-term2)) 77 | EQ -> QSimul term1 78 | GT -> QReal (term1 + term2) (term1 - term2) 79 | where 80 | discr = b*b - 4 * a * c 81 | term1 = -0.5*b/a 82 | term2 = 0.5 * sqrt (abs discr) / a 83 | 84 | day17a :: V2 Point :~> _ 85 | day17a = MkSol 86 | { sParse = parseBox 87 | , sShow = show 88 | , sSolve = \(bbox@(V2 (V2 _ y1) (V2 x2 _))) -> 89 | let V2 (V2 vx1 vy1) (V2 vx2 vy2) = velBounds bbox 90 | in maximumMay 91 | [ mx 92 | | x <- [vx1..vx2] 93 | , y <- [vy1..vy2] 94 | , let steps = stepUntilTooDeep x2 y1 (V2 x y) 95 | , any (inBoundingBox bbox) steps 96 | , Just mx <- [maximumMay (map (view _y) steps)] 97 | ] 98 | } 99 | 100 | stepUntilTooDeep :: Int -> Int -> Point -> [Point] 101 | stepUntilTooDeep xmax ymin v0 = map fst . takeWhile p $ iterate simStep (0, v0) 102 | where 103 | p (V2 x y, _) = y >= ymin && x <= xmax 104 | 105 | simStep :: (Point, Point) -> (Point, Point) 106 | simStep (pos, vel@(V2 vx vy)) = (pos + vel, vel') 107 | where 108 | vel' = V2 (vx - signum vx) (vy - 1) 109 | 110 | -- target area: x=248..285, y=-85..-56 111 | 112 | day17b :: V2 Point :~> _ 113 | day17b = MkSol 114 | { sParse = sParse day17a 115 | , sShow = show 116 | , sSolve = \(bbox@(V2 (V2 _ y1) (V2 x2 _))) -> 117 | let V2 (V2 vx1 vy1) (V2 vx2 vy2) = velBounds bbox 118 | in Just $ countTrue (any (inBoundingBox bbox)) 119 | [ stepUntilTooDeep x2 y1 (V2 x y) 120 | | x <- [vx1..vx2] 121 | , y <- [vy1..vy2] 122 | ] 123 | } 124 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day18.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day18 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 18. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day18 ( 25 | -- day18a 26 | -- , day18b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day18a :: _ :~> _ 49 | day18a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day18b :: _ :~> _ 56 | day18b = MkSol 57 | { sParse = sParse day18a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day19.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day19 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 19. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day19 ( 25 | -- day19a 26 | -- , day19b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day19a :: _ :~> _ 49 | day19a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day19b :: _ :~> _ 56 | day19b = MkSol 57 | { sParse = sParse day19a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day20.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day20 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 20. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day20 ( 25 | -- day20a 26 | -- , day20b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day20a :: _ :~> _ 49 | day20a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day20b :: _ :~> _ 56 | day20b = MkSol 57 | { sParse = sParse day20a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day21.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day21 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 21. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day21 ( 25 | -- day21a 26 | -- , day21b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day21a :: _ :~> _ 49 | day21a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day21b :: _ :~> _ 56 | day21b = MkSol 57 | { sParse = sParse day21a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day22.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day22 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 22. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day22 ( 25 | -- day22a 26 | -- , day22b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day22a :: _ :~> _ 49 | day22a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day22b :: _ :~> _ 56 | day22b = MkSol 57 | { sParse = sParse day22a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day23.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day23 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 23. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day23 ( 25 | -- day23a 26 | -- , day23b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day23a :: _ :~> _ 49 | day23a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day23b :: _ :~> _ 56 | day23b = MkSol 57 | { sParse = sParse day23a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day24.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day24 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 24. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day24 ( 25 | -- day24a 26 | -- , day24b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day24a :: _ :~> _ 49 | day24a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day24b :: _ :~> _ 56 | day24b = MkSol 57 | { sParse = sParse day24a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day25.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day25 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 25. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day25 ( 25 | -- day25a 26 | -- , day25b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day25a :: _ :~> _ 49 | day25a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day25b :: _ :~> _ 56 | day25b = MkSol 57 | { sParse = sParse day25a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /src/AOC/Prelude.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-compat-unqualified-imports #-} 2 | 3 | -- | 4 | -- Module : AOC.Prelude 5 | -- Copyright : (c) Justin Le 2021 6 | -- License : BSD3 7 | -- 8 | -- Maintainer : justin@jle.im 9 | -- Stability : experimental 10 | -- Portability : non-portable 11 | -- 12 | -- Custom Prelude while developing challenges. Ideally, once challenges 13 | -- are completed, an import to this module would be replaced with explicit 14 | -- ones for future readers. 15 | -- 16 | 17 | 18 | module AOC.Prelude ( 19 | module P 20 | ) where 21 | 22 | import AOC.Common as P 23 | import AOC.Common.Point as P 24 | import AOC.Common.Search as P 25 | import AOC.Solver as P 26 | import AOC.Util as P 27 | import Control.Applicative as P 28 | import Control.DeepSeq as P 29 | import Control.Lens as P hiding (uncons) 30 | import Control.Monad as P 31 | import Control.Monad.Except as P 32 | import Control.Monad.State as P 33 | import Data.Bifunctor as P 34 | import Data.Char as P 35 | import Data.Coerce as P 36 | import Data.Containers.ListUtils as P 37 | import Data.List as P 38 | import Data.Either as P 39 | import Data.Finite as P (Finite, packFinite, getFinite, modulo, finites) 40 | import Data.Foldable as P 41 | import Data.Function as P 42 | import Data.Functor as P 43 | import Data.IntMap as P (IntMap) 44 | import Data.IntMap.NonEmpty as P (NEIntMap) 45 | import Data.IntSet as P (IntSet) 46 | import Data.IntSet.NonEmpty as P (NEIntSet) 47 | import Data.Kind as P 48 | import Data.List.NonEmpty as P (NonEmpty(..), nonEmpty) 49 | import Data.List.Split as P 50 | import Data.Map as P (Map) 51 | import Data.Map.NonEmpty as P (NEMap) 52 | import Data.Maybe as P 53 | import Data.Ord as P 54 | import Data.Semigroup as P 55 | import Data.Set as P (Set) 56 | import Data.Set.NonEmpty as P (NESet) 57 | import Data.Text as P (Text) 58 | import Data.Text.Encoding as P (encodeUtf8, decodeUtf8) 59 | import Data.Time as P hiding (Day) 60 | import Data.Traversable as P 61 | import Data.Tuple as P 62 | import Data.Void as P 63 | import Debug.Trace as P 64 | import GHC.Generics as P (Generic) 65 | import Numeric.Natural as P 66 | import Text.Printf as P 67 | import Text.Read as P (readMaybe) 68 | -------------------------------------------------------------------------------- /src/AOC/Run/Config.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Run.Config 3 | -- Copyright : (c) Justin Le 2021 4 | -- License : BSD3 5 | -- 6 | -- Maintainer : justin@jle.im 7 | -- Stability : experimental 8 | -- Portability : non-portable 9 | -- 10 | -- Utilities for loading configuration file. 11 | -- 12 | 13 | 14 | module AOC.Run.Config ( 15 | Config(..), configFile, defConfPath 16 | , session 17 | ) where 18 | 19 | import Control.Exception 20 | import Control.Monad 21 | import Data.Default.Class 22 | import GHC.Generics (Generic) 23 | import System.IO.Error 24 | import Text.Printf 25 | import qualified Data.Aeson as A 26 | import qualified Data.ByteString as BS 27 | import qualified Data.Yaml as Y 28 | 29 | 30 | -- | Configuration for auto-runner. 31 | data Config = Cfg 32 | { _cfgSession :: Maybe String -- ^ Default: 'Nothing' 33 | , _cfgYear :: Integer -- ^ Default: 2015 34 | } 35 | deriving stock (Generic) 36 | 37 | -- | No session key, and 2015. 38 | instance Default Config where 39 | def = Cfg { _cfgSession = Nothing 40 | , _cfgYear = 2015 41 | } 42 | 43 | -- | Default math to find a configuration file. 44 | defConfPath :: FilePath 45 | defConfPath = "aoc-conf.yaml" 46 | 47 | -- | Load a 'Config' from a given filepath. 48 | configFile :: FilePath -> IO Config 49 | configFile fp = do 50 | cfgInp <- tryJust (guard . isDoesNotExistError) 51 | $ BS.readFile fp 52 | case cfgInp of 53 | Left () -> do 54 | Y.encodeFile @Config fp def 55 | return def 56 | Right b -> 57 | case Y.decodeEither' b of 58 | Left e -> do 59 | printf "Configuration file at %s could not be parsed:\n" fp 60 | print e 61 | return def 62 | Right cfg -> return cfg 63 | 64 | -- | Load a session token from the configuration file at a given filepath. 65 | session :: FilePath -> IO (Maybe String) 66 | session = fmap _cfgSession . configFile 67 | 68 | configJSON :: A.Options 69 | configJSON = A.defaultOptions 70 | { A.fieldLabelModifier = A.camelTo2 '-' . drop 4 } 71 | 72 | instance A.ToJSON Config where 73 | toJSON = A.genericToJSON configJSON 74 | toEncoding = A.genericToEncoding configJSON 75 | instance A.FromJSON Config where 76 | parseJSON = A.genericParseJSON configJSON 77 | -------------------------------------------------------------------------------- /src/AOC/Run/Interactive.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Run.Interactive 3 | -- Copyright : (c) Justin Le 2021 4 | -- License : BSD3 5 | -- 6 | -- Maintainer : justin@jle.im 7 | -- Stability : experimental 8 | -- Portability : non-portable 9 | -- 10 | -- Versions of loaders and runners meant to be used in GHCI. 11 | -- 12 | 13 | module AOC.Run.Interactive ( 14 | -- * Fetch and Run 15 | -- ** Return Answers 16 | execSolution 17 | , execSolutionWith 18 | , testSolution 19 | , viewPrompt 20 | , waitForPrompt 21 | , submitSolution 22 | -- ** No Answers 23 | , execSolution_ 24 | , execSolutionWith_ 25 | , testSolution_ 26 | , viewPrompt_ 27 | , waitForPrompt_ 28 | , submitSolution_ 29 | -- * Load Inputs 30 | , loadInput 31 | , loadParseInput 32 | , loadTests 33 | , loadParseTests 34 | -- * Util 35 | , mkSpec 36 | ) where 37 | 38 | import AOC.Challenge 39 | import AOC.Run 40 | import AOC.Run.Config 41 | import AOC.Run.Load 42 | import AOC.Solver 43 | import AOC.Util 44 | import Advent 45 | import Control.Monad.Except 46 | import Data.Bifunctor 47 | import Data.Text (Text) 48 | 49 | -- | Run the solution indicated by the challenge spec on the official 50 | -- puzzle input. Get answer as result. 51 | execSolution :: ChallengeSpec -> IO String 52 | execSolution cs = eitherIO $ do 53 | cfg <- liftIO $ configFile defConfPath 54 | out <- mainRun cfg . defaultMRO $ TSDayPart cs 55 | res <- maybeToEither ["Result not found in result map (Internal Error)"] $ 56 | lookupSolution cs out 57 | liftEither $ snd res 58 | 59 | -- | Run the solution indicated by the challenge spec on a custom input. 60 | -- Get answer as result. 61 | execSolutionWith 62 | :: ChallengeSpec 63 | -> String -- ^ custom puzzle input 64 | -> IO String 65 | execSolutionWith cs inp = eitherIO $ do 66 | cfg <- liftIO $ configFile defConfPath 67 | out <- mainRun cfg $ (defaultMRO (TSDayPart cs)) 68 | { _mroInput = \_ _ -> pure $ Just inp 69 | } 70 | res <- maybeToEither ["Result not found in result map (Internal Error)"] $ 71 | lookupSolution cs out 72 | liftEither $ snd res 73 | 74 | -- | Run test suite for a given challenge spec. 75 | -- 76 | -- Returns 'Just' if any tests were run, with a 'Bool' specifying whether 77 | -- or not all tests passed. 78 | testSolution :: ChallengeSpec -> IO (Maybe Bool) 79 | testSolution cs = eitherIO $ do 80 | cfg <- liftIO $ configFile defConfPath 81 | out <- mainRun cfg $ (defaultMRO (TSDayPart cs)) 82 | { _mroTest = True 83 | } 84 | res <- maybeToEither ["Result not found in result map (Internal Error)"] $ 85 | lookupSolution cs out 86 | pure $ fst res 87 | 88 | -- | View the prompt for a given challenge spec. 89 | viewPrompt :: ChallengeSpec -> IO Text 90 | viewPrompt cs = eitherIO $ do 91 | cfg <- liftIO $ configFile defConfPath 92 | out <- mainView cfg . defaultMVO $ TSDayPart cs 93 | maybeToEither ["Prompt not found in result map (Internal Error)"] $ 94 | lookupSolution cs out 95 | 96 | -- | Countdown to get the prompt for a given challenge spec, if not yet 97 | -- available. 98 | waitForPrompt :: ChallengeSpec -> IO Text 99 | waitForPrompt cs = eitherIO $ do 100 | cfg <- liftIO $ configFile defConfPath 101 | out <- mainView cfg $ (defaultMVO (TSDayPart cs)) 102 | { _mvoWait = True 103 | } 104 | maybeToEither ["Prompt not found in result map (Internal Error)"] $ 105 | lookupSolution cs out 106 | 107 | -- | Submit solution for a given challenge spec, and lock if correct. 108 | submitSolution :: ChallengeSpec -> IO (Text, SubmitRes) 109 | submitSolution cs = eitherIO $ do 110 | cfg <- liftIO $ configFile defConfPath 111 | (raw, res) <- mainSubmit cfg . defaultMSO $ cs 112 | -- automatically get part 2 if correct 113 | -- but we should probably make this global (command line level too?) 114 | case (res, _csPart cs) of 115 | (SubCorrect _, Part1) -> liftIO . viewPrompt_ $ cs { _csPart = Part2 } 116 | _ -> pure () 117 | pure (raw, res) 118 | 119 | -- | Result-suppressing version of 'execSolution'. 120 | execSolution_ :: ChallengeSpec -> IO () 121 | execSolution_ = void . execSolution 122 | 123 | -- | Result-suppressing version of 'execSolutionWith'. 124 | execSolutionWith_ 125 | :: ChallengeSpec 126 | -> String -- ^ custom puzzle input 127 | -> IO () 128 | execSolutionWith_ cs = void . execSolutionWith cs 129 | 130 | -- | Result-suppressing version of 'testSolution'. 131 | testSolution_ :: ChallengeSpec -> IO () 132 | testSolution_ = void . testSolution 133 | 134 | -- | Result-suppressing version of 'viewPrompt'. 135 | viewPrompt_ :: ChallengeSpec -> IO () 136 | viewPrompt_ = void . viewPrompt 137 | 138 | -- | Result-suppressing version of 'waitForPrompt'. 139 | waitForPrompt_ :: ChallengeSpec -> IO () 140 | waitForPrompt_ = void . waitForPrompt 141 | 142 | -- | Result-suppressing version of 'submitSolution'. 143 | submitSolution_ :: ChallengeSpec -> IO () 144 | submitSolution_ = void . submitSolution 145 | 146 | -- | Run the parser of a solution, given its 'ChallengeSpec'. 147 | -- 148 | -- @ 149 | -- 'loadParseInput' (solSpec 'day01a) day01a 150 | -- @ 151 | loadParseInput :: ChallengeSpec -> a :~> b -> IO a 152 | loadParseInput cs s = eitherIO $ do 153 | i <- liftIO $ loadInput cs 154 | maybeToEither ["No parse"] $ sParse s i 155 | 156 | -- | Run the parser of a solution on test data, given its 'ChallengeSpec'. 157 | -- 158 | -- @ 159 | -- 'loadParseTests' (solSpec 'day01a) day01a 160 | -- @ 161 | loadParseTests :: ChallengeSpec -> a :~> b -> IO [(Maybe a, TestMeta)] 162 | loadParseTests cs s = (map . first) (sParse s) <$> loadTests cs 163 | 164 | -- | Load input for a given challenge 165 | loadInput :: ChallengeSpec -> IO String 166 | loadInput cs = eitherIO $ do 167 | CD{..} <- liftIO $ do 168 | Cfg{..} <- configFile defConfPath 169 | challengeData _cfgSession _cfgYear cs 170 | liftEither _cdInput 171 | 172 | -- | Load test cases for a given challenge 173 | loadTests :: ChallengeSpec -> IO [(String, TestMeta)] 174 | loadTests cs = do 175 | Cfg{..} <- configFile defConfPath 176 | _cdTests <$> challengeData _cfgSession _cfgYear cs 177 | 178 | -- | Unsafely create a 'ChallengeSpec' from a day number and part. 179 | -- 180 | -- Is undefined if given a day number out of range (1-25). 181 | mkSpec :: Integer -> Part -> ChallengeSpec 182 | mkSpec i = CS (mkDay_ i) 183 | 184 | eitherIO :: ExceptT [String] IO a -> IO a 185 | eitherIO act = runExceptT act >>= \case 186 | Right x -> pure x 187 | Left es -> fail $ unlines es 188 | 189 | -------------------------------------------------------------------------------- /src/AOC/Solver.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeFamilies #-} 2 | 3 | -- | 4 | -- Module : AOC.Solver 5 | -- Copyright : (c) Justin Le 2021 6 | -- License : BSD3 7 | -- 8 | -- Maintainer : justin@jle.im 9 | -- Stability : experimental 10 | -- Portability : non-portable 11 | -- 12 | -- Types to drive the challenge runner and help speed up/clean up 13 | -- solutions. 14 | -- 15 | 16 | module AOC.Solver ( 17 | (:~>)(..) 18 | , withSolver, withSolver' 19 | , SomeSolution(.., MkSomeSol) 20 | , SolutionError(..) 21 | , runSolution 22 | , runSomeSolution 23 | , ssIsNF 24 | -- * 'DynoMap' 25 | , runSolutionWith 26 | , runSomeSolutionWith 27 | , dyno 28 | , dyno_ 29 | , TestType(..) 30 | ) where 31 | 32 | import AOC.Util 33 | import AOC.Util.DynoMap 34 | import Control.DeepSeq 35 | import Data.Dependent.Sum 36 | import Data.Functor.Identity 37 | import Data.Map (Map) 38 | import GHC.Generics (Generic) 39 | 40 | -- | Abstracting over the type of a challenge solver to help with cleaner 41 | -- solutions. 42 | -- 43 | -- A @a ':~>' b@ encapsulates something that solves a challenge with input 44 | -- type @a@ into a response of type @b@. 45 | -- 46 | -- Consists of a parser, a shower, and a solver. The solver solves 47 | -- a general @a -> 'Maybe' b@ function, and the parser and shower are used 48 | -- to handle the boilerplate of parsing and printing the solution. 49 | data a :~> b = MkSol 50 | { sParse :: String -> Maybe a -- ^ parse input into an @a@ 51 | , sSolve :: (?dyno :: DynoMap) 52 | => a -> Maybe b -- ^ solve an @a@ input to a @b@ solution 53 | , sShow :: b -> String -- ^ print out the @b@ solution in a pretty way 54 | } 55 | 56 | -- | Wrap an @a ':~>' b@ and hide the type variables so we can put 57 | -- different solutions in a container. 58 | data SomeSolution where 59 | MkSomeSolWH :: a :~> b -> SomeSolution 60 | MkSomeSolNF :: (NFData a, NFData b) => a :~> b -> SomeSolution 61 | 62 | -- | Check if a 'SomeSolution' is equipped with an 'NFData' instance on the 63 | -- types 64 | ssIsNF :: SomeSolution -> Bool 65 | ssIsNF = \case 66 | MkSomeSolWH _ -> False 67 | MkSomeSolNF _ -> True 68 | 69 | data SomeSolHelp where 70 | SSH :: a :~> b -> SomeSolHelp 71 | 72 | toHelp :: SomeSolution -> SomeSolHelp 73 | toHelp (MkSomeSolWH x) = SSH x 74 | toHelp (MkSomeSolNF x) = SSH x 75 | 76 | -- | Handy pattern to work with both 'MkSomeSolWH' and 'MkSomeSolNF'. As 77 | -- a constructor, just uses 'MkSomeSolWH', so might not be desirable. 78 | pattern MkSomeSol :: () => forall a b. () => a :~> b -> SomeSolution 79 | pattern MkSomeSol s <- (toHelp->SSH s) 80 | where 81 | MkSomeSol x = MkSomeSolWH x 82 | {-# COMPLETE MkSomeSol #-} 83 | 84 | -- | Errors that might happen when running a ':~>' on some input. 85 | data SolutionError = SEParse 86 | | SESolve 87 | deriving stock (Show, Eq, Ord, Generic) 88 | 89 | instance NFData SolutionError 90 | 91 | -- | Construct a ':~>' from just a normal @String -> String@ solver. 92 | -- Does no parsing or special printing treatment. 93 | withSolver' :: (String -> String) -> String :~> String 94 | withSolver' f = withSolver (Just . f) 95 | 96 | -- | Construct a ':~>' from a @String -> 'Maybe' String@ solver, which 97 | -- might fail. Does no parsing or special printing treatment. 98 | withSolver :: (String -> Maybe String) -> String :~> String 99 | withSolver f = MkSol 100 | { sParse = Just 101 | , sShow = id 102 | , sSolve = f 103 | } 104 | 105 | -- | Run a ':~>' on some input. 106 | runSolution :: a :~> b -> String -> Either SolutionError String 107 | runSolution = runSolutionWith mempty 108 | 109 | -- | Run a ':~>' on some input, with a map of dynamic values for testing 110 | runSolutionWith 111 | :: Map String (DSum TestType Identity) -- ^ map of dynamic values for testing with 'lookupDyno'. 112 | -> a :~> b 113 | -> String 114 | -> Either SolutionError String 115 | runSolutionWith dm MkSol{..} (stripNewline->s) = do 116 | x <- maybeToEither SEParse . sParse $ s 117 | y <- maybeToEither SESolve . sSolve $ x 118 | pure $ sShow y 119 | where 120 | ?dyno = Dyno dm 121 | 122 | -- | Run a 'SomeSolution' on some input. 123 | runSomeSolution 124 | :: SomeSolution 125 | -> String 126 | -> Either SolutionError String 127 | runSomeSolution = runSomeSolutionWith mempty 128 | 129 | -- | Run a 'SomeSolution' on some input, with a map of dynamic values for 130 | -- testing 131 | runSomeSolutionWith 132 | :: Map String (DSum TestType Identity) -- ^ map of dynamic values for testing with 'lookupDyno'. 133 | -> SomeSolution 134 | -> String 135 | -> Either SolutionError String 136 | runSomeSolutionWith dm (MkSomeSol c) = runSolutionWith dm c 137 | 138 | -- | From a @?dyno@ Implicit Params, look up a value at a given key. Meant 139 | -- to be used with TypeApplications: 140 | -- 141 | -- > 'dyno' @"hello" 142 | -- 143 | -- This can be used within the body of 'sSolve', since it will always be 144 | -- called with the implicit parameter. 145 | -- 146 | -- When called on actual puzzle input, result will always be 'Nothing'. 147 | -- But, for some test inputs, there might be supplied values. 148 | -- 149 | -- This is useful for when some problems have parameters that are 150 | -- different with test inputs than for actual inputs. 151 | dyno 152 | :: forall a. (HasTestType a, ?dyno :: DynoMap) 153 | => String 154 | -> Maybe a 155 | dyno = (`lookupDyno` ?dyno) 156 | 157 | -- | A version of 'dyno' taking a default value in case the key is not 158 | -- in the map. When called on actual puzzle input, this is always 'id'. 159 | -- However, for some test inputs, there might be supplied values. 160 | -- 161 | -- Meant to be used with TypeApplications: 162 | -- 163 | -- > 'dyno_' @"hello" 7 164 | -- 165 | -- This is useful for when some problems have parameters that are 166 | -- different with test inputs than for actual inputs. 167 | dyno_ 168 | :: forall a. (HasTestType a, ?dyno :: DynoMap) 169 | => String 170 | -> a -- ^ default 171 | -> a 172 | dyno_ str def = lookupDynoWith str def ?dyno 173 | -------------------------------------------------------------------------------- /src/AOC/Util.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Util 3 | -- Copyright : (c) Justin Le 2021 4 | -- License : BSD3 5 | -- 6 | -- Maintainer : justin@jle.im 7 | -- Stability : experimental 8 | -- Portability : non-portable 9 | -- 10 | -- Assorted utility functions and orphans used for solutions. 11 | -- 12 | 13 | module AOC.Util ( 14 | strip 15 | , stripNewline 16 | , eitherToMaybe 17 | , firstJust 18 | , maybeToEither 19 | , maybeAlt 20 | , traceShowIdMsg 21 | , traceShowMsg 22 | ) where 23 | 24 | import Control.Applicative 25 | import Control.Monad.Except 26 | import Data.Foldable 27 | import Debug.Trace 28 | import qualified Data.Text as T 29 | 30 | -- | Strip trailing and leading whitespace. 31 | strip :: String -> String 32 | strip = T.unpack . T.strip . T.pack 33 | 34 | -- | Strip trailing newline 35 | stripNewline :: String -> String 36 | stripNewline = reverse . dropWhile (== '\n') . reverse 37 | 38 | -- | Convert an 'Either' into a 'Maybe', or any 'Alternative' instance, 39 | -- forgetting the error value. 40 | eitherToMaybe :: Alternative m => Either e a -> m a 41 | eitherToMaybe = either (const empty) pure 42 | 43 | -- | Convert a 'Maybe' into an 'Either', or any 'MonadError' instance, by 44 | -- providing an error value in case 'Nothing' was given. 45 | maybeToEither :: MonadError e m => e -> Maybe a -> m a 46 | maybeToEither e = maybe (throwError e) pure 47 | 48 | -- | Like 'find', but instead of taking an @a -> Bool@, takes an @a -> 49 | -- Maybe b@ and returns the first success. 50 | firstJust 51 | :: Foldable t 52 | => (a -> Maybe b) 53 | -> t a 54 | -> Maybe b 55 | firstJust p = asum . map p . toList 56 | 57 | -- | Generalize a 'Maybe' to any 'Alternative' 58 | maybeAlt :: Alternative m => Maybe a -> m a 59 | maybeAlt = maybe empty pure 60 | 61 | -- | Like 'traceShowId' but with an extra message 62 | traceShowIdMsg :: Show a => String -> a -> a 63 | traceShowIdMsg msg x = trace (msg ++ show x) x 64 | 65 | -- | Like 'traceShow' but with an extra message 66 | traceShowMsg :: Show a => String -> a -> b -> b 67 | traceShowMsg msg x = trace (msg ++ show x) 68 | -------------------------------------------------------------------------------- /src/AOC/Util/DynoMap.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeFamilies #-} 2 | 3 | module AOC.Util.DynoMap ( 4 | DynoMap(..) 5 | , lookupDyno 6 | , lookupDynoWith 7 | , TestType(..) 8 | , HasTestType(..) 9 | ) where 10 | 11 | import Control.Monad 12 | import Data.Constraint 13 | import Data.Constraint.Compose 14 | import Data.Constraint.Extras 15 | import Data.Dependent.Sum 16 | import Data.Functor.Identity 17 | import Data.GADT.Compare 18 | import Data.GADT.Show 19 | import Data.Kind 20 | import Data.Map (Map) 21 | import Data.Maybe 22 | import Data.Type.Equality 23 | import qualified Data.Map as M 24 | 25 | data TestType :: Type -> Type where 26 | TTInt :: TestType Int 27 | TTString :: TestType String 28 | 29 | deriving stock instance Show (TestType a) 30 | instance GShow TestType where 31 | gshowsPrec = showsPrec 32 | instance ArgDict Show TestType where 33 | type ConstraintsFor TestType Show = () 34 | argDict = \case 35 | TTInt -> Dict 36 | TTString -> Dict 37 | instance ArgDict (ComposeC Show Identity) TestType where 38 | type ConstraintsFor TestType (ComposeC Show Identity) = () 39 | argDict = \case 40 | TTInt -> Dict 41 | TTString -> Dict 42 | 43 | instance GEq TestType where 44 | geq = \case 45 | TTInt -> \case 46 | TTInt -> Just Refl 47 | _ -> Nothing 48 | TTString -> \case 49 | TTString -> Just Refl 50 | _ -> Nothing 51 | 52 | class HasTestType a where 53 | hasTestType :: TestType a 54 | instance HasTestType Int where hasTestType = TTInt 55 | instance HasTestType String where hasTestType = TTString 56 | 57 | newtype DynoMap = Dyno { runDyno :: Map String (DSum TestType Identity) } 58 | deriving newtype (Semigroup, Monoid) 59 | 60 | fromTestType 61 | :: forall a f. HasTestType a 62 | => DSum TestType f 63 | -> Maybe (f a) 64 | fromTestType (tt :=> x) = (`gcastWith` x) <$> (tt `geq` hasTestType @a) 65 | 66 | 67 | -- | Lookup the value at a given key in a 'Dyno'. 68 | -- 69 | -- > lookupDyno "hello" 70 | lookupDyno 71 | :: forall a. HasTestType a 72 | => String 73 | -> DynoMap 74 | -> Maybe a 75 | lookupDyno sm = fmap runIdentity . fromTestType 76 | <=< M.lookup sm 77 | . runDyno 78 | 79 | -- | Like 'lookupDyno', but with a default value to be returned if the key 80 | -- is not found or has the wrong type. 81 | lookupDynoWith 82 | :: forall a. HasTestType a 83 | => String 84 | -> a 85 | -> DynoMap 86 | -> a 87 | lookupDynoWith sm def = fromMaybe def . lookupDyno sm 88 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: lts-18.18 21 | 22 | # User packages to be built. 23 | # Various formats can be used as shown in the example below. 24 | # 25 | # packages: 26 | # - some-directory 27 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 28 | # - location: 29 | # git: https://github.com/commercialhaskell/stack.git 30 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 31 | # - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a 32 | # subdirs: 33 | # - auto-update 34 | # - wai 35 | packages: 36 | - . 37 | # Dependency packages to be pulled from upstream that are not in the resolver 38 | # using the same syntax as the packages field. 39 | # (e.g., acme-missiles-0.3) 40 | extra-deps: 41 | - advent-of-code-api-0.2.8.0 42 | - advent-of-code-ocr-0.1.2.0 43 | # - megaparsec-8.0.0 44 | # - parser-combinators-1.2.1 45 | - finitary-2.1.1.0 46 | # - typelits-witnesses-0.4.0.0 47 | # - dependent-sum-0.7.1.0 48 | # - constraints-extras-0.3.2.0 49 | # - conduino-0.2.2.0 50 | # - list-transformer-1.0.7 51 | - monoid-extras-0.6.1 52 | 53 | allow-newer: true 54 | 55 | # Override default flag values for local packages and extra-deps 56 | # flags: {} 57 | 58 | # Extra package databases containing global packages 59 | # extra-package-dbs: [] 60 | 61 | # Control whether we use the GHC we find on the path 62 | # system-ghc: true 63 | # 64 | # Require a specific version of stack, using version ranges 65 | # require-stack-version: -any # Default 66 | # require-stack-version: ">=1.9" 67 | # 68 | # Override the architecture used by stack, especially useful on Windows 69 | # arch: i386 70 | # arch: x86_64 71 | # 72 | # Extra directories used by stack for building 73 | # extra-include-dirs: [/path/to/dir] 74 | # extra-lib-dirs: [/path/to/dir] 75 | # 76 | # Allow a newer minor version of GHC than the snapshot specifies 77 | # compiler-check: newer-minor 78 | -------------------------------------------------------------------------------- /stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: 7 | - completed: 8 | hackage: advent-of-code-api-0.2.8.0@sha256:37f277d71f846f78f393eef786e1834648d7de951a8061aefa8b06e1aa5e0aa7,2351 9 | pantry-tree: 10 | size: 1185 11 | sha256: 9f2e833e9127ca801d94b520bf63189a828ea2ae571fe8bb2922909efa98fabb 12 | original: 13 | hackage: advent-of-code-api-0.2.8.0 14 | - completed: 15 | hackage: advent-of-code-ocr-0.1.2.0@sha256:86d2d6ea54072283dfb11ee0d1cc69db36da17578d85bc23d5a6cf8f57328256,2927 16 | pantry-tree: 17 | size: 614 18 | sha256: 40abbd30800075fd8f38404b7950908f13f9425c071a96e2088ade46061a357c 19 | original: 20 | hackage: advent-of-code-ocr-0.1.2.0 21 | - completed: 22 | hackage: finitary-2.1.1.0@sha256:f0f19aace424e5fdb5543fbcfb9446e50c8cb349fe7564642c5c25dbe3418076,2743 23 | pantry-tree: 24 | size: 438 25 | sha256: 38e03206b355db7a6a2f3ad73039b18b15245141b6974080b7d9a99e82ddf76a 26 | original: 27 | hackage: finitary-2.1.1.0 28 | - completed: 29 | hackage: monoid-extras-0.6.1@sha256:15312b3ed40481fc879fc7021857421794403763325d5c05b8536198012170b1,2197 30 | pantry-tree: 31 | size: 1187 32 | sha256: 0baa6b12d8bbc9872d91f5dca7738759e3bd79bf719d1e94b96470fc1dda23b6 33 | original: 34 | hackage: monoid-extras-0.6.1 35 | snapshots: 36 | - completed: 37 | size: 586296 38 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml 39 | sha256: 63539429076b7ebbab6daa7656cfb079393bf644971156dc349d7c0453694ac2 40 | original: lts-18.18 41 | -------------------------------------------------------------------------------- /template/DayXX.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day${day} 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day ${day_short}. See "AOC.Solver" for the types used in this module! 12 | -- 13 | -- After completing the challenge, it is recommended to: 14 | -- 15 | -- * Replace "AOC.Prelude" imports to specific modules (with explicit 16 | -- imports) for readability. 17 | -- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@ 18 | -- pragmas. 19 | -- * Replace the partial type signatures underscores in the solution 20 | -- types @_ :~> _@ with the actual types of inputs and outputs of the 21 | -- solution. You can delete the type signatures completely and GHC 22 | -- will recommend what should go in place of the underscores. 23 | 24 | module AOC.Challenge.Day${day} ( 25 | -- day${day}a 26 | -- , day${day}b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | import qualified Data.Graph.Inductive as G 32 | import qualified Data.IntMap as IM 33 | import qualified Data.IntSet as IS 34 | import qualified Data.List.NonEmpty as NE 35 | import qualified Data.List.PointedList as PL 36 | import qualified Data.List.PointedList.Circular as PLC 37 | import qualified Data.Map as M 38 | import qualified Data.OrdPSQ as PSQ 39 | import qualified Data.Sequence as Seq 40 | import qualified Data.Set as S 41 | import qualified Data.Text as T 42 | import qualified Data.Vector as V 43 | import qualified Linear as L 44 | import qualified Text.Megaparsec as P 45 | import qualified Text.Megaparsec.Char as P 46 | import qualified Text.Megaparsec.Char.Lexer as PP 47 | 48 | day${day}a :: _ :~> _ 49 | day${day}a = MkSol 50 | { sParse = Just . lines 51 | , sShow = show 52 | , sSolve = Just 53 | } 54 | 55 | day${day}b :: _ :~> _ 56 | day${day}b = MkSol 57 | { sParse = sParse day${day}a 58 | , sShow = show 59 | , sSolve = Just 60 | } 61 | -------------------------------------------------------------------------------- /template/feed-item.xml.template: -------------------------------------------------------------------------------- 1 | 2 | Day ${day} 3 | ${body} 4 | https://github.com/${github}/advent-of-code-${year}/blob/master/reflections.md#day-${day} 5 | ${time} 6 | 7 | -------------------------------------------------------------------------------- /template/feed.xml.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ${name}'s Advent of Code ${year} Reflections 6 | Reflections for my Advent of Code solutions as I try to solve them all in fun ways using Haskell! 7 | https://github.com/${github}/advent-of-code-${year}/blob/master/reflections.md 8 | Copyright ${year} ${name} 9 | en-us 10 | ${time} 11 | ${email} 12 | ${time} 13 | ${email} 14 | Shake + Template 15 | ${body} 16 | 17 | 18 | -------------------------------------------------------------------------------- /template/reflection.md.template: -------------------------------------------------------------------------------- 1 | Day ${dayshort} 2 | ------ 3 | 4 | 9 | 10 | *[Prompt][d${daylong}p]* / *[Code][d${daylong}g]* / *[Rendered][d${daylong}h]* / *[Standalone Reflection Page][d${daylong}r]* 11 | 12 | [d${daylong}p]: https://adventofcode.com/${year}/day/${dayshort} 13 | [d${daylong}g]: https://github.com/${github}/advent-of-code-${year}/blob/master/src/AOC/Challenge/Day${daylong}.hs 14 | [d${daylong}h]: https://${github}.github.io/advent-of-code-${year}/src/AOC.Challenge.Day${daylong}.html 15 | [d${daylong}r]: https://github.com/${github}/advent-of-code-${year}/blob/master/reflections-out/day${daylong}.md 16 | 17 | ${body} 18 | 19 | ### Day ${dayshort} Benchmarks 20 | 21 | ``` 22 | ${benchmarks} 23 | ``` 24 | -------------------------------------------------------------------------------- /template/reflections.md.template: -------------------------------------------------------------------------------- 1 | Reflections 2 | =========== 3 | 4 | 8 | 9 | ${other_years} 10 | 11 | ${other_links} 12 | 13 | [Available as an RSS Feed][rss] 14 | 15 | [rss]: ${rss} 16 | 17 | Table of Contents 18 | ----------------- 19 | 20 | ${toc} 21 | 22 | ${body} 23 | -------------------------------------------------------------------------------- /template/standalone-reflection.md.template: -------------------------------------------------------------------------------- 1 | Day ${dayshort} 2 | === 3 | 4 | 9 | 10 | *[all][reflections]* / ${other_days} 11 | 12 | [reflections]: https://github.com/${github}/advent-of-code-${year}/blob/master/reflections.md 13 | ${other_links} 14 | 15 | [Available as an RSS Feed][rss] 16 | 17 | [rss]: ${rss} 18 | 19 | *[Prompt][d${daylong}p]* / *[Code][d${daylong}g]* / *[Rendered][d${daylong}h]* 20 | 21 | [d${daylong}p]: https://adventofcode.com/${year}/day/${dayshort} 22 | [d${daylong}g]: https://github.com/${github}/advent-of-code-${year}/blob/master/src/AOC/Challenge/Day${daylong}.hs 23 | [d${daylong}h]: https://${github}.github.io/advent-of-code-${year}/src/AOC.Challenge.Day${daylong}.html 24 | 25 | ${body} 26 | 27 | *[Back to all reflections for ${year}][reflections]* 28 | 29 | ## Day ${dayshort} Benchmarks 30 | 31 | ``` 32 | ${benchmarks} 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /test-data/2016/01a.txt: -------------------------------------------------------------------------------- 1 | R2, L3 2 | >>> 5 3 | R2, R2, R2 4 | >>> 2 5 | R5, L5, R5, R3 6 | >>> 12 7 | -------------------------------------------------------------------------------- /test-data/2016/01b.txt: -------------------------------------------------------------------------------- 1 | R8, R4, R4, R8 2 | >>> 4 3 | -------------------------------------------------------------------------------- /test-data/2016/02a.txt: -------------------------------------------------------------------------------- 1 | ULL 2 | RRDDD 3 | LURDL 4 | UUUUD 5 | >>> 1985 6 | -------------------------------------------------------------------------------- /test-data/2016/02b.txt: -------------------------------------------------------------------------------- 1 | ULL 2 | RRDDD 3 | LURDL 4 | UUUUD 5 | >>> 5DB3 6 | -------------------------------------------------------------------------------- /test-data/2016/03a.txt: -------------------------------------------------------------------------------- 1 | 5 10 25 2 | >>> 0 3 | -------------------------------------------------------------------------------- /test-data/2016/04a.txt: -------------------------------------------------------------------------------- 1 | aaaaa-bbb-z-y-x-123[abxyz] 2 | a-b-c-d-e-f-g-h-987[abcde] 3 | not-a-real-room-404[oarel] 4 | totally-real-room-200[decoy] 5 | >>> 1514 6 | -------------------------------------------------------------------------------- /test-data/2016/05a.txt: -------------------------------------------------------------------------------- 1 | abc 2 | >>> 18f47a30 3 | -------------------------------------------------------------------------------- /test-data/2016/05b.txt: -------------------------------------------------------------------------------- 1 | abc 2 | >>> 05ace8e3 3 | -------------------------------------------------------------------------------- /test-data/2016/06a.txt: -------------------------------------------------------------------------------- 1 | eedadn 2 | drvtee 3 | eandsr 4 | raavrd 5 | atevrs 6 | tsrnev 7 | sdttsa 8 | rasrtv 9 | nssdts 10 | ntnada 11 | svetve 12 | tesnvt 13 | vntsnd 14 | vrdear 15 | dvrsen 16 | enarar 17 | >>> easter 18 | -------------------------------------------------------------------------------- /test-data/2016/06b.txt: -------------------------------------------------------------------------------- 1 | eedadn 2 | drvtee 3 | eandsr 4 | raavrd 5 | atevrs 6 | tsrnev 7 | sdttsa 8 | rasrtv 9 | nssdts 10 | ntnada 11 | svetve 12 | tesnvt 13 | vntsnd 14 | vrdear 15 | dvrsen 16 | enarar 17 | >>> advent 18 | -------------------------------------------------------------------------------- /test-data/2016/07a.txt: -------------------------------------------------------------------------------- 1 | abba[mnop]qrst 2 | >>> 1 3 | abcd[bddb]xyyx 4 | >>> 0 5 | aaaa[qwer]tyui 6 | >>> 0 7 | ioxxoj[asdfgh]zxcvbn 8 | >>> 1 9 | abba[mnop]qrst 10 | abcd[bddb]xyyx 11 | aaaa[qwer]tyui 12 | ioxxoj[asdfgh]zxcvbn 13 | >>> 2 14 | -------------------------------------------------------------------------------- /test-data/2016/07b.txt: -------------------------------------------------------------------------------- 1 | aba[bab]xyz 2 | >>> 1 3 | xyx[xyx]xyx 4 | >>> 0 5 | aaa[kek]eke 6 | >>> 1 7 | zazbz[bzb]cdb 8 | >>> 1 9 | aba[bab]xyz 10 | xyx[xyx]xyx 11 | aaa[kek]eke 12 | zazbz[bzb]cdb 13 | >>> 3 14 | -------------------------------------------------------------------------------- /test-data/2016/08a.txt: -------------------------------------------------------------------------------- 1 | rect 3x2 2 | rotate column x=1 by 1 3 | rotate row y=0 by 4 4 | rotate column x=1 by 1 5 | >>> 6 6 | -------------------------------------------------------------------------------- /test-data/2016/09a.txt: -------------------------------------------------------------------------------- 1 | ADVENT 2 | >>> 6 3 | A(1x5)BC 4 | >>> 7 5 | (3x3)XYZ 6 | >>> 9 7 | A(2x2)BCD(2x2)EFG 8 | >>> 11 9 | (6x1)(1x3)A 10 | >>> 6 11 | X(8x2)(3x3)ABCY 12 | >>> 18 13 | -------------------------------------------------------------------------------- /test-data/2016/09b.txt: -------------------------------------------------------------------------------- 1 | (3x3)XYZ 2 | >>> 9 3 | X(8x2)(3x3)ABCY 4 | >>> 20 5 | (27x12)(20x12)(13x14)(7x10)(1x12)A 6 | >>> 241920 7 | (25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN 8 | >>> 445 9 | -------------------------------------------------------------------------------- /test-data/2017/01a.txt: -------------------------------------------------------------------------------- 1 | 1122 2 | >>> 3 3 | 1111 4 | >>> 4 5 | 1234 6 | >>> 0 7 | 91212129 8 | >>> 9 9 | -------------------------------------------------------------------------------- /test-data/2017/01b.txt: -------------------------------------------------------------------------------- 1 | 1212 2 | >>> 6 3 | 1221 4 | >>> 0 5 | 123425 6 | >>> 4 7 | 123123 8 | >>> 12 9 | 12131415 10 | >>> 4 11 | -------------------------------------------------------------------------------- /test-data/2017/02a.txt: -------------------------------------------------------------------------------- 1 | 5 1 9 5 2 | 7 5 3 3 | 2 4 6 8 4 | >>> 18 5 | -------------------------------------------------------------------------------- /test-data/2017/02b.txt: -------------------------------------------------------------------------------- 1 | 5 9 2 8 2 | 9 4 7 3 3 | 3 8 6 5 4 | >>> 9 5 | -------------------------------------------------------------------------------- /test-data/2017/03a.txt: -------------------------------------------------------------------------------- 1 | 1 2 | >>> 0 3 | 12 4 | >>> 3 5 | 23 6 | >>> 2 7 | 1024 8 | >>> 31 9 | -------------------------------------------------------------------------------- /test-data/2017/03b.txt: -------------------------------------------------------------------------------- 1 | 12 2 | >>> 23 3 | 60 4 | >>> 122 5 | 748 6 | >>> 806 7 | -------------------------------------------------------------------------------- /test-data/2017/04a.txt: -------------------------------------------------------------------------------- 1 | aa bb cc dd ee 2 | >>> 1 3 | aa bb cc dd aa 4 | >>> 0 5 | aa bb cc dd aaa 6 | >>> 1 7 | aa bb cc dd ee 8 | aa bb cc dd aa 9 | aa bb cc dd aaa 10 | >>> 2 11 | -------------------------------------------------------------------------------- /test-data/2017/04b.txt: -------------------------------------------------------------------------------- 1 | abcde fghij 2 | >>> 1 3 | abcde xyz ecdab 4 | >>> 0 5 | a ab abc abd abf abj 6 | >>> 1 7 | iiii oiii ooii oooi oooo 8 | >>> 1 9 | oiii ioii iioi iiio 10 | >>> 0 11 | abcde fghij 12 | abcde xyz ecdab 13 | a ab abc abd abf abj 14 | iiii oiii ooii oooi oooo 15 | oiii ioii iioi iiio 16 | >>> 3 17 | -------------------------------------------------------------------------------- /test-data/2017/05a.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 3 3 | 0 4 | 1 5 | -3 6 | >>> 5 7 | -------------------------------------------------------------------------------- /test-data/2017/05b.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 3 3 | 0 4 | 1 5 | -3 6 | >>> 10 7 | -------------------------------------------------------------------------------- /test-data/2017/06a.txt: -------------------------------------------------------------------------------- 1 | 0 2 7 0 2 | >>> 5 3 | -------------------------------------------------------------------------------- /test-data/2017/06b.txt: -------------------------------------------------------------------------------- 1 | 0 2 7 0 2 | >>> 4 3 | -------------------------------------------------------------------------------- /test-data/2017/07a.txt: -------------------------------------------------------------------------------- 1 | pbga (66) 2 | xhth (57) 3 | ebii (61) 4 | havc (66) 5 | ktlj (57) 6 | fwft (72) -> ktlj, cntj, xhth 7 | qoyq (66) 8 | padx (45) -> pbga, havc, qoyq 9 | tknk (41) -> ugml, padx, fwft 10 | jptl (61) 11 | ugml (68) -> gyxo, ebii, jptl 12 | gyxo (61) 13 | cntj (57) 14 | >>> tknk 15 | -------------------------------------------------------------------------------- /test-data/2017/07b.txt: -------------------------------------------------------------------------------- 1 | pbga (66) 2 | xhth (57) 3 | ebii (61) 4 | havc (66) 5 | ktlj (57) 6 | fwft (72) -> ktlj, cntj, xhth 7 | qoyq (66) 8 | padx (45) -> pbga, havc, qoyq 9 | tknk (41) -> ugml, padx, fwft 10 | jptl (61) 11 | ugml (68) -> gyxo, ebii, jptl 12 | gyxo (61) 13 | cntj (57) 14 | >>> 60 15 | -------------------------------------------------------------------------------- /test-data/2017/08a.txt: -------------------------------------------------------------------------------- 1 | b inc 5 if a > 1 2 | a inc 1 if b < 5 3 | c dec -10 if a >= 1 4 | c inc -20 if c == 10 5 | >>> 1 6 | -------------------------------------------------------------------------------- /test-data/2017/08b.txt: -------------------------------------------------------------------------------- 1 | b inc 5 if a > 1 2 | a inc 1 if b < 5 3 | c dec -10 if a >= 1 4 | c inc -20 if c == 10 5 | >>> 10 6 | -------------------------------------------------------------------------------- /test-data/2017/09a.txt: -------------------------------------------------------------------------------- 1 | {} 2 | >>> 1 3 | {{{}}} 4 | >>> 6 5 | {{},{}} 6 | >>> 5 7 | {{{},{},{{}}}} 8 | >>> 16 9 | {,,,} 10 | >>> 1 11 | {{},{},{},{}} 12 | >>> 9 13 | {{},{},{},{}} 14 | >>> 9 15 | {{},{},{},{}} 16 | >>> 3 17 | -------------------------------------------------------------------------------- /test-data/2017/09b.txt: -------------------------------------------------------------------------------- 1 | <> 2 | >>> 0 3 | 4 | >>> 17 5 | <<<<> 6 | >>> 3 7 | <{!>}> 8 | >>> 2 9 | 10 | >>> 0 11 | > 12 | >>> 0 13 | <{o"i!a,<{i 14 | >>> 10 15 | -------------------------------------------------------------------------------- /test-data/2017/10b.txt: -------------------------------------------------------------------------------- 1 | 2 | >>> a2582a3a0e66e6e86e3812dcb672a272 3 | AoC 2017 4 | >>> 33efeb34ea91902bb2f59c9920caa6cd 5 | 1,2,3 6 | >>> 3efbe78a8d82f29979031a4aa0b16a9d 7 | 1,2,4 8 | >>> 63960835bcdc130f0b66d7ff4f6a5a8e 9 | -------------------------------------------------------------------------------- /test-data/2017/11a.txt: -------------------------------------------------------------------------------- 1 | ne,ne,ne 2 | >>> 3 3 | ne,ne,sw,sw 4 | >>> 0 5 | ne,ne,s,s 6 | >>> 2 7 | se,sw,se,sw,sw 8 | >>> 3 9 | -------------------------------------------------------------------------------- /test-data/2017/11b.txt: -------------------------------------------------------------------------------- 1 | ne,ne,ne 2 | >>> 3 3 | ne,ne,sw,sw 4 | >>> 2 5 | ne,ne,s,s 6 | >>> 2 7 | se,sw,se,sw,sw 8 | >>> 3 9 | -------------------------------------------------------------------------------- /test-data/2017/13a.txt: -------------------------------------------------------------------------------- 1 | 0: 3 2 | 1: 2 3 | 4: 4 4 | 6: 4 5 | >>> 24 6 | -------------------------------------------------------------------------------- /test-data/2017/13b.txt: -------------------------------------------------------------------------------- 1 | 0: 3 2 | 1: 2 3 | 4: 4 4 | 6: 4 5 | >>> 10 6 | -------------------------------------------------------------------------------- /test-data/2017/14a.txt: -------------------------------------------------------------------------------- 1 | flqrgnkx 2 | >>> 8108 3 | -------------------------------------------------------------------------------- /test-data/2017/14b.txt: -------------------------------------------------------------------------------- 1 | flqrgnkx 2 | >>> 1242 3 | -------------------------------------------------------------------------------- /test-data/2017/17a.txt: -------------------------------------------------------------------------------- 1 | 3 2 | >>> 638 3 | -------------------------------------------------------------------------------- /test-data/2017/18b.txt: -------------------------------------------------------------------------------- 1 | snd 1 2 | snd 2 3 | snd p 4 | rcv a 5 | rcv b 6 | rcv c 7 | rcv d 8 | >>> 3 9 | -------------------------------------------------------------------------------- /test-data/2017/19a.txt: -------------------------------------------------------------------------------- 1 | | 2 | | +--+ 3 | A | C 4 | F---|----E|--+ 5 | | | | D 6 | +B-+ +--+ 7 | >>> ABCDEF 8 | -------------------------------------------------------------------------------- /test-data/2017/19b.txt: -------------------------------------------------------------------------------- 1 | | 2 | | +--+ 3 | A | C 4 | F---|----E|--+ 5 | | | | D 6 | +B-+ +--+ 7 | >>> 38 8 | -------------------------------------------------------------------------------- /test-data/2017/20a.txt: -------------------------------------------------------------------------------- 1 | p=< 3,0,0>, v=< 2,0,0>, a=<-1,0,0> 2 | p=< 4,0,0>, v=< 0,0,0>, a=<-2,0,0> 3 | >>> 0 4 | -------------------------------------------------------------------------------- /test-data/2017/20b.txt: -------------------------------------------------------------------------------- 1 | p=<-6,0,0>, v=< 3,0,0>, a=< 0,0,0> 2 | p=<-4,0,0>, v=< 2,0,0>, a=< 0,0,0> 3 | p=<-2,0,0>, v=< 1,0,0>, a=< 0,0,0> 4 | p=< 3,0,0>, v=<-1,0,0>, a=< 0,0,0> 5 | >>> 1 6 | -------------------------------------------------------------------------------- /test-data/2018/01a.txt: -------------------------------------------------------------------------------- 1 | +1 2 | -2 3 | +3 4 | +1 5 | >>> 3 6 | +1 7 | +1 8 | +1 9 | >>> 3 10 | +1 11 | +1 12 | -2 13 | >>> 0 14 | -1 15 | -2 16 | -3 17 | >>> -6 18 | -------------------------------------------------------------------------------- /test-data/2018/01b.txt: -------------------------------------------------------------------------------- 1 | +1 2 | -2 3 | +3 4 | +1 5 | >>> 2 6 | +1 7 | -1 8 | >>> 0 9 | +3 10 | +3 11 | +4 12 | -2 13 | -4 14 | >>> 10 15 | -6 16 | +3 17 | +8 18 | +5 19 | -6 20 | >>> 5 21 | +7 22 | +7 23 | -2 24 | -7 25 | -4 26 | >>> 14 27 | -------------------------------------------------------------------------------- /test-data/2018/02a.txt: -------------------------------------------------------------------------------- 1 | abcdef 2 | bababc 3 | abbcde 4 | abcccd 5 | aabcdd 6 | abcdee 7 | ababab 8 | >>> 12 9 | -------------------------------------------------------------------------------- /test-data/2018/02b.txt: -------------------------------------------------------------------------------- 1 | abcde 2 | fghij 3 | klmno 4 | pqrst 5 | fguij 6 | axcye 7 | wvxyz 8 | >>> fgij 9 | -------------------------------------------------------------------------------- /test-data/2018/03a.txt: -------------------------------------------------------------------------------- 1 | #1 @ 1,3: 4x4 2 | #2 @ 3,1: 4x4 3 | #3 @ 5,5: 2x2 4 | >>> 4 5 | -------------------------------------------------------------------------------- /test-data/2018/03b.txt: -------------------------------------------------------------------------------- 1 | #1 @ 1,3: 4x4 2 | #2 @ 3,1: 4x4 3 | #3 @ 5,5: 2x2 4 | >>> 3 5 | -------------------------------------------------------------------------------- /test-data/2018/04a.txt: -------------------------------------------------------------------------------- 1 | [1518-11-01 00:00] Guard #10 begins shift 2 | [1518-11-01 00:05] falls asleep 3 | [1518-11-01 00:25] wakes up 4 | [1518-11-01 00:30] falls asleep 5 | [1518-11-01 00:55] wakes up 6 | [1518-11-01 23:58] Guard #99 begins shift 7 | [1518-11-02 00:40] falls asleep 8 | [1518-11-02 00:50] wakes up 9 | [1518-11-03 00:05] Guard #10 begins shift 10 | [1518-11-03 00:24] falls asleep 11 | [1518-11-03 00:29] wakes up 12 | [1518-11-04 00:02] Guard #99 begins shift 13 | [1518-11-04 00:36] falls asleep 14 | [1518-11-04 00:46] wakes up 15 | [1518-11-05 00:03] Guard #99 begins shift 16 | [1518-11-05 00:45] falls asleep 17 | [1518-11-05 00:55] wakes up 18 | >>> 240 19 | -------------------------------------------------------------------------------- /test-data/2018/04b.txt: -------------------------------------------------------------------------------- 1 | [1518-11-01 00:00] Guard #10 begins shift 2 | [1518-11-01 00:05] falls asleep 3 | [1518-11-01 00:25] wakes up 4 | [1518-11-01 00:30] falls asleep 5 | [1518-11-01 00:55] wakes up 6 | [1518-11-01 23:58] Guard #99 begins shift 7 | [1518-11-02 00:40] falls asleep 8 | [1518-11-02 00:50] wakes up 9 | [1518-11-03 00:05] Guard #10 begins shift 10 | [1518-11-03 00:24] falls asleep 11 | [1518-11-03 00:29] wakes up 12 | [1518-11-04 00:02] Guard #99 begins shift 13 | [1518-11-04 00:36] falls asleep 14 | [1518-11-04 00:46] wakes up 15 | [1518-11-05 00:03] Guard #99 begins shift 16 | [1518-11-05 00:45] falls asleep 17 | [1518-11-05 00:55] wakes up 18 | >>> 4455 19 | -------------------------------------------------------------------------------- /test-data/2018/05a.txt: -------------------------------------------------------------------------------- 1 | aA 2 | >>> 0 3 | abBA 4 | >>> 0 5 | abAB 6 | >>> 4 7 | aabAAB 8 | >>> 6 9 | dabAcCaCBAcCcaDA 10 | >>> 10 11 | -------------------------------------------------------------------------------- /test-data/2018/05b.txt: -------------------------------------------------------------------------------- 1 | dabAcCaCBAcCcaDA 2 | >>> 4 3 | -------------------------------------------------------------------------------- /test-data/2018/06a.txt: -------------------------------------------------------------------------------- 1 | 1, 1 2 | 1, 6 3 | 8, 3 4 | 3, 4 5 | 5, 5 6 | 8, 9 7 | >>> 17 8 | -------------------------------------------------------------------------------- /test-data/2018/06b.txt: -------------------------------------------------------------------------------- 1 | 1, 1 2 | 1, 6 3 | 8, 3 4 | 3, 4 5 | 5, 5 6 | 8, 9 7 | >>>lim:32:int 8 | >>> 16 9 | -------------------------------------------------------------------------------- /test-data/2018/07a.txt: -------------------------------------------------------------------------------- 1 | Step C must be finished before step A can begin. 2 | Step C must be finished before step F can begin. 3 | Step A must be finished before step B can begin. 4 | Step A must be finished before step D can begin. 5 | Step B must be finished before step E can begin. 6 | Step D must be finished before step E can begin. 7 | Step F must be finished before step E can begin. 8 | >>> CABDFE 9 | -------------------------------------------------------------------------------- /test-data/2018/07b.txt: -------------------------------------------------------------------------------- 1 | Step C must be finished before step A can begin. 2 | Step C must be finished before step F can begin. 3 | Step A must be finished before step B can begin. 4 | Step A must be finished before step D can begin. 5 | Step B must be finished before step E can begin. 6 | Step D must be finished before step E can begin. 7 | Step F must be finished before step E can begin. 8 | >>>cap:2:int 9 | >>>wait:0:int 10 | >>> 15 11 | -------------------------------------------------------------------------------- /test-data/2018/08a.txt: -------------------------------------------------------------------------------- 1 | 2 3 0 3 10 11 12 1 1 0 1 99 2 1 1 2 2 | >>> 138 3 | -------------------------------------------------------------------------------- /test-data/2018/08b.txt: -------------------------------------------------------------------------------- 1 | 2 3 0 3 10 11 12 1 1 0 1 99 2 1 1 2 2 | >>> 66 3 | -------------------------------------------------------------------------------- /test-data/2018/09a.txt: -------------------------------------------------------------------------------- 1 | 9 25 2 | >>> 32 3 | 10 1618 4 | >>> 8317 5 | 13 players; last marble is worth 7999 points 6 | >>> 146373 7 | 17 1104 8 | >>> 2764 9 | 21 6111 10 | >>> 54718 11 | 30 5807 12 | >>> 37305 13 | -------------------------------------------------------------------------------- /test-data/2018/10b.txt: -------------------------------------------------------------------------------- 1 | position=< 9, 1> velocity=< 0, 2> 2 | position=< 7, 0> velocity=<-1, 0> 3 | position=< 3, -2> velocity=<-1, 1> 4 | position=< 6, 10> velocity=<-2, -1> 5 | position=< 2, -4> velocity=< 2, 2> 6 | position=<-6, 10> velocity=< 2, -2> 7 | position=< 1, 8> velocity=< 1, -1> 8 | position=< 1, 7> velocity=< 1, 0> 9 | position=<-3, 11> velocity=< 1, -2> 10 | position=< 7, 6> velocity=<-1, -1> 11 | position=<-2, 3> velocity=< 1, 0> 12 | position=<-4, 3> velocity=< 2, 0> 13 | position=<10, -3> velocity=<-1, 1> 14 | position=< 5, 11> velocity=< 1, -2> 15 | position=< 4, 7> velocity=< 0, -1> 16 | position=< 8, -2> velocity=< 0, 1> 17 | position=<15, 0> velocity=<-2, 0> 18 | position=< 1, 6> velocity=< 1, 0> 19 | position=< 8, 9> velocity=< 0, -1> 20 | position=< 3, 3> velocity=<-1, 1> 21 | position=< 0, 5> velocity=< 0, -1> 22 | position=<-2, 2> velocity=< 2, 0> 23 | position=< 5, -2> velocity=< 1, 2> 24 | position=< 1, 4> velocity=< 2, 1> 25 | position=<-2, 7> velocity=< 2, -2> 26 | position=< 3, 6> velocity=<-1, -1> 27 | position=< 5, 0> velocity=< 1, 0> 28 | position=<-6, 0> velocity=< 2, 0> 29 | position=< 5, 9> velocity=< 1, -2> 30 | position=<14, 7> velocity=<-2, 0> 31 | position=<-3, 6> velocity=< 2, -1> 32 | >>> 3 33 | -------------------------------------------------------------------------------- /test-data/2018/11a.txt: -------------------------------------------------------------------------------- 1 | 18 2 | >>> 33,45 3 | 42 4 | >>> 21,61 5 | -------------------------------------------------------------------------------- /test-data/2018/11b.txt: -------------------------------------------------------------------------------- 1 | 18 2 | >>> 90,269,16 3 | 42 4 | >>> 232,251,12 5 | -------------------------------------------------------------------------------- /test-data/2018/14a.txt: -------------------------------------------------------------------------------- 1 | 9 2 | >>> 5158916779 3 | 5 4 | >>> 0124515891 5 | 18 6 | >>> 9251071085 7 | 2018 8 | >>> 5941429882 9 | -------------------------------------------------------------------------------- /test-data/2018/14b.txt: -------------------------------------------------------------------------------- 1 | 51589 2 | >>> 9 3 | 01245 4 | >>> 5 5 | 92510 6 | >>> 18 7 | 59414 8 | >>> 2018 9 | 5891 10 | >>> 11 11 | -------------------------------------------------------------------------------- /test-data/2018/15a.txt: -------------------------------------------------------------------------------- 1 | ######### 2 | #G..G..G# 3 | #.......# 4 | #.......# 5 | #G..E..G# 6 | #.......# 7 | #.......# 8 | #G..G..G# 9 | ######### 10 | >>> 27828 11 | ####### 12 | #.G...# 13 | #...EG# 14 | #.#.#G# 15 | #..G#E# 16 | #.....# 17 | ####### 18 | >>> 27730 19 | ####### 20 | #G..#E# 21 | #E#E.E# 22 | #G.##.# 23 | #...#E# 24 | #...E.# 25 | ####### 26 | >>> 36334 27 | ####### 28 | #E..EG# 29 | #.#G.E# 30 | #E.##E# 31 | #G..#.# 32 | #..E#.# 33 | ####### 34 | >>> 39514 35 | ####### 36 | #E.G#.# 37 | #.#G..# 38 | #G.#.G# 39 | #G..#.# 40 | #...E.# 41 | ####### 42 | >>> 27755 43 | ####### 44 | #.E...# 45 | #.#..G# 46 | #.###.# 47 | #E#G#G# 48 | #...#G# 49 | ####### 50 | >>> 28944 51 | ######### 52 | #G......# 53 | #.E.#...# 54 | #..##..G# 55 | #...##..# 56 | #...#...# 57 | #.G...G.# 58 | #.....G.# 59 | ######### 60 | >>> 18740 61 | -------------------------------------------------------------------------------- /test-data/2018/15b.txt: -------------------------------------------------------------------------------- 1 | ####### 2 | #.G...# 3 | #...EG# 4 | #.#.#G# 5 | #..G#E# 6 | #.....# 7 | ####### 8 | >>> 4988 9 | ####### 10 | #E..EG# 11 | #.#G.E# 12 | #E.##E# 13 | #G..#.# 14 | #..E#.# 15 | ####### 16 | >>> 31284 17 | ####### 18 | #E.G#.# 19 | #.#G..# 20 | #G.#.G# 21 | #G..#.# 22 | #...E.# 23 | ####### 24 | >>> 3478 25 | ####### 26 | #.E...# 27 | #.#..G# 28 | #.###.# 29 | #E#G#G# 30 | #...#G# 31 | ####### 32 | >>> 6474 33 | ######### 34 | #G......# 35 | #.E.#...# 36 | #..##..G# 37 | #...##..# 38 | #...#...# 39 | #.G...G.# 40 | #.....G.# 41 | ######### 42 | >>> 1140 43 | -------------------------------------------------------------------------------- /test-data/2018/17a.txt: -------------------------------------------------------------------------------- 1 | x=495, y=2..7 2 | y=7, x=495..501 3 | x=501, y=3..7 4 | x=498, y=2..4 5 | x=506, y=1..2 6 | x=498, y=10..13 7 | x=504, y=10..13 8 | y=13, x=498..504 9 | >>> 57 10 | x=493, y=1..2 11 | x=497, y=2..4 12 | x=503, y=2..4 13 | y=4, x=497..503 14 | x=494, y=8..9 15 | x=506, y=7..9 16 | y=9, x=495..505 17 | >>> 55 18 | -------------------------------------------------------------------------------- /test-data/2018/18a.txt: -------------------------------------------------------------------------------- 1 | .#.#...|#. 2 | .....#|##| 3 | .|..|...#. 4 | ..|#.....# 5 | #.#|||#|#| 6 | ...#.||... 7 | .|....|... 8 | ||...#|.#| 9 | |.||||..|. 10 | ...#.|..|. 11 | >>> 1147 12 | -------------------------------------------------------------------------------- /test-data/2018/20a.txt: -------------------------------------------------------------------------------- 1 | ^WNE$ 2 | >>> 3 3 | ^ENWWW(NEEE|SSE(EE|N))$ 4 | >>> 10 5 | ^ENNWSWW(NEWS|)SSSEEN(WNSE|)EE(SWEN|)NNN$ 6 | >>> 18 7 | -------------------------------------------------------------------------------- /test-data/2018/22a.txt: -------------------------------------------------------------------------------- 1 | depth: 510 2 | target: 10,10 3 | >>> 114 4 | -------------------------------------------------------------------------------- /test-data/2018/22b.txt: -------------------------------------------------------------------------------- 1 | depth: 510 2 | target: 10,10 3 | >>> 45 4 | -------------------------------------------------------------------------------- /test-data/2018/23a.txt: -------------------------------------------------------------------------------- 1 | pos=<0,0,0>, r=4 2 | pos=<1,0,0>, r=1 3 | pos=<4,0,0>, r=3 4 | pos=<0,2,0>, r=1 5 | pos=<0,5,0>, r=3 6 | pos=<0,0,3>, r=1 7 | pos=<1,1,1>, r=1 8 | pos=<1,1,2>, r=1 9 | pos=<1,3,1>, r=1 10 | >>> 7 11 | -------------------------------------------------------------------------------- /test-data/2018/23b.txt: -------------------------------------------------------------------------------- 1 | pos=<10,12,12>, r=2 2 | pos=<12,14,12>, r=2 3 | pos=<16,12,12>, r=4 4 | pos=<14,14,14>, r=6 5 | pos=<50,50,50>, r=200 6 | pos=<10,10,10>, r=5 7 | >>> 36 8 | -------------------------------------------------------------------------------- /test-data/2018/24a.txt: -------------------------------------------------------------------------------- 1 | Immune System: 2 | 17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2 3 | 989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3 4 | 5 | Infection: 6 | 801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1 7 | 4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4 8 | >>> 5216 9 | -------------------------------------------------------------------------------- /test-data/2018/24b.txt: -------------------------------------------------------------------------------- 1 | Immune System: 2 | 17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2 3 | 989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3 4 | 5 | Infection: 6 | 801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1 7 | 4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4 8 | >>> 51 9 | -------------------------------------------------------------------------------- /test-data/2019/01a.txt: -------------------------------------------------------------------------------- 1 | 12 2 | >>> 2 3 | 14 4 | >>> 2 5 | 1969 6 | >>> 654 7 | 100756 8 | >>> 33583 9 | -------------------------------------------------------------------------------- /test-data/2019/01b.txt: -------------------------------------------------------------------------------- 1 | 14 2 | >>> 2 3 | 1969 4 | >>> 966 5 | 100756 6 | >>> 50346 7 | -------------------------------------------------------------------------------- /test-data/2019/02a.txt: -------------------------------------------------------------------------------- 1 | 1,9,10,3,2,3,11,0,99,30,40,50 2 | >>>noun:9:int 3 | >>>verb:10:int 4 | >>> 3500 5 | 1,0,0,0,99 6 | >>>noun:0:int 7 | >>>verb:0:int 8 | >>> 2 9 | 2,3,0,3,99 10 | >>>noun:3:int 11 | >>>verb:0:int 12 | >>> 2 13 | 2,4,4,5,99,0 14 | >>>noun:4:int 15 | >>>verb:4:int 16 | >>> 2 17 | 1,1,1,4,99,5,6,0,99 18 | >>>noun:1:int 19 | >>>verb:1:int 20 | >>> 30 21 | -------------------------------------------------------------------------------- /test-data/2019/03a.txt: -------------------------------------------------------------------------------- 1 | R8,U5,L5,D3 2 | U7,R6,D4,L4 3 | >>> 6 4 | R75,D30,R83,U83,L12,D49,R71,U7,L72 5 | U62,R66,U55,R34,D71,R55,D58,R83 6 | >>> 159 7 | R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 8 | U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 9 | >>> 135 10 | -------------------------------------------------------------------------------- /test-data/2019/03b.txt: -------------------------------------------------------------------------------- 1 | R8,U5,L5,D3 2 | U7,R6,D4,L4 3 | >>> 30 4 | R75,D30,R83,U83,L12,D49,R71,U7,L72 5 | U62,R66,U55,R34,D71,R55,D58,R83 6 | >>> 610 7 | R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 8 | U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 9 | >>> 410 10 | -------------------------------------------------------------------------------- /test-data/2019/04a.txt: -------------------------------------------------------------------------------- 1 | 111111-111111 2 | >>> 1 3 | 223450-223450 4 | >>> 0 5 | 123789-123789 6 | >>> 0 7 | -------------------------------------------------------------------------------- /test-data/2019/04b.txt: -------------------------------------------------------------------------------- 1 | 112233-112233 2 | >>> 1 3 | 123444-123444 4 | >>> 0 5 | 111122-111122 6 | >>> 1 7 | -------------------------------------------------------------------------------- /test-data/2019/05a.txt: -------------------------------------------------------------------------------- 1 | 3,0,4,0,99 2 | >>> 1 3 | -------------------------------------------------------------------------------- /test-data/2019/06a.txt: -------------------------------------------------------------------------------- 1 | COM)B 2 | B)C 3 | C)D 4 | D)E 5 | E)F 6 | B)G 7 | G)H 8 | D)I 9 | E)J 10 | J)K 11 | K)L 12 | >>> 42 13 | -------------------------------------------------------------------------------- /test-data/2019/06b.txt: -------------------------------------------------------------------------------- 1 | COM)B 2 | B)C 3 | C)D 4 | D)E 5 | E)F 6 | B)G 7 | G)H 8 | D)I 9 | E)J 10 | J)K 11 | K)L 12 | K)YOU 13 | I)SAN 14 | >>> 4 15 | -------------------------------------------------------------------------------- /test-data/2019/07a.txt: -------------------------------------------------------------------------------- 1 | 3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0 2 | >>> 43210 3 | 3,23,3,24,1002,24,10,24,1002,23,-1,23, 101,5,23,23,1,24,23,23,4,23,99,0,0 4 | >>> 54321 5 | 3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33, 1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0 6 | >>> 65210 7 | -------------------------------------------------------------------------------- /test-data/2019/07b.txt: -------------------------------------------------------------------------------- 1 | 3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5 2 | >>> 139629729 3 | 3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10 4 | >>> 18216 5 | -------------------------------------------------------------------------------- /test-data/2019/08a.txt: -------------------------------------------------------------------------------- 1 | 123456789012 2 | >>>w:3:int 3 | >>>h:2:int 4 | >>> 1 5 | -------------------------------------------------------------------------------- /test-data/2019/09a.txt: -------------------------------------------------------------------------------- 1 | 109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99 2 | >>> 109 3 | 1102,34915192,34915192,7,4,7,99,0 4 | >>> 1219070632396864 5 | 104,1125899906842624,99 6 | >>> 1125899906842624 7 | -------------------------------------------------------------------------------- /test-data/2019/10a.txt: -------------------------------------------------------------------------------- 1 | .#..# 2 | ..... 3 | ##### 4 | ....# 5 | ...## 6 | >>> 8 7 | ......#.#. 8 | #..#.#.... 9 | ..#######. 10 | .#.#.###.. 11 | .#..#..... 12 | ..#....#.# 13 | #..#....#. 14 | .##.#..### 15 | ##...#..#. 16 | .#....#### 17 | >>> 33 18 | #.#...#.#. 19 | .###....#. 20 | .#....#... 21 | ##.#.#.#.# 22 | ....#.#.#. 23 | .##..###.# 24 | ..#...##.. 25 | ..##....## 26 | ......#... 27 | .####.###. 28 | >>> 35 29 | .#..#..### 30 | ####.###.# 31 | ....###.#. 32 | ..###.##.# 33 | ##.##.#.#. 34 | ....###..# 35 | ..#.#..#.# 36 | #..#.#.### 37 | .##...##.# 38 | .....#.#.. 39 | >>> 41 40 | .#..##.###...####### 41 | ##.############..##. 42 | .#.######.########.# 43 | .###.#######.####.#. 44 | #####.##.#.##.###.## 45 | ..#####..#.######### 46 | #################### 47 | #.####....###.#.#.## 48 | ##.################# 49 | #####.##.###..####.. 50 | ..######..##.####### 51 | ####.##.####...##..# 52 | .#####..#.######.### 53 | ##...#.##########... 54 | #.##########.####### 55 | .####.#.###.###.#.## 56 | ....##.##.###..##### 57 | .#.#.###########.### 58 | #.#.#.#####.####.### 59 | ###.##.####.##.#..## 60 | >>> 210 61 | -------------------------------------------------------------------------------- /test-data/2019/10b.txt: -------------------------------------------------------------------------------- 1 | .#..##.###...####### 2 | ##.############..##. 3 | .#.######.########.# 4 | .###.#######.####.#. 5 | #####.##.#.##.###.## 6 | ..#####..#.######### 7 | #################### 8 | #.####....###.#.#.## 9 | ##.################# 10 | #####.##.###..####.. 11 | ..######..##.####### 12 | ####.##.####...##..# 13 | .#####..#.######.### 14 | ##...#.##########... 15 | #.##########.####### 16 | .####.#.###.###.#.## 17 | ....##.##.###..##### 18 | .#.#.###########.### 19 | #.#.#.#####.####.### 20 | ###.##.####.##.#..## 21 | >>> 802 22 | -------------------------------------------------------------------------------- /test-data/2019/12a.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | >>>steps:10:int 6 | >>> 179 7 | 8 | 9 | 10 | 11 | >>>steps:100:int 12 | >>> 1940 13 | -------------------------------------------------------------------------------- /test-data/2019/12b.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | >>> 2772 6 | 7 | 8 | 9 | 10 | >>> 4686774924 11 | -------------------------------------------------------------------------------- /test-data/2019/14a.txt: -------------------------------------------------------------------------------- 1 | 10 ORE => 10 A 2 | 1 ORE => 1 B 3 | 7 A, 1 B => 1 C 4 | 7 A, 1 C => 1 D 5 | 7 A, 1 D => 1 E 6 | 7 A, 1 E => 1 FUEL 7 | >>> 31 8 | 9 ORE => 2 A 9 | 8 ORE => 3 B 10 | 7 ORE => 5 C 11 | 3 A, 4 B => 1 AB 12 | 5 B, 7 C => 1 BC 13 | 4 C, 1 A => 1 CA 14 | 2 AB, 3 BC, 4 CA => 1 FUEL 15 | >>> 165 16 | 157 ORE => 5 NZVS 17 | 165 ORE => 6 DCFZ 18 | 44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL 19 | 12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ 20 | 179 ORE => 7 PSHF 21 | 177 ORE => 5 HKGWZ 22 | 7 DCFZ, 7 PSHF => 2 XJWVT 23 | 165 ORE => 2 GPVTF 24 | 3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT 25 | >>> 13312 26 | 2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG 27 | 17 NVRVD, 3 JNWZP => 8 VPVL 28 | 53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL 29 | 22 VJHF, 37 MNCFX => 5 FWMGM 30 | 139 ORE => 4 NVRVD 31 | 144 ORE => 7 JNWZP 32 | 5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC 33 | 5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV 34 | 145 ORE => 6 MNCFX 35 | 1 NVRVD => 8 CXFTF 36 | 1 VJHF, 6 MNCFX => 4 RFSQX 37 | 176 ORE => 6 VJHF 38 | >>> 180697 39 | 171 ORE => 8 CNZTR 40 | 7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL 41 | 114 ORE => 4 BHXH 42 | 14 VRPVC => 6 BMBT 43 | 6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL 44 | 6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT 45 | 15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW 46 | 13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW 47 | 5 BMBT => 4 WPTQ 48 | 189 ORE => 9 KTJDG 49 | 1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP 50 | 12 VRPVC, 27 CNZTR => 2 XDBXC 51 | 15 KTJDG, 12 BHXH => 5 XCVML 52 | 3 BHXH, 2 VRPVC => 7 MZWV 53 | 121 ORE => 7 VRPVC 54 | 7 XCVML => 6 RJRHP 55 | 5 BHXH, 4 VRPVC => 5 LTCX 56 | >>> 2210736 57 | -------------------------------------------------------------------------------- /test-data/2019/14b.txt: -------------------------------------------------------------------------------- 1 | 157 ORE => 5 NZVS 2 | 165 ORE => 6 DCFZ 3 | 44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL 4 | 12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ 5 | 179 ORE => 7 PSHF 6 | 177 ORE => 5 HKGWZ 7 | 7 DCFZ, 7 PSHF => 2 XJWVT 8 | 165 ORE => 2 GPVTF 9 | 3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT 10 | >>> 82892753 11 | 2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG 12 | 17 NVRVD, 3 JNWZP => 8 VPVL 13 | 53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL 14 | 22 VJHF, 37 MNCFX => 5 FWMGM 15 | 139 ORE => 4 NVRVD 16 | 144 ORE => 7 JNWZP 17 | 5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC 18 | 5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV 19 | 145 ORE => 6 MNCFX 20 | 1 NVRVD => 8 CXFTF 21 | 1 VJHF, 6 MNCFX => 4 RFSQX 22 | 176 ORE => 6 VJHF 23 | >>> 5586022 24 | 171 ORE => 8 CNZTR 25 | 7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL 26 | 114 ORE => 4 BHXH 27 | 14 VRPVC => 6 BMBT 28 | 6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL 29 | 6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT 30 | 15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW 31 | 13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW 32 | 5 BMBT => 4 WPTQ 33 | 189 ORE => 9 KTJDG 34 | 1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP 35 | 12 VRPVC, 27 CNZTR => 2 XDBXC 36 | 15 KTJDG, 12 BHXH => 5 XCVML 37 | 3 BHXH, 2 VRPVC => 7 MZWV 38 | 121 ORE => 7 VRPVC 39 | 7 XCVML => 6 RJRHP 40 | 5 BHXH, 4 VRPVC => 5 LTCX 41 | >>> 460664 42 | -------------------------------------------------------------------------------- /test-data/2019/16a.txt: -------------------------------------------------------------------------------- 1 | 80871224585914546619083218645595 2 | >>> 24176176 3 | 19617804207202209144916044189917 4 | >>> 73745418 5 | 69317163492948606335995924319873 6 | >>> 52432133 7 | -------------------------------------------------------------------------------- /test-data/2019/16b.txt: -------------------------------------------------------------------------------- 1 | 03036732577212944063491565474664 2 | >>> 84462026 3 | 02935109699940807407585447034323 4 | >>> 78725270 5 | 03081770884921959731165446850517 6 | >>> 53553731 7 | -------------------------------------------------------------------------------- /test-data/2019/18a.txt: -------------------------------------------------------------------------------- 1 | ######### 2 | #b.A.@.a# 3 | ######### 4 | >>> 8 5 | ######################## 6 | #f.D.E.e.C.b.A.@.a.B.c.# 7 | ######################.# 8 | #d.....................# 9 | ######################## 10 | >>> 86 11 | ######################## 12 | #...............b.C.D.f# 13 | #.###################### 14 | #.....@.a.B.c.d.A.e.F.g# 15 | ######################## 16 | >>> 132 17 | ################# 18 | #i.G..c...e..H.p# 19 | ########.######## 20 | #j.A..b...f..D.o# 21 | ########@######## 22 | #k.E..a...g..B.n# 23 | ########.######## 24 | #l.F..d...h..C.m# 25 | ################# 26 | >>> 136 27 | ######################## 28 | #@..............ac.GI.b# 29 | ###d#e#f################ 30 | ###A#B#C################ 31 | ###g#h#i################ 32 | ######################## 33 | >>> 81 34 | -------------------------------------------------------------------------------- /test-data/2019/18b.txt: -------------------------------------------------------------------------------- 1 | ############### 2 | #d.ABC.#.....a# 3 | ######...###### 4 | ######.@.###### 5 | ######...###### 6 | #b.....#.....c# 7 | ############### 8 | >>> 24 9 | ############# 10 | #DcBa.#.GhKl# 11 | #.###...#I### 12 | #e#d#.@.#j#k# 13 | ###C#...###J# 14 | #fEbA.#.FgHi# 15 | ############# 16 | >>> 32 17 | ############# 18 | #g#f.D#..h#l# 19 | #F###e#E###.# 20 | #dCba...BcIJ# 21 | #####.@.##### 22 | #nK.L...G...# 23 | #M###N#H###.# 24 | #o#m..#i#jk.# 25 | ############# 26 | >>> 72 27 | -------------------------------------------------------------------------------- /test-data/2019/20a.txt: -------------------------------------------------------------------------------- 1 | A 2 | A 3 | #######.######### 4 | #######.........# 5 | #######.#######.# 6 | #######.#######.# 7 | #######.#######.# 8 | ##### B ###.# 9 | BC...## C ###.# 10 | ##.## ###.# 11 | ##...DE F ###.# 12 | ##### G ###.# 13 | #########.#####.# 14 | DE..#######...###.# 15 | #.#########.###.# 16 | FG..#########.....# 17 | ###########.##### 18 | Z 19 | Z 20 | >>> 23 21 | A 22 | A 23 | #################.############# 24 | #.#...#...................#.#.# 25 | #.#.#.###.###.###.#########.#.# 26 | #.#.#.......#...#.....#.#.#...# 27 | #.#########.###.#####.#.#.###.# 28 | #.............#.#.....#.......# 29 | ###.###########.###.#####.#.#.# 30 | #.....# A C #.#.#.# 31 | ####### S P #####.# 32 | #.#...# #......VT 33 | #.#.#.# #.##### 34 | #...#.# YN....#.# 35 | #.###.# #####.# 36 | DI....#.# #.....# 37 | #####.# #.###.# 38 | ZZ......# QG....#..AS 39 | ###.### ####### 40 | JO..#.#.# #.....# 41 | #.#.#.# ###.#.# 42 | #...#..DI BU....#..LF 43 | #####.# #.##### 44 | YN......# VT..#....QG 45 | #.###.# #.###.# 46 | #.#...# #.....# 47 | ###.### J L J #.#.### 48 | #.....# O F P #.#...# 49 | #.###.#####.#.#####.#####.###.# 50 | #...#.#.#...#.....#.....#.#...# 51 | #.#####.###.###.#.#.#########.# 52 | #...#.#.....#...#.#.#.#.....#.# 53 | #.###.#####.###.###.#.#.####### 54 | #.#.........#...#.............# 55 | #########.###.###.############# 56 | B J C 57 | U P P 58 | >>> 58 59 | -------------------------------------------------------------------------------- /test-data/2019/20b.txt: -------------------------------------------------------------------------------- 1 | A 2 | A 3 | #######.######### 4 | #######.........# 5 | #######.#######.# 6 | #######.#######.# 7 | #######.#######.# 8 | ##### B ###.# 9 | BC...## C ###.# 10 | ##.## ###.# 11 | ##...DE F ###.# 12 | ##### G ###.# 13 | #########.#####.# 14 | DE..#######...###.# 15 | #.#########.###.# 16 | FG..#########.....# 17 | ###########.##### 18 | Z 19 | Z 20 | >>> 26 21 | Z L X W C 22 | Z P Q B K 23 | ###########.#.#.#.#######.############### 24 | #...#.......#.#.......#.#.......#.#.#...# 25 | ###.#.#.#.#.#.#.#.###.#.#.#######.#.#.### 26 | #.#...#.#.#...#.#.#...#...#...#.#.......# 27 | #.###.#######.###.###.#.###.###.#.####### 28 | #...#.......#.#...#...#.............#...# 29 | #.#########.#######.#.#######.#######.### 30 | #...#.# F R I Z #.#.#.# 31 | #.###.# D E C H #.#.#.# 32 | #.#...# #...#.# 33 | #.###.# #.###.# 34 | #.#....OA WB..#.#..ZH 35 | #.###.# #.#.#.# 36 | CJ......# #.....# 37 | ####### ####### 38 | #.#....CK #......IC 39 | #.###.# #.###.# 40 | #.....# #...#.# 41 | ###.### #.#.#.# 42 | XF....#.# RF..#.#.# 43 | #####.# ####### 44 | #......CJ NM..#...# 45 | ###.#.# #.###.# 46 | RE....#.# #......RF 47 | ###.### X X L #.#.#.# 48 | #.....# F Q P #.#.#.# 49 | ###.###########.###.#######.#########.### 50 | #.....#...#.....#.......#...#.....#.#...# 51 | #####.#.###.#######.#######.###.###.#.#.# 52 | #.......#.......#.#.#.#.#...#...#...#.#.# 53 | #####.###.#####.#.#.#.#.###.###.#.###.### 54 | #.......#.....#.#...#...............#...# 55 | #############.#.#.###.################### 56 | A O F N 57 | A A D M 58 | >>> 396 59 | -------------------------------------------------------------------------------- /test-data/2019/24b.txt: -------------------------------------------------------------------------------- 1 | ....# 2 | #..#. 3 | #.?## 4 | ..#.. 5 | #.... 6 | >>>steps:10:int 7 | >>> 99 8 | -------------------------------------------------------------------------------- /test-data/2020/01a.txt: -------------------------------------------------------------------------------- 1 | 1721 2 | 979 3 | 366 4 | 299 5 | 675 6 | 1456 7 | >>> 514579 8 | -------------------------------------------------------------------------------- /test-data/2020/01b.txt: -------------------------------------------------------------------------------- 1 | 1721 2 | 979 3 | 366 4 | 299 5 | 675 6 | 1456 7 | >>> 241861950 8 | -------------------------------------------------------------------------------- /test-data/2020/02a.txt: -------------------------------------------------------------------------------- 1 | 1-3 a: abcde 2 | >>> 1 3 | 1-3 b: cdefg 4 | >>> 0 5 | 2-9 c: ccccccccc 6 | >>> 1 7 | 1-3 a: abcde 8 | 1-3 b: cdefg 9 | 2-9 c: ccccccccc 10 | >>> 2 11 | -------------------------------------------------------------------------------- /test-data/2020/02b.txt: -------------------------------------------------------------------------------- 1 | 1-3 a: abcde 2 | >>> 1 3 | 1-3 b: cdefg 4 | >>> 0 5 | 2-9 c: ccccccccc 6 | >>> 0 7 | 1-3 a: abcde 8 | 1-3 b: cdefg 9 | 2-9 c: ccccccccc 10 | >>> 1 11 | -------------------------------------------------------------------------------- /test-data/2020/04a.txt: -------------------------------------------------------------------------------- 1 | ecl:gry pid:860033327 eyr:2020 hcl:#fffffd 2 | byr:1937 iyr:2017 cid:147 hgt:183cm 3 | >>> 1 4 | iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 5 | hcl:#cfa07d byr:1929 6 | >>> 0 7 | hcl:#ae17e1 iyr:2013 8 | eyr:2024 9 | ecl:brn pid:760753108 byr:1931 10 | hgt:179cm 11 | >>> 1 12 | hcl:#cfa07d eyr:2025 pid:166559648 13 | iyr:2011 ecl:brn hgt:59in 14 | >>> 0 15 | ecl:gry pid:860033327 eyr:2020 hcl:#fffffd 16 | byr:1937 iyr:2017 cid:147 hgt:183cm 17 | 18 | iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 19 | hcl:#cfa07d byr:1929 20 | 21 | hcl:#ae17e1 iyr:2013 22 | eyr:2024 23 | ecl:brn pid:760753108 byr:1931 24 | hgt:179cm 25 | 26 | hcl:#cfa07d eyr:2025 pid:166559648 27 | iyr:2011 ecl:brn hgt:59in 28 | >>> 2 29 | -------------------------------------------------------------------------------- /test-data/2020/04b.txt: -------------------------------------------------------------------------------- 1 | eyr:1972 cid:100 2 | hcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926 3 | 4 | iyr:2019 5 | hcl:#602927 eyr:1967 hgt:170cm 6 | ecl:grn pid:012533040 byr:1946 7 | 8 | hcl:dab227 iyr:2012 9 | ecl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277 10 | 11 | hgt:59cm ecl:zzz 12 | eyr:2038 hcl:74454a iyr:2023 13 | pid:3556412378 byr:2007 14 | >>> 0 15 | pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980 16 | hcl:#623a2f 17 | 18 | eyr:2029 ecl:blu cid:129 byr:1989 19 | iyr:2014 pid:896056539 hcl:#a97842 hgt:165cm 20 | 21 | hcl:#888785 22 | hgt:164cm byr:2001 iyr:2015 cid:88 23 | pid:545766238 ecl:hzl 24 | eyr:2022 25 | 26 | iyr:2010 hgt:158cm hcl:#b6652a ecl:blu byr:1944 eyr:2021 pid:093154719 27 | >>> 4 28 | -------------------------------------------------------------------------------- /test-data/2020/05a.txt: -------------------------------------------------------------------------------- 1 | FBFBBFFRLR 2 | >>> 357 3 | BFFFBBFRRR 4 | >>> 567 5 | FFFBBBFRRR 6 | >>> 119 7 | BBFFBBFRLL 8 | >>> 820 9 | FBFBBFFRLR 10 | BFFFBBFRRR 11 | FFFBBBFRRR 12 | BBFFBBFRLL 13 | >>> 820 14 | -------------------------------------------------------------------------------- /test-data/2020/07a.txt: -------------------------------------------------------------------------------- 1 | light red bags contain 1 bright white bag, 2 muted yellow bags. 2 | dark orange bags contain 3 bright white bags, 4 muted yellow bags. 3 | bright white bags contain 1 shiny gold bag. 4 | muted yellow bags contain 2 shiny gold bags, 9 faded blue bags. 5 | shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags. 6 | dark olive bags contain 3 faded blue bags, 4 dotted black bags. 7 | vibrant plum bags contain 5 faded blue bags, 6 dotted black bags. 8 | faded blue bags contain no other bags. 9 | dotted black bags contain no other bags. 10 | >>> 4 11 | -------------------------------------------------------------------------------- /test-data/2020/07b.txt: -------------------------------------------------------------------------------- 1 | light red bags contain 1 bright white bag, 2 muted yellow bags. 2 | dark orange bags contain 3 bright white bags, 4 muted yellow bags. 3 | bright white bags contain 1 shiny gold bag. 4 | muted yellow bags contain 2 shiny gold bags, 9 faded blue bags. 5 | shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags. 6 | dark olive bags contain 3 faded blue bags, 4 dotted black bags. 7 | vibrant plum bags contain 5 faded blue bags, 6 dotted black bags. 8 | faded blue bags contain no other bags. 9 | dotted black bags contain no other bags. 10 | >>> 32 11 | shiny gold bags contain 2 dark red bags. 12 | dark red bags contain 2 dark orange bags. 13 | dark orange bags contain 2 dark yellow bags. 14 | dark yellow bags contain 2 dark green bags. 15 | dark green bags contain 2 dark blue bags. 16 | dark blue bags contain 2 dark violet bags. 17 | dark violet bags contain no other bags. 18 | >>> 126 19 | -------------------------------------------------------------------------------- /test-data/2020/09a.txt: -------------------------------------------------------------------------------- 1 | 35 2 | 20 3 | 15 4 | 25 5 | 47 6 | 40 7 | 62 8 | 55 9 | 65 10 | 95 11 | 102 12 | 117 13 | 150 14 | 182 15 | 127 16 | 219 17 | 299 18 | 277 19 | 309 20 | 576 21 | >>>window:5:int 22 | >>> 127 23 | -------------------------------------------------------------------------------- /test-data/2020/09b.txt: -------------------------------------------------------------------------------- 1 | 35 2 | 20 3 | 15 4 | 25 5 | 47 6 | 40 7 | 62 8 | 55 9 | 65 10 | 95 11 | 102 12 | 117 13 | 150 14 | 182 15 | 127 16 | 219 17 | 299 18 | 277 19 | 309 20 | 576 21 | >>>window:5:int 22 | >>> 62 23 | -------------------------------------------------------------------------------- /test-data/2020/10a.txt: -------------------------------------------------------------------------------- 1 | 16 2 | 10 3 | 15 4 | 5 5 | 1 6 | 11 7 | 7 8 | 19 9 | 6 10 | 12 11 | 4 12 | >>> 35 13 | 28 14 | 33 15 | 18 16 | 42 17 | 31 18 | 14 19 | 46 20 | 20 21 | 48 22 | 47 23 | 24 24 | 23 25 | 49 26 | 45 27 | 19 28 | 38 29 | 39 30 | 11 31 | 1 32 | 32 33 | 25 34 | 35 35 | 8 36 | 17 37 | 7 38 | 9 39 | 4 40 | 2 41 | 34 42 | 10 43 | 3 44 | >>> 220 45 | -------------------------------------------------------------------------------- /test-data/2020/10b.txt: -------------------------------------------------------------------------------- 1 | 16 2 | 10 3 | 15 4 | 5 5 | 1 6 | 11 7 | 7 8 | 19 9 | 6 10 | 12 11 | 4 12 | >>> 8 13 | 28 14 | 33 15 | 18 16 | 42 17 | 31 18 | 14 19 | 46 20 | 20 21 | 48 22 | 47 23 | 24 24 | 23 25 | 49 26 | 45 27 | 19 28 | 38 29 | 39 30 | 11 31 | 1 32 | 32 33 | 25 34 | 35 35 | 8 36 | 17 37 | 7 38 | 9 39 | 4 40 | 2 41 | 34 42 | 10 43 | 3 44 | >>> 19208 45 | 3 46 | 4 47 | 5 48 | 6 49 | >>> 4 50 | 1 51 | 2 52 | 3 53 | 4 54 | 5 55 | 6 56 | 7 57 | 8 58 | 9 59 | 10 60 | >>> 274 61 | 1 62 | 2 63 | 3 64 | 4 65 | 5 66 | 6 67 | 7 68 | 8 69 | 9 70 | 10 71 | 11 72 | 12 73 | 13 74 | 14 75 | 15 76 | >>> 5768 77 | 1 78 | 2 79 | 3 80 | 4 81 | 5 82 | 6 83 | 7 84 | 8 85 | 9 86 | 10 87 | 11 88 | 12 89 | 13 90 | 14 91 | 15 92 | 16 93 | 17 94 | 18 95 | 19 96 | 20 97 | >>> 121415 98 | -------------------------------------------------------------------------------- /test-data/2020/11a.txt: -------------------------------------------------------------------------------- 1 | L.LL.LL.LL 2 | LLLLLLL.LL 3 | L.L.L..L.. 4 | LLLL.LL.LL 5 | L.LL.LL.LL 6 | L.LLLLL.LL 7 | ..L.L..... 8 | LLLLLLLLLL 9 | L.LLLLLL.L 10 | L.LLLLL.LL 11 | >>> 37 12 | -------------------------------------------------------------------------------- /test-data/2020/11b.txt: -------------------------------------------------------------------------------- 1 | L.LL.LL.LL 2 | LLLLLLL.LL 3 | L.L.L..L.. 4 | LLLL.LL.LL 5 | L.LL.LL.LL 6 | L.LLLLL.LL 7 | ..L.L..... 8 | LLLLLLLLLL 9 | L.LLLLLL.L 10 | L.LLLLL.LL 11 | >>> 26 12 | -------------------------------------------------------------------------------- /test-data/2020/12a.txt: -------------------------------------------------------------------------------- 1 | F10 2 | N3 3 | F7 4 | R90 5 | F11 6 | >>> 25 7 | -------------------------------------------------------------------------------- /test-data/2020/12b.txt: -------------------------------------------------------------------------------- 1 | F10 2 | N3 3 | F7 4 | R90 5 | F11 6 | >>> 286 7 | -------------------------------------------------------------------------------- /test-data/2020/13a.txt: -------------------------------------------------------------------------------- 1 | 939 2 | 7,13,x,x,59,x,31,19 3 | >>> 295 4 | -------------------------------------------------------------------------------- /test-data/2020/13b.txt: -------------------------------------------------------------------------------- 1 | 939 2 | 7,13,x,x,59,x,31,19 3 | >>> 1068781 4 | 1 5 | 17,x,13,19 6 | >>> 3417 7 | 1 8 | 67,7,59,61 9 | >>> 754018 10 | 1 11 | 67,x,7,59,61 12 | >>> 779210 13 | 1 14 | 67,7,x,59,61 15 | >>> 1261476 16 | 1 17 | 1789,37,47,1889 18 | >>> 1202161486 19 | -------------------------------------------------------------------------------- /test-data/2020/14a.txt: -------------------------------------------------------------------------------- 1 | mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X 2 | mem[8] = 11 3 | mem[7] = 101 4 | mem[8] = 0 5 | >>> 165 6 | -------------------------------------------------------------------------------- /test-data/2020/14b.txt: -------------------------------------------------------------------------------- 1 | mask = 000000000000000000000000000000X1001X 2 | mem[42] = 100 3 | mask = 00000000000000000000000000000000X0XX 4 | mem[26] = 1 5 | >>> 208 6 | -------------------------------------------------------------------------------- /test-data/2020/15a.txt: -------------------------------------------------------------------------------- 1 | 0,3,6 2 | >>> 436 3 | 1,3,2 4 | >>> 1 5 | 2,1,3 6 | >>> 10 7 | 1,2,3 8 | >>> 27 9 | 2,3,1 10 | >>> 78 11 | 3,2,1 12 | >>> 438 13 | 3,1,2 14 | >>> 1836 15 | -------------------------------------------------------------------------------- /test-data/2020/15b.txt: -------------------------------------------------------------------------------- 1 | 0,3,6 2 | >>> 175594 3 | 1,3,2 4 | >>> 2578 5 | 2,1,3 6 | >>> 3544142 7 | 1,2,3 8 | >>> 261214 9 | 2,3,1 10 | >>> 6895259 11 | 3,2,1 12 | >>> 18 13 | 3,1,2 14 | >>> 362 15 | -------------------------------------------------------------------------------- /test-data/2020/16a.txt: -------------------------------------------------------------------------------- 1 | class: 1-3 or 5-7 2 | row: 6-11 or 33-44 3 | seat: 13-40 or 45-50 4 | 5 | your ticket: 6 | 7,1,14 7 | 8 | nearby tickets: 9 | 7,3,47 10 | 40,4,50 11 | 55,2,20 12 | 38,6,12 13 | >>> 71 14 | -------------------------------------------------------------------------------- /test-data/2020/16b.txt: -------------------------------------------------------------------------------- 1 | class: 0-1 or 4-19 2 | row: 0-5 or 8-19 3 | seat: 0-13 or 16-19 4 | 5 | your ticket: 6 | 11,12,13 7 | 8 | nearby tickets: 9 | 3,9,18 10 | 15,1,5 11 | 5,14,9 12 | >>>prefix:seat:text 13 | >>> 13 14 | -------------------------------------------------------------------------------- /test-data/2020/17a.txt: -------------------------------------------------------------------------------- 1 | .#. 2 | ..# 3 | ### 4 | >>> 112 5 | -------------------------------------------------------------------------------- /test-data/2020/17b.txt: -------------------------------------------------------------------------------- 1 | .#. 2 | ..# 3 | ### 4 | >>> 848 5 | -------------------------------------------------------------------------------- /test-data/2020/18a.txt: -------------------------------------------------------------------------------- 1 | 1 + 2 * 3 + 4 * 5 + 6 2 | >>> 71 3 | 1 + (2 * 3) + (4 * (5 + 6)) 4 | >>> 51 5 | 2 * 3 + (4 * 5) 6 | >>> 26 7 | 5 + (8 * 3 + 9 + 3 * 4 * 3) 8 | >>> 437 9 | 5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4)) 10 | >>> 12240 11 | ((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2 12 | >>> 13632 13 | -------------------------------------------------------------------------------- /test-data/2020/18b.txt: -------------------------------------------------------------------------------- 1 | 1 + 2 * 3 + 4 * 5 + 6 2 | >>> 231 3 | 1 + (2 * 3) + (4 * (5 + 6)) 4 | >>> 51 5 | 2 * 3 + (4 * 5) 6 | >>> 46 7 | 5 + (8 * 3 + 9 + 3 * 4 * 3) 8 | >>> 1445 9 | 5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4)) 10 | >>> 669060 11 | ((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2 12 | >>> 23340 13 | -------------------------------------------------------------------------------- /test-data/2020/19a.txt: -------------------------------------------------------------------------------- 1 | 0: 4 1 5 2 | 1: 2 3 | 3 2 3 | 2: 4 4 | 5 5 4 | 3: 4 5 | 5 4 5 | 4: "a" 6 | 5: "b" 7 | 8 | ababbb 9 | bababa 10 | abbbab 11 | aaabbb 12 | aaaabbb 13 | >>> 2 14 | -------------------------------------------------------------------------------- /test-data/2020/19b.txt: -------------------------------------------------------------------------------- 1 | 42: 9 14 | 10 1 2 | 9: 14 27 | 1 26 3 | 10: 23 14 | 28 1 4 | 1: "a" 5 | 11: 42 31 6 | 5: 1 14 | 15 1 7 | 19: 14 1 | 14 14 8 | 12: 24 14 | 19 1 9 | 16: 15 1 | 14 14 10 | 31: 14 17 | 1 13 11 | 6: 14 14 | 1 14 12 | 2: 1 24 | 14 4 13 | 0: 8 11 14 | 13: 14 3 | 1 12 15 | 15: 1 | 14 16 | 17: 14 2 | 1 7 17 | 23: 25 1 | 22 14 18 | 28: 16 1 19 | 4: 1 1 20 | 20: 14 14 | 1 15 21 | 3: 5 14 | 16 1 22 | 27: 1 6 | 14 18 23 | 14: "b" 24 | 21: 14 1 | 1 14 25 | 25: 1 1 | 1 14 26 | 22: 14 14 27 | 8: 42 28 | 26: 14 22 | 1 20 29 | 18: 15 15 30 | 7: 14 5 | 1 21 31 | 24: 14 1 32 | 33 | abbbbbabbbaaaababbaabbbbabababbbabbbbbbabaaaa 34 | bbabbbbaabaabba 35 | babbbbaabbbbbabbbbbbaabaaabaaa 36 | aaabbbbbbaaaabaababaabababbabaaabbababababaaa 37 | bbbbbbbaaaabbbbaaabbabaaa 38 | bbbababbbbaaaaaaaabbababaaababaabab 39 | ababaaaaaabaaab 40 | ababaaaaabbbaba 41 | baabbaaaabbaaaababbaababb 42 | abbbbabbbbaaaababbbbbbaaaababb 43 | aaaaabbaabaaaaababaa 44 | aaaabbaaaabbaaa 45 | aaaabbaabbaaaaaaabbbabbbaaabbaabaaa 46 | babaaabbbaaabaababbaabababaaab 47 | aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba 48 | >>> 12 49 | -------------------------------------------------------------------------------- /test-data/2020/20a.txt: -------------------------------------------------------------------------------- 1 | Tile 2311: 2 | ..##.#..#. 3 | ##..#..... 4 | #...##..#. 5 | ####.#...# 6 | ##.##.###. 7 | ##...#.### 8 | .#.#.#..## 9 | ..#....#.. 10 | ###...#.#. 11 | ..###..### 12 | 13 | Tile 1951: 14 | #.##...##. 15 | #.####...# 16 | .....#..## 17 | #...###### 18 | .##.#....# 19 | .###.##### 20 | ###.##.##. 21 | .###....#. 22 | ..#.#..#.# 23 | #...##.#.. 24 | 25 | Tile 1171: 26 | ####...##. 27 | #..##.#..# 28 | ##.#..#.#. 29 | .###.####. 30 | ..###.#### 31 | .##....##. 32 | .#...####. 33 | #.##.####. 34 | ####..#... 35 | .....##... 36 | 37 | Tile 1427: 38 | ###.##.#.. 39 | .#..#.##.. 40 | .#.##.#..# 41 | #.#.#.##.# 42 | ....#...## 43 | ...##..##. 44 | ...#.##### 45 | .#.####.#. 46 | ..#..###.# 47 | ..##.#..#. 48 | 49 | Tile 1489: 50 | ##.#.#.... 51 | ..##...#.. 52 | .##..##... 53 | ..#...#... 54 | #####...#. 55 | #..#.#.#.# 56 | ...#.#.#.. 57 | ##.#...##. 58 | ..##.##.## 59 | ###.##.#.. 60 | 61 | Tile 2473: 62 | #....####. 63 | #..#.##... 64 | #.##..#... 65 | ######.#.# 66 | .#...#.#.# 67 | .######### 68 | .###.#..#. 69 | ########.# 70 | ##...##.#. 71 | ..###.#.#. 72 | 73 | Tile 2971: 74 | ..#.#....# 75 | #...###... 76 | #.#.###... 77 | ##.##..#.. 78 | .#####..## 79 | .#..####.# 80 | #..#.#..#. 81 | ..####.### 82 | ..#.#.###. 83 | ...#.#.#.# 84 | 85 | Tile 2729: 86 | ...#.#.#.# 87 | ####.#.... 88 | ..#.#..... 89 | ....#..#.# 90 | .##..##.#. 91 | .#.####... 92 | ####.#.#.. 93 | ##.####... 94 | ##..#.##.. 95 | #.##...##. 96 | 97 | Tile 3079: 98 | #.#.#####. 99 | .#..###### 100 | ..#....... 101 | ######.... 102 | ####.#..#. 103 | .#...#.##. 104 | #.#####.## 105 | ..#.###... 106 | ..#....... 107 | ..#.###... 108 | >>> 20899048083289 109 | -------------------------------------------------------------------------------- /test-data/2020/20b.txt: -------------------------------------------------------------------------------- 1 | Tile 2311: 2 | ..##.#..#. 3 | ##..#..... 4 | #...##..#. 5 | ####.#...# 6 | ##.##.###. 7 | ##...#.### 8 | .#.#.#..## 9 | ..#....#.. 10 | ###...#.#. 11 | ..###..### 12 | 13 | Tile 1951: 14 | #.##...##. 15 | #.####...# 16 | .....#..## 17 | #...###### 18 | .##.#....# 19 | .###.##### 20 | ###.##.##. 21 | .###....#. 22 | ..#.#..#.# 23 | #...##.#.. 24 | 25 | Tile 1171: 26 | ####...##. 27 | #..##.#..# 28 | ##.#..#.#. 29 | .###.####. 30 | ..###.#### 31 | .##....##. 32 | .#...####. 33 | #.##.####. 34 | ####..#... 35 | .....##... 36 | 37 | Tile 1427: 38 | ###.##.#.. 39 | .#..#.##.. 40 | .#.##.#..# 41 | #.#.#.##.# 42 | ....#...## 43 | ...##..##. 44 | ...#.##### 45 | .#.####.#. 46 | ..#..###.# 47 | ..##.#..#. 48 | 49 | Tile 1489: 50 | ##.#.#.... 51 | ..##...#.. 52 | .##..##... 53 | ..#...#... 54 | #####...#. 55 | #..#.#.#.# 56 | ...#.#.#.. 57 | ##.#...##. 58 | ..##.##.## 59 | ###.##.#.. 60 | 61 | Tile 2473: 62 | #....####. 63 | #..#.##... 64 | #.##..#... 65 | ######.#.# 66 | .#...#.#.# 67 | .######### 68 | .###.#..#. 69 | ########.# 70 | ##...##.#. 71 | ..###.#.#. 72 | 73 | Tile 2971: 74 | ..#.#....# 75 | #...###... 76 | #.#.###... 77 | ##.##..#.. 78 | .#####..## 79 | .#..####.# 80 | #..#.#..#. 81 | ..####.### 82 | ..#.#.###. 83 | ...#.#.#.# 84 | 85 | Tile 2729: 86 | ...#.#.#.# 87 | ####.#.... 88 | ..#.#..... 89 | ....#..#.# 90 | .##..##.#. 91 | .#.####... 92 | ####.#.#.. 93 | ##.####... 94 | ##..#.##.. 95 | #.##...##. 96 | 97 | Tile 3079: 98 | #.#.#####. 99 | .#..###### 100 | ..#....... 101 | ######.... 102 | ####.#..#. 103 | .#...#.##. 104 | #.#####.## 105 | ..#.###... 106 | ..#....... 107 | ..#.###... 108 | >>> 273 109 | -------------------------------------------------------------------------------- /test-data/2020/21a.txt: -------------------------------------------------------------------------------- 1 | mxmxvkd kfcds sqjhc nhms (contains dairy, fish) 2 | trh fvjkl sbzzf mxmxvkd (contains dairy) 3 | sqjhc fvjkl (contains soy) 4 | sqjhc mxmxvkd sbzzf (contains fish) 5 | >>> 5 6 | -------------------------------------------------------------------------------- /test-data/2020/21b.txt: -------------------------------------------------------------------------------- 1 | mxmxvkd kfcds sqjhc nhms (contains dairy, fish) 2 | trh fvjkl sbzzf mxmxvkd (contains dairy) 3 | sqjhc fvjkl (contains soy) 4 | sqjhc mxmxvkd sbzzf (contains fish) 5 | >>> mxmxvkd,sqjhc,fvjkl 6 | -------------------------------------------------------------------------------- /test-data/2020/22a.txt: -------------------------------------------------------------------------------- 1 | Player 1: 2 | 9 3 | 2 4 | 6 5 | 3 6 | 1 7 | 8 | Player 2: 9 | 5 10 | 8 11 | 4 12 | 7 13 | 10 14 | >>> 306 15 | -------------------------------------------------------------------------------- /test-data/2020/22b.txt: -------------------------------------------------------------------------------- 1 | Player 1: 2 | 9 3 | 2 4 | 6 5 | 3 6 | 1 7 | 8 | Player 2: 9 | 5 10 | 8 11 | 4 12 | 7 13 | 10 14 | >>> 291 15 | -------------------------------------------------------------------------------- /test-data/2020/23a.txt: -------------------------------------------------------------------------------- 1 | 389125467 2 | >>> 67384529 3 | -------------------------------------------------------------------------------- /test-data/2020/23b.txt: -------------------------------------------------------------------------------- 1 | 389125467 2 | >>> 149245887792 3 | -------------------------------------------------------------------------------- /test-data/2020/24a.txt: -------------------------------------------------------------------------------- 1 | sesenwnenenewseeswwswswwnenewsewsw 2 | neeenesenwnwwswnenewnwwsewnenwseswesw 3 | seswneswswsenwwnwse 4 | nwnwneseeswswnenewneswwnewseswneseene 5 | swweswneswnenwsewnwneneseenw 6 | eesenwseswswnenwswnwnwsewwnwsene 7 | sewnenenenesenwsewnenwwwse 8 | wenwwweseeeweswwwnwwe 9 | wsweesenenewnwwnwsenewsenwwsesesenwne 10 | neeswseenwwswnwswswnw 11 | nenwswwsewswnenenewsenwsenwnesesenew 12 | enewnwewneswsewnwswenweswnenwsenwsw 13 | sweneswneswneneenwnewenewwneswswnese 14 | swwesenesewenwneswnwwneseswwne 15 | enesenwswwswneneswsenwnewswseenwsese 16 | wnwnesenesenenwwnenwsewesewsesesew 17 | nenewswnwewswnenesenwnesewesw 18 | eneswnwswnwsenenwnwnwwseeswneewsenese 19 | neswnwewnwnwseenwseesewsenwsweewe 20 | wseweeenwnesenwwwswnew 21 | >>> 10 22 | -------------------------------------------------------------------------------- /test-data/2020/24b.txt: -------------------------------------------------------------------------------- 1 | sesenwnenenewseeswwswswwnenewsewsw 2 | neeenesenwnwwswnenewnwwsewnenwseswesw 3 | seswneswswsenwwnwse 4 | nwnwneseeswswnenewneswwnewseswneseene 5 | swweswneswnenwsewnwneneseenw 6 | eesenwseswswnenwswnwnwsewwnwsene 7 | sewnenenenesenwsewnenwwwse 8 | wenwwweseeeweswwwnwwe 9 | wsweesenenewnwwnwsenewsenwwsesesenwne 10 | neeswseenwwswnwswswnw 11 | nenwswwsewswnenenewsenwsenwnesesenew 12 | enewnwewneswsewnwswenweswnenwsenwsw 13 | sweneswneswneneenwnewenewwneswswnese 14 | swwesenesewenwneswnwwneseswwne 15 | enesenwswwswneneswsenwnewswseenwsese 16 | wnwnesenesenenwwnenwsewesewsesesew 17 | nenewswnwewswnenesenwnesewesw 18 | eneswnwswnwsenenwnwnwwseeswneewsenese 19 | neswnwewnwnwseenwseesewsenwsweewe 20 | wseweeenwnesenwwwswnew 21 | >>> 2208 22 | -------------------------------------------------------------------------------- /test-data/2021/01a.txt: -------------------------------------------------------------------------------- 1 | 199 2 | 200 3 | 208 4 | 210 5 | 200 6 | 207 7 | 240 8 | 269 9 | 260 10 | 263 11 | >>> 7 12 | -------------------------------------------------------------------------------- /test-data/2021/01b.txt: -------------------------------------------------------------------------------- 1 | 199 2 | 200 3 | 208 4 | 210 5 | 200 6 | 207 7 | 240 8 | 269 9 | 260 10 | 263 11 | >>> 5 12 | -------------------------------------------------------------------------------- /test-data/2021/02a.txt: -------------------------------------------------------------------------------- 1 | forward 5 2 | down 5 3 | forward 8 4 | up 3 5 | down 8 6 | forward 2 7 | >>> 150 8 | -------------------------------------------------------------------------------- /test-data/2021/02b.txt: -------------------------------------------------------------------------------- 1 | forward 5 2 | down 5 3 | forward 8 4 | up 3 5 | down 8 6 | forward 2 7 | >>> 900 8 | -------------------------------------------------------------------------------- /test-data/2021/04a.txt: -------------------------------------------------------------------------------- 1 | 7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 2 | 3 | 22 13 17 11 0 4 | 8 2 23 4 24 5 | 21 9 14 16 7 6 | 6 10 3 18 5 7 | 1 12 20 15 19 8 | 9 | 3 15 0 2 22 10 | 9 18 13 17 5 11 | 19 8 7 25 23 12 | 20 11 10 24 4 13 | 14 21 16 12 6 14 | 15 | 14 21 17 24 4 16 | 10 16 15 9 19 17 | 18 8 23 26 20 18 | 22 11 13 6 5 19 | 2 0 12 3 7 20 | >>> 4512 21 | -------------------------------------------------------------------------------- /test-data/2021/04b.txt: -------------------------------------------------------------------------------- 1 | 7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 2 | 3 | 22 13 17 11 0 4 | 8 2 23 4 24 5 | 21 9 14 16 7 6 | 6 10 3 18 5 7 | 1 12 20 15 19 8 | 9 | 3 15 0 2 22 10 | 9 18 13 17 5 11 | 19 8 7 25 23 12 | 20 11 10 24 4 13 | 14 21 16 12 6 14 | 15 | 14 21 17 24 4 16 | 10 16 15 9 19 17 | 18 8 23 26 20 18 | 22 11 13 6 5 19 | 2 0 12 3 7 20 | >>> 1924 21 | -------------------------------------------------------------------------------- /test-data/2021/05a.txt: -------------------------------------------------------------------------------- 1 | 0,9 -> 5,9 2 | 8,0 -> 0,8 3 | 9,4 -> 3,4 4 | 2,2 -> 2,1 5 | 7,0 -> 7,4 6 | 6,4 -> 2,0 7 | 0,9 -> 2,9 8 | 3,4 -> 1,4 9 | 0,0 -> 8,8 10 | 5,5 -> 8,2 11 | >>> 5 12 | -------------------------------------------------------------------------------- /test-data/2021/05b.txt: -------------------------------------------------------------------------------- 1 | 0,9 -> 5,9 2 | 8,0 -> 0,8 3 | 9,4 -> 3,4 4 | 2,2 -> 2,1 5 | 7,0 -> 7,4 6 | 6,4 -> 2,0 7 | 0,9 -> 2,9 8 | 3,4 -> 1,4 9 | 0,0 -> 8,8 10 | 5,5 -> 8,2 11 | >>> 12 12 | -------------------------------------------------------------------------------- /test-data/2021/06a.txt: -------------------------------------------------------------------------------- 1 | 3,4,3,1,2 2 | >>> 5934 3 | -------------------------------------------------------------------------------- /test-data/2021/06b.txt: -------------------------------------------------------------------------------- 1 | 3,4,3,1,2 2 | >>> 26984457539 3 | -------------------------------------------------------------------------------- /test-data/2021/07a.txt: -------------------------------------------------------------------------------- 1 | 16,1,2,0,4,2,7,1,2,14 2 | >>> 37 3 | -------------------------------------------------------------------------------- /test-data/2021/07b.txt: -------------------------------------------------------------------------------- 1 | 16,1,2,0,4,2,7,1,2,14 2 | >>> 168 3 | -------------------------------------------------------------------------------- /test-data/2021/08a.txt: -------------------------------------------------------------------------------- 1 | be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe 2 | edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc 3 | fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg 4 | fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb 5 | aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea 6 | fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb 7 | dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe 8 | bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef 9 | egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb 10 | gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce 11 | >>> 26 12 | -------------------------------------------------------------------------------- /test-data/2021/08b.txt: -------------------------------------------------------------------------------- 1 | be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe 2 | edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc 3 | fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg 4 | fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb 5 | aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea 6 | fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb 7 | dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe 8 | bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef 9 | egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb 10 | gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce 11 | >>> 61229 12 | -------------------------------------------------------------------------------- /test-data/2021/09a.txt: -------------------------------------------------------------------------------- 1 | 2199943210 2 | 3987894921 3 | 9856789892 4 | 8767896789 5 | 9899965678 6 | >>> 15 7 | -------------------------------------------------------------------------------- /test-data/2021/09b.txt: -------------------------------------------------------------------------------- 1 | 2199943210 2 | 3987894921 3 | 9856789892 4 | 8767896789 5 | 9899965678 6 | >>> 1134 7 | -------------------------------------------------------------------------------- /test-data/2021/10a.txt: -------------------------------------------------------------------------------- 1 | [({(<(())[]>[[{[]{<()<>> 2 | [(()[<>])]({[<{<<[]>>( 3 | {([(<{}[<>[]}>{[]{[(<()> 4 | (((({<>}<{<{<>}{[]{[]{} 5 | [[<[([]))<([[{}[[()]]] 6 | [{[{({}]{}}([{[{{{}}([] 7 | {<[[]]>}<{[{[{[]{()[[[] 8 | [<(<(<(<{}))><([]([]() 9 | <{([([[(<>()){}]>(<<{{ 10 | <{([{{}}[<[[[<>{}]]]>[]] 11 | >>> 26397 12 | -------------------------------------------------------------------------------- /test-data/2021/10b.txt: -------------------------------------------------------------------------------- 1 | [({(<(())[]>[[{[]{<()<>> 2 | [(()[<>])]({[<{<<[]>>( 3 | {([(<{}[<>[]}>{[]{[(<()> 4 | (((({<>}<{<{<>}{[]{[]{} 5 | [[<[([]))<([[{}[[()]]] 6 | [{[{({}]{}}([{[{{{}}([] 7 | {<[[]]>}<{[{[{[]{()[[[] 8 | [<(<(<(<{}))><([]([]() 9 | <{([([[(<>()){}]>(<<{{ 10 | <{([{{}}[<[[[<>{}]]]>[]] 11 | >>> 288957 12 | -------------------------------------------------------------------------------- /test-data/2021/11a.txt: -------------------------------------------------------------------------------- 1 | 11111 2 | 19991 3 | 19191 4 | 19991 5 | 11111 6 | >>> 259 7 | 5483143223 8 | 2745854711 9 | 5264556173 10 | 6141336146 11 | 6357385478 12 | 4167524645 13 | 2176841721 14 | 6882881134 15 | 4846848554 16 | 5283751526 17 | >>> 1656 18 | -------------------------------------------------------------------------------- /test-data/2021/11b.txt: -------------------------------------------------------------------------------- 1 | 5483143223 2 | 2745854711 3 | 5264556173 4 | 6141336146 5 | 6357385478 6 | 4167524645 7 | 2176841721 8 | 6882881134 9 | 4846848554 10 | 5283751526 11 | >>> 195 12 | -------------------------------------------------------------------------------- /test-data/2021/12a.txt: -------------------------------------------------------------------------------- 1 | start-A 2 | start-b 3 | A-c 4 | A-b 5 | b-d 6 | A-end 7 | b-end 8 | >>> 10 9 | dc-end 10 | HN-start 11 | start-kj 12 | dc-start 13 | dc-HN 14 | LN-dc 15 | HN-end 16 | kj-sa 17 | kj-HN 18 | kj-dc 19 | >>> 19 20 | fs-end 21 | he-DX 22 | fs-he 23 | start-DX 24 | pj-DX 25 | end-zg 26 | zg-sl 27 | zg-pj 28 | pj-he 29 | RW-he 30 | fs-DX 31 | pj-RW 32 | zg-RW 33 | start-pj 34 | he-WI 35 | zg-he 36 | pj-fs 37 | start-RW 38 | >>> 226 39 | -------------------------------------------------------------------------------- /test-data/2021/12b.txt: -------------------------------------------------------------------------------- 1 | start-A 2 | start-b 3 | A-c 4 | A-b 5 | b-d 6 | A-end 7 | b-end 8 | >>> 36 9 | dc-end 10 | HN-start 11 | start-kj 12 | dc-start 13 | dc-HN 14 | LN-dc 15 | HN-end 16 | kj-sa 17 | kj-HN 18 | kj-dc 19 | >>> 103 20 | fs-end 21 | he-DX 22 | fs-he 23 | start-DX 24 | pj-DX 25 | end-zg 26 | zg-sl 27 | zg-pj 28 | pj-he 29 | RW-he 30 | fs-DX 31 | pj-RW 32 | zg-RW 33 | start-pj 34 | he-WI 35 | zg-he 36 | pj-fs 37 | start-RW 38 | >>> 3509 39 | -------------------------------------------------------------------------------- /test-data/2021/13a.txt: -------------------------------------------------------------------------------- 1 | 6,10 2 | 0,14 3 | 9,10 4 | 0,3 5 | 10,4 6 | 4,11 7 | 6,0 8 | 6,12 9 | 4,1 10 | 0,13 11 | 10,12 12 | 3,4 13 | 3,0 14 | 8,4 15 | 1,10 16 | 2,14 17 | 8,10 18 | 9,0 19 | 20 | fold along y=7 21 | fold along x=5 22 | >>> 17 23 | -------------------------------------------------------------------------------- /test-data/2021/14a.txt: -------------------------------------------------------------------------------- 1 | NNCB 2 | 3 | CH -> B 4 | HH -> N 5 | CB -> H 6 | NH -> C 7 | HB -> C 8 | HC -> B 9 | HN -> C 10 | NN -> C 11 | BH -> H 12 | NC -> B 13 | NB -> B 14 | BN -> B 15 | BB -> N 16 | BC -> B 17 | CC -> N 18 | CN -> C 19 | >>> 1588 20 | -------------------------------------------------------------------------------- /test-data/2021/14b.txt: -------------------------------------------------------------------------------- 1 | NNCB 2 | 3 | CH -> B 4 | HH -> N 5 | CB -> H 6 | NH -> C 7 | HB -> C 8 | HC -> B 9 | HN -> C 10 | NN -> C 11 | BH -> H 12 | NC -> B 13 | NB -> B 14 | BN -> B 15 | BB -> N 16 | BC -> B 17 | CC -> N 18 | CN -> C 19 | >>> 2188189693529 20 | -------------------------------------------------------------------------------- /test-data/2021/15a.txt: -------------------------------------------------------------------------------- 1 | 1163751742 2 | 1381373672 3 | 2136511328 4 | 3694931569 5 | 7463417111 6 | 1319128137 7 | 1359912421 8 | 3125421639 9 | 1293138521 10 | 2311944581 11 | >>> 40 12 | -------------------------------------------------------------------------------- /test-data/2021/15b.txt: -------------------------------------------------------------------------------- 1 | 1163751742 2 | 1381373672 3 | 2136511328 4 | 3694931569 5 | 7463417111 6 | 1319128137 7 | 1359912421 8 | 3125421639 9 | 1293138521 10 | 2311944581 11 | >>> 315 12 | -------------------------------------------------------------------------------- /test-data/2021/16a.txt: -------------------------------------------------------------------------------- 1 | 8A004A801A8002F478 2 | >>> 16 3 | 620080001611562C8802118E34 4 | >>> 12 5 | C0015000016115A2E0802F182340 6 | >>> 23 7 | A0016C880162017C3686B18A3D4780 8 | >>> 31 9 | -------------------------------------------------------------------------------- /test-data/2021/16b.txt: -------------------------------------------------------------------------------- 1 | C200B40A82 2 | >>> 3 3 | 04005AC33890 4 | >>> 54 5 | 880086C3E88112 6 | >>> 7 7 | CE00C43D881120 8 | >>> 9 9 | D8005AC2A8F0 10 | >>> 1 11 | F600BC2D8F 12 | >>> 0 13 | 9C005AC2F8F0 14 | >>> 0 15 | 9C0141080250320F1802104A08 16 | >>> 1 17 | -------------------------------------------------------------------------------- /test-data/2021/17a.txt: -------------------------------------------------------------------------------- 1 | target area: x=20..30, y=-10..-5 2 | >>> 45 3 | -------------------------------------------------------------------------------- /test-data/2021/17b.txt: -------------------------------------------------------------------------------- 1 | target area: x=20..30, y=-10..-5 2 | >>> 112 3 | -------------------------------------------------------------------------------- /test/Spec.hs: -------------------------------------------------------------------------------- 1 | 2 | import AOC 3 | import Control.Monad.Except 4 | import Data.Semigroup 5 | import System.Exit 6 | import Text.Printf 7 | import qualified System.Console.ANSI as ANSI 8 | 9 | main :: IO () 10 | main = do 11 | putStrLn "" 12 | cfg <- configFile defConfPath 13 | out <- runExceptT $ do 14 | runOut <- mainRun cfg $ (defaultMRO TSAll) 15 | { _mroTest = True 16 | } 17 | let Sum totErrors = (foldMap . foldMap) (uncurry numErrors) runOut 18 | when (totErrors > 0) $ throwError [printf "Failed %d test(s)." totErrors] 19 | case out of 20 | Left e -> do 21 | withColor ANSI.Vivid ANSI.Red $ 22 | putStrLn "[ERROR]" 23 | mapM_ putStrLn e 24 | exitFailure 25 | Right () -> pure () 26 | 27 | numErrors 28 | :: Maybe Bool 29 | -> Either [String] String 30 | -> Sum Int 31 | numErrors m e = contM m <> contE e 32 | where 33 | contM Nothing = 0 34 | contM (Just r) 35 | | r = Sum 0 36 | | otherwise = Sum 1 37 | contE (Left es) 38 | | null es = Sum 0 39 | | otherwise = Sum 1 40 | contE (Right _) = Sum 0 41 | --------------------------------------------------------------------------------