├── .gitignore ├── Build.hs ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Setup.hs ├── aoc-dev.cabal ├── app └── aoc.hs ├── bench └── Bench.hs ├── flake.lock ├── flake.nix ├── 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 │ ├── 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 └── 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 | -------------------------------------------------------------------------------- /Build.hs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stack 2 | -- stack --install-ghc runghc --package shake --package template --package directory --package containers --package text --package filepath --package strip-ansi-escape --package html-entities --package time 3 | 4 | {-# LANGUAGE OverloadedStrings #-} 5 | {-# LANGUAGE TypeApplications #-} 6 | {-# OPTIONS_GHC -Wall #-} 7 | 8 | -- | Assemble README and Reflections 9 | 10 | import Control.Monad 11 | import Data.Char 12 | import Data.Foldable 13 | import Data.List 14 | import Data.Maybe 15 | import Data.String.AnsiEscapeCodes.Strip.Text 16 | import Data.Text (Text) 17 | import Data.Text.Template 18 | import Data.Time 19 | import Development.Shake 20 | import Development.Shake.FilePath 21 | import Text.Printf 22 | import Text.Read 23 | import qualified Data.Map as M 24 | import qualified Data.Set as S 25 | import qualified Data.Text as T 26 | import qualified Data.Text.Lazy as TL 27 | import qualified HTMLEntities.Text as H 28 | 29 | -- CONSTANTS 30 | year :: Integer 31 | year = 2020 32 | github :: String 33 | github = "mstksg" 34 | otherYears :: S.Set Integer 35 | otherYears = S.fromList [2016 .. 2020] 36 | 37 | ctx0 :: M.Map Text Text 38 | ctx0 = M.fromList [ 39 | ("year" , T.pack (show year) ) 40 | , ("github", T.pack github ) 41 | , ("name" , "Justin Le" ) 42 | , ("email" , "justin@jle.im" ) 43 | , ("rss" , "http://feeds.feedburner.com/jle-advent-of-code-2020") 44 | , ("other_years", yearLinks ) 45 | ] 46 | 47 | opts :: ShakeOptions 48 | opts = shakeOptions { shakeFiles = "_build" 49 | , shakeVersion = "1.0" 50 | , shakeVerbosity = Chatty 51 | , shakeThreads = 1 -- for benchmarks to work properly 52 | } 53 | 54 | parseDayFp :: FilePath -> Maybe Int 55 | parseDayFp = readMaybe . filter validChar . takeBaseName 56 | where 57 | validChar p = isDigit p || p == '_' 58 | 59 | reflPath :: Int -> FilePath 60 | reflPath d = "reflections" printf "day%02d.md" d 61 | reflOutPath :: Int -> FilePath 62 | reflOutPath d = "_reflections" printf "day%02d.md" d 63 | reflOutCodedPath :: Int -> FilePath 64 | reflOutCodedPath d = "_reflections" printf "day%02d-coded.md" d 65 | reflXmlPath :: Int -> FilePath 66 | reflXmlPath d = "_reflections" printf "day%02d.xml" d 67 | benchPath :: Int -> FilePath 68 | benchPath d = "bench-out" printf "day%02d.txt" d 69 | standaloneReflectionPath :: Int -> FilePath 70 | standaloneReflectionPath d = "reflections-out" printf "day%02d.md" d 71 | 72 | main :: IO () 73 | main = shakeArgs opts $ do 74 | action $ do 75 | rd <- S.toList <$> reflectionDays 76 | need $ ["README.md", "reflections.md", "feed.xml"] 77 | ++ map standaloneReflectionPath rd 78 | 79 | "reflections.md" %> \fp -> do 80 | days <- getDays 81 | bodies <- forM (M.toList days) $ \(d, hasRefl) -> 82 | if hasRefl 83 | then T.pack <$> readFile' (reflOutPath d) 84 | else T.pack <$> readFile' (reflOutCodedPath d) 85 | let toc = flip map (M.toList days) $ \(d, hasRefl) -> 86 | if hasRefl 87 | then printf "* [Day %d](#day-%d)" d d 88 | else printf "* [Day %d](#day-%d) *(no reflection yet)*" d d 89 | yearUrls = tUnlines' . flip foldMap otherYears $ \oy -> 90 | T.pack (printf "[%04d]: https://github.com/%s/advent-of-code-%04d/blob/master/reflections.md" oy github oy) 91 | <$ guard (oy /= year) 92 | 93 | ctx = ctx0 <> M.fromList 94 | [ ("toc" , T.pack $ unlines' toc ) 95 | , ("body", T.intercalate "\n\n\n" bodies) 96 | , ("other_links", yearUrls ) 97 | ] 98 | writeTemplate fp ctx "template/reflections.md.template" 99 | 100 | "reflections-out/*.md" %> \fp -> do 101 | rDays <- S.toList <$> reflectionDays 102 | let Just d = parseDayFp fp 103 | hasRefl = not $ "coded" `isInfixOf` fp 104 | refl <- if hasRefl 105 | then T.pack <$> readFile' (reflPath d) 106 | else pure "*Reflection not yet written -- please check back later!*" 107 | bench <- T.pack <$> readFile' (benchPath d) 108 | let otherDays = T.intercalate " / " . flip map rDays $ \od -> 109 | let linker :: String 110 | linker 111 | | od == d = printf "%d" od 112 | | otherwise = printf "[%d][day%02d]" od od 113 | in T.pack $ printf "*%s*" linker 114 | dayLinks = tUnlines' . flip mapMaybe rDays $ \od -> 115 | T.pack 116 | (printf "[day%02d]: https://github.com/%s/advent-of-code-%04d/blob/master/%s" od github year (standaloneReflectionPath od)) 117 | <$ guard (od /= d) 118 | ctx = ctx0 <> M.fromList 119 | [ ("daylong" , T.pack $ printf "%02d" d) 120 | , ("dayshort" , T.pack $ printf "%d" d ) 121 | , ("body" , refl ) 122 | , ("benchmarks", bench ) 123 | , ("other_links", dayLinks ) 124 | , ("other_days" , otherDays ) 125 | ] 126 | writeTemplate fp ctx "template/standalone-reflection.md.template" 127 | 128 | 129 | "README.md" %> \fp -> do 130 | days <- getDays 131 | let mkRow d = case M.lookup d days of 132 | Just True -> 133 | printf "| Day %2d | [x][d%02dr] | [x][d%02dg] | [x][d%02dh] | [x][d%02db] |" 134 | d d d d d 135 | Just False -> 136 | printf "| Day %2d | | [x][d%02dg] | [x][d%02dh] | [x][d%02db] |" 137 | d d d d 138 | Nothing -> 139 | printf "| Day %2d | | | | |" 140 | d 141 | table = unlines' $ 142 | "| Challenge | Reflections | Code | Rendered | Benchmarks |" 143 | : "| --------- | ----------- | --------- | ---------- | ---------- |" 144 | : map mkRow [1..25] 145 | links = unlines' . M.foldMapWithKey mkLinks $ days 146 | yearUrls = tUnlines' . flip foldMap otherYears $ \oy -> 147 | T.pack (printf "[%04d]: https://github.com/%s/advent-of-code-%04d" oy github oy) 148 | <$ guard (oy /= year) 149 | ctx = ctx0 <> M.fromList 150 | [ ("table" , T.pack table) 151 | , ("links" , T.pack links) 152 | , ("other_links", yearUrls ) 153 | ] 154 | writeTemplate fp ctx "template/README.md.template" 155 | 156 | "feed.xml" %> \fp -> do 157 | days <- reflectionDays 158 | bodies <- forM (reverse (toList days)) $ \d -> 159 | T.pack <$> readFile' (reflXmlPath d) 160 | time <- utcToZonedTime (read "EST") <$> liftIO getCurrentTime 161 | let ctx = ctx0 <> M.fromList 162 | [ ("body", T.intercalate "\n" bodies) 163 | , ("time", T.pack . formatTime defaultTimeLocale rfc822DateFormat $ time ) 164 | ] 165 | writeTemplate fp ctx "template/feed.xml.template" 166 | 167 | "_reflections/*.md" %> \fp -> do 168 | let Just d = parseDayFp fp 169 | hasRefl = not $ "coded" `isInfixOf` fp 170 | refl <- if hasRefl 171 | then T.pack <$> readFile' (reflPath d) 172 | else pure "*Reflection not yet written -- please check back later!*" 173 | bench <- T.pack <$> readFile' (benchPath d) 174 | let ctx = ctx0 <> M.fromList 175 | [ ("daylong" , T.pack $ printf "%02d" d) 176 | , ("dayshort" , T.pack $ printf "%d" d ) 177 | , ("body" , refl ) 178 | , ("benchmarks", bench ) 179 | ] 180 | writeTemplate fp ctx "template/reflection.md.template" 181 | 182 | "_reflections/*.xml" %> \fp -> do 183 | let Just d = parseDayFp fp 184 | refl <- readFile' (reflOutPath d) 185 | Stdout html <- cmd ("pandoc -t html -f markdown" :: String) 186 | (Stdin refl) 187 | let time = ZonedTime 188 | (LocalTime (fromGregorian year 12 d) 189 | (TimeOfDay 1 0 0) 190 | ) 191 | (read "EST") 192 | ctx = ctx0 <> M.fromList 193 | [ ("day" , T.pack $ printf "%d" d ) 194 | , ("body", H.text . T.pack $ html ) 195 | , ("time", T.pack . formatTime defaultTimeLocale rfc822DateFormat $ time ) 196 | ] 197 | writeTemplate fp ctx "template/feed-item.xml.template" 198 | 199 | "bench-out/*.txt" %> \fp -> do 200 | let Just d = parseDayFp fp 201 | Stdout out <- cmd ("stack run --" :: String) (printf "bench %d" d :: String) 202 | writeFileChanged fp . T.unpack . T.strip . stripAnsiEscapeCodes . T.pack $ out 203 | 204 | "clean" ~> do 205 | removeFilesAfter "_reflections" ["//*"] 206 | -- removeFilesAfter "bench-out" ["//*"] 207 | removeFilesAfter "_build" ["//*"] 208 | removeFilesAfter "reflections-out" ["//*"] 209 | removeFilesAfter "/" ["README.md", "reflections.md"] 210 | where 211 | reflectionDays = S.fromList . mapMaybe parseDayFp <$> getDirectoryFiles "reflections" ["*.md"] 212 | codedDays = do 213 | ds <- mapMaybe parseDayFp <$> getDirectoryFiles codePath ["*.hs"] 214 | fmap S.fromList . flip filterM ds $ \d -> do 215 | let dayFile = codePath printf "Day%02d.hs" d 216 | not . ("AOC.Prelude" `T.isInfixOf`) . T.pack <$> readFile' dayFile 217 | getDays = do 218 | rd <- M.fromSet (const True ) <$> reflectionDays 219 | cd <- M.fromSet (const False) <$> codedDays 220 | pure $ M.unionWith (||) rd cd 221 | writeTemplate fp ctx templ = do 222 | out <- flip substitute (ctx M.!) . T.pack <$> readFile' templ 223 | writeFileChanged fp (TL.unpack out) 224 | 225 | yearLinks :: Text 226 | yearLinks = T.intercalate " / " . flip map (S.toList otherYears) $ \oy -> 227 | let linker | oy == year = "%04d" 228 | | otherwise = "[%04d][]" 229 | in T.pack $ printf "*%s*" (printf linker oy :: String) 230 | 231 | mkLinks :: Int -> Bool -> [String] 232 | mkLinks d hasRef = catMaybes [ 233 | Just $ printf "[d%02dg]: https://github.com/mstksg/advent-of-code-%04d/blob/master/src/AOC/Challenge/Day%02d.hs" 234 | d year d 235 | , Just $ printf "[d%02dh]: https://mstksg.github.io/advent-of-code-%04d/src/AOC.Challenge.Day%02d.html" 236 | d year d 237 | , do guard hasRef 238 | Just $ printf "[d%02dr]: https://github.com/mstksg/advent-of-code-%04d/blob/master/reflections.md#day-%d" 239 | d year d 240 | , Just $ printf "[d%02db]: https://github.com/mstksg/advent-of-code-%04d/blob/master/reflections.md#day-%d-benchmarks" 241 | d year d 242 | ] 243 | 244 | unlines' :: [String] -> String 245 | unlines' = intercalate "\n" 246 | tUnlines' :: [Text] -> Text 247 | tUnlines' = T.intercalate "\n" 248 | 249 | codePath :: FilePath 250 | codePath = "src/AOC/Challenge" 251 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Haskell Advent of Code Development Environment 2 | ================================================== 3 | 4 | This package contains the framework and executable for my [Advent of Code][aoc] 5 | interactive development environment and runner/tester/benchmarker. Networking 6 | powered by *[advent-of-code-api][]* library. 7 | 8 | You write your solutions in the `AOC.Challenge.DayXX` modules, make sure to 9 | uncomment the exports, and everything should be good to go for interactive 10 | running, testing, viewing of prompts --- as well as integration into the 11 | executable. 12 | 13 | [aoc]: https://adventofcode.com 14 | [advent-of-code-api]: https://hackage.haskell.org/package/advent-of-code-api 15 | 16 | Designed to only accommodate development for a single year. If you want to 17 | work on multiple years, you should re-fork into a new directory and 18 | re-configure (and change the executable name). 19 | 20 | ### `:~>` type 21 | 22 | The solutions expected in terms of a `:~>` record type: 23 | 24 | ```haskell 25 | data a :~> b = MkSol 26 | { sParse :: String -> Maybe a -- ^ parse input into an `a` 27 | , sSolve :: a -> Maybe b -- ^ solve an `a` input to a `b` solution 28 | , sShow :: b -> String -- ^ print out the `b` solution for submission 29 | } 30 | ``` 31 | 32 | An `a :~> b` is a solution to a challenge expecting input of type `a` and 33 | producing answers of type `b`. It also packs in functions to parse a `String` 34 | into an `a`, and functions to show a `b` as a `String` to submit as an answer. 35 | 36 | This is meant to help mentally separate out parsing, solving, and showing, 37 | allowing for some cleaner code and an easier time planning my solution. 38 | 39 | Such a challenge can be "run" on string inputs by feeding the string into 40 | `sParse`, then `sSolve`, then `sShow`: 41 | 42 | ```haskell 43 | -- | Run a ':~>' on some input, retuning 'Maybe' 44 | runSolution :: Challenge -> String -> Maybe String 45 | runSolution MkSol{..} s = do 46 | x <- sParse s 47 | y <- sSolve x 48 | pure $ sShow y 49 | ``` 50 | 51 | In the actual library, I have `runSolution` return an `Either` so I can debug 52 | which stage the error happened in. 53 | 54 | `sSolve` also supports `dyno_ :: Typeable a => String -> a -> a`, which is how 55 | *special test parameters* are implemented. For example, 2018 Day 6 involves 56 | finding points that had a total distance of less than 10000, but for the test 57 | input, we found the points that had a total distance of less than 32. So, 58 | `dyno_` allows you to write `dyno_ "limit" 10000`. This will be `10000` when 59 | running on test input, but will be replaced by the "limit" key that test data 60 | is allowed to manually supply. (See [this file][7btest] for reference.) 61 | 62 | [7btest]: https://github.com/mstksg/advent-of-code-dev/blob/master/test-data/2018/07b.txt 63 | 64 | It is common to want to use certain common "utility" functions between 65 | different tests. For this, you can add them to the `AOC.Common` module, and 66 | these will be loaded as a part of `AOC.Prelude`. 67 | 68 | Configuration 69 | ------------ 70 | 71 | When you run the `aoc-dev` executable for the first time, it will generate a 72 | default configuration file at `./aoc-conf.yaml`. At the moment, the 73 | configuration contains two fields: 74 | 75 | 1. `session`: the session key. Allows you to download input data, part 2 76 | prompts, and also submit challenges. 77 | 78 | Can be found by logging in on a web client and checking the cookies. You 79 | can usually check these with in-browser developer tools. 80 | 81 | 2. `year`: The year you are working on. Note that this project is designed to 82 | only accommodate one "year" at a time. If you want to work on a different 83 | year, you should re-fork and start in a new project directory (and change 84 | he executable name). Note that you can re-use session keys between years, 85 | provided that they have not expired. 86 | 87 | Interactive 88 | ----------- 89 | 90 | The *[AOC.Run.Interactive][interactive]* module has code (powered by 91 | *[advent-of-code-api][]*) for testing your solutions and submitting within 92 | GHCI, so you don't have to re-compile. If you edit your solution programs, they 93 | are automatically updated when you hit `:r` in ghci. 94 | 95 | [interactive]: https://mstksg.github.io/advent-of-code-dev/AOC-Run-Interactive.html 96 | 97 | ```haskell 98 | ghci> execSolution_ $ solSpec 'day02a -- get answer for challenge based on solution 99 | ghci> testSolution_ $ solSpec 'day02a -- run solution against test suite 100 | ghci> viewPrompt_ $ solSpec 'day02a -- view the prompt for a part 101 | ghci> waitForPrompt_ $ solSpec 'day02a -- count down to the prompt for a part 102 | ghci> submitSolution_ $ solSpec 'day02a -- submit a solution 103 | ``` 104 | 105 | These are loaded with session key stored in the configuration file. 106 | 107 | These identifiers (like `day02a`) need to be exported and in scope for this to 108 | work. If they aren't, you can manually specify the day and part, by using 109 | `mkCS 2 Part1`, etc. 110 | 111 | Executable 112 | ---------- 113 | 114 | Comes with test examples given in problems. The executable is named `aoc-dev` 115 | by default, but it is recommended that you change the name (in `package.yaml`) 116 | based on whatever year you are attempting. 117 | 118 | ``` 119 | $ aoc-dev --help 120 | aoc-dev - Advent of Code 2020 challenge runner 121 | 122 | Usage: aoc-dev [-c|--config PATH] COMMAND 123 | Run, test, bench, challenges from Advent of Code, and view prompts. 124 | Available days: 1, 2, 3 (...) 125 | 126 | Available options: 127 | -c,--config PATH Path to configuration file (default: aoc-conf.yaml) 128 | -h,--help Show this help text 129 | 130 | Available commands: 131 | run Run, test, and benchmark challenges 132 | view View a prompt for a given challenge 133 | submit Test and submit answers for challenges 134 | test Alias for run --test 135 | bench Alias for run --bench 136 | countdown Alias for view --countdown 137 | 138 | $ aoc-dev run 3 b 139 | >> Day 03b 140 | >> [✓] 243 141 | ``` 142 | 143 | You can supply input via stdin with `--stdin`: 144 | 145 | ``` 146 | $ aoc-dev run 1 --stdin 147 | >> Day 01a 148 | +1 149 | +2 150 | +1 151 | -3 152 | 153 | [?] 1 154 | >> Day 01b 155 | [?] 1 156 | ``` 157 | 158 | Benchmarking is implemented using *criterion* 159 | 160 | ``` 161 | $ aoc-dev bench 2 162 | >> Day 02a 163 | benchmarking... 164 | time 1.317 ms (1.271 ms .. 1.392 ms) 165 | 0.982 R² (0.966 R² .. 0.999 R²) 166 | mean 1.324 ms (1.298 ms .. 1.373 ms) 167 | std dev 115.5 μs (77.34 μs .. 189.0 μs) 168 | variance introduced by outliers: 65% (severely inflated) 169 | 170 | >> Day 02b 171 | benchmarking... 172 | time 69.61 ms (68.29 ms .. 72.09 ms) 173 | 0.998 R² (0.996 R² .. 1.000 R²) 174 | mean 69.08 ms (68.47 ms .. 69.99 ms) 175 | std dev 1.327 ms (840.8 μs .. 1.835 ms) 176 | ``` 177 | 178 | Test suites run the example problems given in the puzzle description, and 179 | outputs are colorized in ANSI terminals. 180 | 181 | ``` 182 | $ aoc-dev test 1 183 | >> Day 01a 184 | [✓] (3) 185 | [✓] (3) 186 | [✓] (0) 187 | [✓] (-6) 188 | [✓] Passed 4 out of 4 test(s) 189 | [✓] 416 190 | >> Day 01b 191 | [✓] (2) 192 | [✓] (0) 193 | [✓] (10) 194 | [✓] (5) 195 | [✓] (14) 196 | [✓] Passed 5 out of 5 test(s) 197 | [✓] 56752 198 | ``` 199 | 200 | This should only work if you're running `aoc-dev` in the project directory. 201 | 202 | **To run on actual inputs**, the executable expects inputs to be found in the 203 | folder `data/XX.txt` in the directory you are running in. That is, the input 204 | for Day 7 will be expected at `data/07.txt`. 205 | 206 | Session keys are required to download input data, "Part 2" prompts for each 207 | challenge, and also to submit. 208 | 209 | You can "lock in" your current answers (telling the executable that those are 210 | the correct answers) by passing in `--lock`. This will lock in any final 211 | puzzle solutions encountered as the verified official answers. Later, if you 212 | edit or modify your solutions, they will be checked on the locked-in answers. 213 | 214 | These are stored in `data/ans/XXpart.txt`. That is, the target output for Day 7 215 | (Part 2, `b`) will be expected at `data/ans/07b.txt`. You can also manually 216 | edit these files. 217 | 218 | You can view prompts: (use `--countdown` to count down until a prompt is 219 | released, and display immediately) 220 | 221 | ``` 222 | $ aoc-dev view 3 b 223 | >> Day 03b 224 | --- Part Two --- 225 | ---------------- 226 | 227 | Amidst the chaos, you notice that exactly one claim doesn't overlap by 228 | even a single square inch of fabric with any other claim. If you can 229 | somehow draw attention to it, maybe the Elves will be able to make 230 | Santa's suit after all! 231 | 232 | For example, in the claims above, only claim `3` is intact after all 233 | claims are made. 234 | 235 | *What is the ID of the only claim that doesn't overlap?* 236 | ``` 237 | 238 | You can also submit answers: 239 | 240 | ``` 241 | $ aoc-dev submit 1 a 242 | ``` 243 | 244 | Submissions will automatically run the test suite. If any tests fail, you will 245 | be asked to confirm submission or else abort. The submit command will output 246 | the result of your submission: The message from the AoC website, and whether or 247 | not your answer was correct (or invalid or ignored). Answers that are 248 | confirmed correct will be locked in and saved for future testing against, in 249 | case you change your solution. 250 | 251 | All networking features are powered by *[advent-of-code-api][]*. 252 | 253 | Note also that `stack test`, `stack bench`, `cabal test`, `cabal bench`, etc. 254 | are all convenient aliases of `aoc-dev test all` and `aoc-dev bench all`. This 255 | can be useful continuous integration purposes. 256 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /aoc-dev.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 2.4 2 | name: aoc-dev 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-dev#readme 10 | bug-reports: https://github.com/mstksg/advent-of-code-dev/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-dev.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 | , bytestring 32 | , constraints 33 | , constraints-extras < 0.4 34 | , containers 35 | , criterion 36 | , data-default-class 37 | , deepseq 38 | , dependent-sum 39 | , directory 40 | , filepath 41 | , finite-typelits 42 | , haskeline 43 | , haskell-names 44 | , haskell-src-exts 45 | , hpack 46 | , megaparsec 47 | , microlens-th 48 | , mtl 49 | , pandoc 50 | , parser-combinators 51 | , profunctors 52 | , tagsoup 53 | , template-haskell 54 | , text 55 | , th-abstraction 56 | , time 57 | , transformers 58 | , yaml 59 | 60 | ghc-options: -Wall 61 | -Wcompat 62 | -Widentities 63 | -Wincomplete-uni-patterns 64 | -Wincomplete-record-updates 65 | -Wno-partial-type-signatures 66 | if impl(ghc >= 8.0) 67 | ghc-options: -Wredundant-constraints 68 | if impl(ghc >= 8.2) 69 | ghc-options: -fhide-source-paths 70 | if impl(ghc >= 8.4) 71 | ghc-options: -Wmissing-export-lists 72 | -Wpartial-fields 73 | if impl(ghc >= 8.8) 74 | ghc-options: -Wmissing-deriving-strategies 75 | 76 | default-language: Haskell2010 77 | default-extensions: 78 | AllowAmbiguousTypes 79 | ApplicativeDo 80 | BangPatterns 81 | BlockArguments 82 | DataKinds 83 | DeriveFoldable 84 | DeriveFunctor 85 | DeriveGeneric 86 | DeriveTraversable 87 | DerivingStrategies 88 | EmptyCase 89 | FlexibleContexts 90 | FlexibleInstances 91 | FunctionalDependencies 92 | GADTs 93 | GeneralizedNewtypeDeriving 94 | ImplicitParams 95 | KindSignatures 96 | LambdaCase 97 | MonadComprehensions 98 | MultiParamTypeClasses 99 | MultiWayIf 100 | NumDecimals 101 | OverloadedLabels 102 | PartialTypeSignatures 103 | PatternGuards 104 | PatternSynonyms 105 | PolyKinds 106 | RankNTypes 107 | RecordWildCards 108 | ScopedTypeVariables 109 | StandaloneDeriving 110 | TemplateHaskell 111 | TupleSections 112 | TypeApplications 113 | TypeInType 114 | TypeOperators 115 | UndecidableInstances 116 | ViewPatterns 117 | 118 | library 119 | import: common-options 120 | exposed-modules: 121 | AOC 122 | AOC.Challenge 123 | AOC.Challenge.Day01 124 | AOC.Challenge.Day02 125 | AOC.Challenge.Day03 126 | AOC.Challenge.Day04 127 | AOC.Challenge.Day05 128 | AOC.Challenge.Day06 129 | AOC.Challenge.Day07 130 | AOC.Challenge.Day08 131 | AOC.Challenge.Day09 132 | AOC.Challenge.Day10 133 | AOC.Challenge.Day11 134 | AOC.Challenge.Day12 135 | AOC.Challenge.Day13 136 | AOC.Challenge.Day14 137 | AOC.Challenge.Day15 138 | AOC.Challenge.Day16 139 | AOC.Challenge.Day17 140 | AOC.Challenge.Day18 141 | AOC.Challenge.Day19 142 | AOC.Challenge.Day20 143 | AOC.Challenge.Day21 144 | AOC.Challenge.Day22 145 | AOC.Challenge.Day23 146 | AOC.Challenge.Day24 147 | AOC.Challenge.Day25 148 | AOC.Common 149 | AOC.Discover 150 | AOC.Prelude 151 | AOC.Run 152 | AOC.Run.Config 153 | AOC.Run.Interactive 154 | AOC.Run.Load 155 | AOC.Solver 156 | AOC.Util 157 | AOC.Util.DynoMap 158 | other-modules: 159 | Paths_aoc_dev 160 | hs-source-dirs: 161 | src 162 | other-modules: 163 | Paths_aoc_dev 164 | 165 | executable aoc-dev 166 | import: common-options 167 | main-is: aoc.hs 168 | hs-source-dirs: 169 | app 170 | build-depends: 171 | ansi-terminal 172 | , aoc-dev 173 | , base >=4.7 && <5 174 | , containers 175 | , deepseq 176 | , finite-typelits 177 | , microlens 178 | , mtl 179 | , optparse-applicative 180 | default-language: Haskell2010 181 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 182 | 183 | test-suite aoc-dev-test 184 | import: common-options 185 | type: exitcode-stdio-1.0 186 | main-is: Spec.hs 187 | hs-source-dirs: 188 | test 189 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 190 | build-depends: 191 | ansi-terminal 192 | , aoc-dev 193 | , base >=4.7 && <5 194 | , mtl 195 | default-language: Haskell2010 196 | 197 | benchmark aoc-dev-bench 198 | type: exitcode-stdio-1.0 199 | main-is: Bench.hs 200 | hs-source-dirs: 201 | bench 202 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 203 | build-depends: 204 | aoc-dev 205 | , base >=4.7 && <5 206 | , mtl 207 | default-language: Haskell2010 208 | 209 | -------------------------------------------------------------------------------- /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.Monad 9 | import Control.Monad.Except 10 | import Data.Char 11 | import Data.Foldable 12 | import Data.IORef 13 | import Data.Maybe 14 | import Lens.Micro 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 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "aoc-dev"; 3 | inputs = { 4 | haskellNix.url = "github:input-output-hk/haskell.nix"; 5 | nixpkgs.follows = "haskellNix/nixpkgs-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | outputs = { self, nixpkgs, flake-utils, haskellNix }: 9 | flake-utils.lib.eachSystem [ "x86_64-linux" "x86_64-darwin" ] (system: 10 | let 11 | overlays = [ 12 | haskellNix.overlay 13 | (final: prev: { 14 | aoc-dev = final.haskell-nix.project' { 15 | name = "aoc-dev"; 16 | src = ./.; 17 | compiler-nix-name = "ghc902"; 18 | shell.tools = { 19 | cabal = { }; 20 | # hlint = {}; 21 | haskell-language-server = { }; 22 | }; 23 | }; 24 | }) 25 | ]; 26 | pkgs = import nixpkgs { inherit system overlays; inherit (haskellNix) config; }; 27 | flake = pkgs.aoc-dev.flake { }; 28 | in 29 | flake // { packages.default = flake.packages."aoc-dev:exe:aoc-dev"; } 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /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 = 2020 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 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day01 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 1. 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.Day01 ( 25 | -- day01a 26 | -- , day01b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day01a :: _ :~> _ 32 | day01a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day01b :: _ :~> _ 39 | day01b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day02.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day02 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 2. 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.Day02 ( 25 | -- day02a 26 | -- , day02b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day02a :: _ :~> _ 32 | day02a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day02b :: _ :~> _ 39 | day02b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day03.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day03 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 3. 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.Day03 ( 25 | -- day03a 26 | -- , day03b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day03a :: _ :~> _ 32 | day03a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day03b :: _ :~> _ 39 | day03b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day04.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day04 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 4. 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.Day04 ( 25 | -- day04a 26 | -- , day04b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day04a :: _ :~> _ 32 | day04a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day04b :: _ :~> _ 39 | day04b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day05.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day05 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 5. 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.Day05 ( 25 | -- day05a 26 | -- , day05b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day05a :: _ :~> _ 32 | day05a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day05b :: _ :~> _ 39 | day05b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day06.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day06 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 6. 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.Day06 ( 25 | -- day06a 26 | -- , day06b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day06a :: _ :~> _ 32 | day06a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day06b :: _ :~> _ 39 | day06b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day07.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day07 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 7. 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.Day07 ( 25 | -- day07a 26 | -- , day07b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day07a :: _ :~> _ 32 | day07a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day07b :: _ :~> _ 39 | day07b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day08.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day08 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 8. 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.Day08 ( 25 | -- day08a 26 | -- , day08b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day08a :: _ :~> _ 32 | day08a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day08b :: _ :~> _ 39 | day08b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day09.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day09 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 9. 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.Day09 ( 25 | -- day09a 26 | -- , day09b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day09a :: _ :~> _ 32 | day09a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day09b :: _ :~> _ 39 | day09b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day10.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day10 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 10. 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.Day10 ( 25 | -- day10a 26 | -- , day10b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day10a :: _ :~> _ 32 | day10a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day10b :: _ :~> _ 39 | day10b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day11.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day11 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 11. 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.Day11 ( 25 | -- day11a 26 | -- , day11b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day11a :: _ :~> _ 32 | day11a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day11b :: _ :~> _ 39 | day11b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day12a :: _ :~> _ 32 | day12a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day12b :: _ :~> _ 39 | day12b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day13a :: _ :~> _ 32 | day13a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day13b :: _ :~> _ 39 | day13b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day14.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day14 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 14. 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.Day14 ( 25 | -- day14a 26 | -- , day14b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day14a :: _ :~> _ 32 | day14a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day14b :: _ :~> _ 39 | day14b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day15.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day15 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 15. 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.Day15 ( 25 | -- day15a 26 | -- , day15b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day15a :: _ :~> _ 32 | day15a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day15b :: _ :~> _ 39 | day15b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Challenge/Day16.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | {-# OPTIONS_GHC -Wno-unused-top-binds #-} 3 | 4 | -- | 5 | -- Module : AOC.Challenge.Day16 6 | -- License : BSD3 7 | -- 8 | -- Stability : experimental 9 | -- Portability : non-portable 10 | -- 11 | -- Day 16. 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.Day16 ( 25 | -- day16a 26 | -- , day16b 27 | ) where 28 | 29 | import AOC.Prelude 30 | 31 | day16a :: _ :~> _ 32 | day16a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day16b :: _ :~> _ 39 | day16b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day17a :: _ :~> _ 32 | day17a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day17b :: _ :~> _ 39 | day17b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day18a :: _ :~> _ 32 | day18a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day18b :: _ :~> _ 39 | day18b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day19a :: _ :~> _ 32 | day19a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day19b :: _ :~> _ 39 | day19b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day20a :: _ :~> _ 32 | day20a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day20b :: _ :~> _ 39 | day20b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day21a :: _ :~> _ 32 | day21a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day21b :: _ :~> _ 39 | day21b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day22a :: _ :~> _ 32 | day22a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day22b :: _ :~> _ 39 | day22b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day23a :: _ :~> _ 32 | day23a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day23b :: _ :~> _ 39 | day23b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day24a :: _ :~> _ 32 | day24a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day24b :: _ :~> _ 39 | day24b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /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 | day25a :: _ :~> _ 32 | day25a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day25b :: _ :~> _ 39 | day25b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /src/AOC/Common.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Challenge 3 | -- License : BSD3 4 | -- 5 | -- Stability : experimental 6 | -- Portability : non-portable 7 | -- 8 | -- Meant to be a place to include common functionality used across 9 | -- different parts in the challenge. 10 | -- 11 | 12 | 13 | module AOC.Common ( 14 | ) where 15 | 16 | -------------------------------------------------------------------------------- /src/AOC/Discover.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE TemplateHaskell #-} 3 | 4 | -- | 5 | -- Module : AOC.Discover 6 | -- Copyright : (c) Justin Le 2021 7 | -- License : BSD3 8 | -- 9 | -- Maintainer : justin@jle.im 10 | -- Stability : experimental 11 | -- Portability : non-portable 12 | -- 13 | -- Template Haskell for discovering all named challenges in a given 14 | -- directory. 15 | -- 16 | 17 | module AOC.Discover ( 18 | mkChallengeMap 19 | , solutionList 20 | , ChallengeMap 21 | , ChallengeSpec(..) 22 | , solSpec 23 | , solSpecStr 24 | , solSpecStr_ 25 | , charPart 26 | , challengeName 27 | , solverNFData 28 | , deepInstance 29 | ) where 30 | 31 | import AOC.Solver 32 | import Advent 33 | import Control.Applicative 34 | import Control.DeepSeq 35 | import Control.Monad 36 | import Control.Monad.Trans.Class 37 | import Control.Monad.Trans.Maybe 38 | import Data.Bifunctor 39 | import Data.Data 40 | import Data.Foldable 41 | import Data.Function 42 | import Data.Map (Map) 43 | import Data.Maybe 44 | import Data.Traversable 45 | import Data.Void 46 | import GHC.Exts 47 | import Language.Haskell.Exts as E 48 | import Language.Haskell.Names 49 | import Language.Haskell.TH as TH 50 | import Language.Haskell.TH.Datatype 51 | import Language.Haskell.TH.Syntax (TExp(..)) 52 | import Prelude 53 | import System.Directory 54 | import System.FilePath 55 | import Text.Printf 56 | import qualified Data.List.NonEmpty as NE 57 | import qualified Distribution.Pretty as C 58 | import qualified Data.Map as M 59 | import qualified Distribution.PackageDescription as C 60 | import qualified Distribution.PackageDescription.Parsec as C 61 | import qualified Distribution.Simple.Utils as C 62 | import qualified Distribution.Verbosity as C 63 | import qualified Text.Megaparsec as P 64 | import qualified Text.Megaparsec.Char as P 65 | import qualified Text.Megaparsec.Char.Lexer as PL 66 | 67 | -- | Big quick escape hatch if things explode in the middle of solving. 68 | -- This will disable the check for NFData when using 'MkSomeSol' and assume 69 | -- no NFData in every case. 70 | checkIfNFData :: Bool 71 | checkIfNFData = True 72 | -- checkIfNFData = False 73 | 74 | -- | A specification for a specific challenge. Should consist of a day and 75 | -- a lowercase character. 76 | data ChallengeSpec = CS { _csDay :: Day 77 | , _csPart :: Part 78 | } 79 | deriving stock (Show, Eq, Ord) 80 | 81 | -- | A map of days to parts to solutions. 82 | type ChallengeMap = Map Day (Map Part SomeSolution) 83 | 84 | -- | Get a 'ChallengeSpec' from a given reified solution (name). 85 | -- 86 | -- @ 87 | -- solSpec \'day02a == CS { _csDay = 1, _csPart = 'a' } 88 | -- @ 89 | -- 90 | solSpec :: TH.Name -> ChallengeSpec 91 | solSpec n = solSpecStr_ (nameBase n) 92 | 93 | solSpecStr :: String -> Either (P.ParseErrorBundle String Void) ChallengeSpec 94 | solSpecStr = P.runParser challengeName "" 95 | 96 | solSpecStr_ :: String -> ChallengeSpec 97 | solSpecStr_ = either (error . P.errorBundlePretty) id . solSpecStr 98 | 99 | instance IsString ChallengeSpec where 100 | fromString = solSpecStr_ 101 | 102 | type Parser = P.Parsec Void String 103 | 104 | -- | Template Haskell splice to produce a list of all named solutions in 105 | -- a directory. Expects solutions as function names following the format 106 | -- @dayDDp@, where @DD@ is a two-digit zero-added day, and @p@ is 107 | -- a lower-case letter corresponding to the part of the challenge. 108 | -- 109 | -- See 'mkChallengeMap' for a description of usage. 110 | #if MIN_VERSION_template_haskell(2,17,0) 111 | solutionList :: FilePath -> Code Q [(Day, (Part, SomeSolution))] 112 | solutionList dir = Code $ 113 | #else 114 | solutionList :: FilePath -> Q (TH.TExp [(Day, (Part, SomeSolution))]) 115 | solutionList dir = 116 | #endif 117 | fmap (TExp . ListE) 118 | . traverse (fmap unType . specExp) 119 | =<< runIO (getChallengeSpecs dir) 120 | 121 | -- | Meant to be called like: 122 | -- 123 | -- @ 124 | -- mkChallengeMap $$(solutionList "src\/AOC\/Challenge") 125 | -- @ 126 | mkChallengeMap :: [(Day, (Part, SomeSolution))] -> ChallengeMap 127 | mkChallengeMap = M.unionsWith M.union 128 | . map (uncurry M.singleton . second (uncurry M.singleton)) 129 | 130 | 131 | specExp :: ChallengeSpec -> Q (TExp (Day, (Part, SomeSolution))) 132 | specExp s@(CS d p) = do 133 | n <- lookupValueName (specName s) 134 | con <- case n of 135 | Nothing -> pure 'MkSomeSolWH 136 | Just n' -> do 137 | isNF <- solverNFData n' 138 | pure $ if isNF 139 | then 'MkSomeSolNF 140 | else 'MkSomeSolWH 141 | pure $ TExp $ tTupE 142 | [ VarE 'mkDay_ `AppE` LitE (IntegerL (dayInt d)) 143 | , tTupE 144 | [ ConE (partCon p) 145 | , ConE con `AppE` VarE (mkName (specName s)) 146 | ] 147 | ] 148 | where 149 | partCon Part1 = 'Part1 150 | partCon Part2 = 'Part2 151 | #if MIN_VERSION_template_haskell(2,16,0) 152 | tTupE = TupE . fmap Just 153 | #else 154 | tTupE = TupE 155 | #endif 156 | 157 | specName :: ChallengeSpec -> String 158 | specName (CS d p) = printf "day%02d%c" (dayInt d) (partChar p) 159 | 160 | getChallengeSpecs 161 | :: FilePath -- ^ directory of modules 162 | -> IO [ChallengeSpec] -- ^ all challenge specs found 163 | getChallengeSpecs dir = do 164 | exts <- defaultExtensions 165 | files <- listDirectory dir 166 | parsed <- forM files $ \f -> do 167 | let mode = defaultParseMode { extensions = exts 168 | , fixities = Just [] 169 | , parseFilename = f 170 | } 171 | res <- parseFileWithMode mode (dir f) 172 | case res of 173 | ParseOk x -> pure x 174 | ParseFailed l e -> fail $ printf "Failed parsing %s at %s: %s" f (show l) e 175 | pure $ moduleSolutions parsed 176 | 177 | defaultExtensions :: IO [E.Extension] 178 | defaultExtensions = do 179 | gpd <- C.readGenericPackageDescription C.silent =<< C.defaultPackageDesc C.silent 180 | pure . map reExtension . foldMap (C.defaultExtensions . C.libBuildInfo) $ gpdLibraries gpd 181 | where 182 | gpdLibraries gpd = 183 | Data.Foldable.toList (C.library (C.packageDescription gpd)) 184 | ++ foldMap Data.Foldable.toList (C.condLibrary gpd) 185 | ++ foldMap (foldMap Data.Foldable.toList) (C.condSubLibraries gpd) 186 | reExtension = parseExtension . C.prettyShow 187 | 188 | moduleSolutions :: (Data l, Eq l) => [Module l] -> [ChallengeSpec] 189 | moduleSolutions = (foldMap . foldMap) (maybeToList . isSolution) 190 | . flip resolve M.empty 191 | 192 | 193 | isSolution :: Symbol -> Maybe ChallengeSpec 194 | isSolution s = do 195 | Value _ (Ident _ n) <- pure s 196 | Right c <- pure $ P.runParser challengeName "" n 197 | pure c 198 | 199 | challengeName :: Parser ChallengeSpec 200 | challengeName = do 201 | _ <- P.string "day" 202 | dInt <- PL.decimal 203 | dFin <- maybe (fail $ "Day not in range: " ++ show dInt) pure $ 204 | mkDay dInt 205 | c <- P.lowerChar 206 | p <- maybe (fail $ printf "Part not parsed: %c" c) pure $ 207 | charPart c 208 | pure $ CS dFin p 209 | 210 | -- | Parse a 'Char' into a 'Part' 211 | charPart :: Char -> Maybe Part 212 | charPart 'a' = Just Part1 213 | charPart 'b' = Just Part2 214 | charPart _ = Nothing 215 | 216 | -- | Check if a solver identifier is of type @A ':~>' B@, where @B@ is an 217 | -- instance of 'NFData'. 218 | solverNFData :: TH.Name -> Q Bool 219 | solverNFData n 220 | | checkIfNFData = reify n >>= \case 221 | VarI _ (ConT c `AppT` a `AppT` _) _ 222 | | c == ''(:~>) -> deepInstance ''NFData a 223 | _ -> pure False 224 | | otherwise = pure False 225 | 226 | -- | Check if a type is an instance of a class, unifying when possible 227 | deepInstance 228 | :: TH.Name -- ^ class 229 | -> TH.Type -- ^ type 230 | -> Q Bool 231 | deepInstance cn = fmap isJust . runMaybeT . deepInstance_ cn 232 | 233 | deepInstance_ 234 | :: TH.Name -- ^ class 235 | -> TH.Type -- ^ type 236 | -> MaybeT Q () 237 | deepInstance_ cn t = do 238 | insts <- maybe empty pure . NE.nonEmpty =<< lift (reifyInstances cn [t]) 239 | forM_ insts $ \case 240 | InstanceD _ ctx instHead _ -> do 241 | uni <- lift $ unifyTypes [ConT cn `AppT` t, instHead] 242 | forM_ ctx $ \case 243 | AppT (ConT c) v -> deepInstance_ c (applySubstitution uni v) 244 | _ -> empty 245 | _ -> empty 246 | -------------------------------------------------------------------------------- /src/AOC/Prelude.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : AOC.Prelude 3 | -- Copyright : (c) Justin Le 2021 4 | -- License : BSD3 5 | -- 6 | -- Maintainer : justin@jle.im 7 | -- Stability : experimental 8 | -- Portability : non-portable 9 | -- 10 | -- Custom Prelude while developing challenges. Ideally, once challenges 11 | -- are completed, an import to this module would be replaced with explicit 12 | -- ones for future readers. 13 | -- 14 | 15 | 16 | module AOC.Prelude ( 17 | module P 18 | ) where 19 | 20 | import AOC.Common as P 21 | import AOC.Solver as P 22 | import AOC.Util as P 23 | import Control.Applicative as P 24 | import Control.DeepSeq as P 25 | import Control.Monad as P 26 | import Control.Monad.Except as P 27 | import Control.Monad.State as P 28 | import Data.Bifunctor as P 29 | import Data.Char as P 30 | import Data.Containers.ListUtils as P 31 | import Data.Either as P 32 | import Data.Foldable as P 33 | import Data.Function as P 34 | import Data.Functor as P 35 | import Data.IntMap as P (IntMap) 36 | import Data.IntSet as P (IntSet) 37 | import Data.Kind as P 38 | import qualified Data.List as P 39 | import Data.List.NonEmpty as P (NonEmpty(..), nonEmpty) 40 | import Data.Map as P (Map) 41 | import Data.Maybe as P 42 | import Data.Ord as P 43 | import Data.Profunctor as P (Profunctor(..)) 44 | import Data.Semigroup as P 45 | import Data.Set as P (Set) 46 | import Data.Time as P hiding (Day) 47 | import Data.Traversable as P 48 | import Data.Tuple as P 49 | import Data.Void as P 50 | import Debug.Trace as P 51 | import GHC.Generics as P (Generic) 52 | import Numeric.Natural as P 53 | import Text.Printf as P 54 | import Text.Read as P (readMaybe) 55 | -------------------------------------------------------------------------------- /src/AOC/Run.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ImplicitParams #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE TemplateHaskell #-} 4 | 5 | -- | 6 | -- Module : AOC.Interactive 7 | -- Copyright : (c) Justin Le 2021 8 | -- License : BSD3 9 | -- 10 | -- Maintainer : justin@jle.im 11 | -- Stability : experimental 12 | -- Portability : non-portable 13 | -- 14 | -- Run actions regarding challenges, solutions, tests, submissions, viewing 15 | -- prompts, etc. 16 | -- 17 | -- Essentially implements the functionality of the main app. 18 | -- 19 | 20 | module AOC.Run ( 21 | -- * Options 22 | TestSpec(..) 23 | -- * Runners 24 | -- ** Run solutions, tests, benchmarks 25 | , MainRunOpts(..), HasMainRunOpts(..), mainRun, defaultMRO 26 | -- ** View prompts 27 | , MainViewOpts(..), HasMainViewOpts(..), mainView, defaultMVO 28 | -- ** Submit answers 29 | , MainSubmitOpts(..), HasMainSubmitOpts(..), mainSubmit, defaultMSO 30 | -- * Util 31 | , withColor 32 | ) where 33 | 34 | import AOC.Challenge 35 | import AOC.Run.Config 36 | import AOC.Run.Load 37 | import AOC.Solver 38 | import AOC.Util 39 | import Advent 40 | import Control.Applicative 41 | import Control.Concurrent 42 | import Control.DeepSeq 43 | import Control.Exception 44 | import Control.Monad 45 | import Control.Monad.Except 46 | import Criterion 47 | import Data.Bifunctor 48 | import Data.Char 49 | import Data.Map (Map) 50 | import Data.Maybe 51 | import Data.Text (Text) 52 | import Data.Time hiding (Day) 53 | import Lens.Micro.TH 54 | import Text.Printf 55 | import qualified Data.Map as M 56 | import qualified Data.Set as S 57 | import qualified Data.Text as T 58 | import qualified Data.Text.IO as T 59 | import qualified System.Console.ANSI as ANSI 60 | import qualified System.Console.Haskeline as H 61 | 62 | -- | Specification of parts to test and run 63 | data TestSpec = TSAll 64 | | TSDayAll { _tsDay :: Day } 65 | | TSDayPart { _tsSpec :: ChallengeSpec } 66 | deriving stock Show 67 | 68 | -- | Options for 'mainRun'. 69 | data MainRunOpts = MRO { _mroSpec :: !TestSpec 70 | , _mroActual :: !Bool -- ^ Run input? (Defualt: True 71 | , _mroTest :: !Bool -- ^ Run tests? (Default: False) 72 | , _mroBench :: !Bool -- ^ Benchmark? (Default: False) 73 | , _mroLock :: !Bool -- ^ Lock in answer as correct? (Default: False) 74 | , _mroInput :: !(Day -> Part -> IO (Maybe String)) -- ^ Manually supply input (Default: always return Nothing) 75 | } 76 | 77 | makeClassy ''MainRunOpts 78 | 79 | -- | Options for 'mainView'. 80 | data MainViewOpts = MVO { _mvoSpec :: !TestSpec 81 | , _mvoWait :: !Bool 82 | } 83 | deriving stock Show 84 | 85 | makeClassy ''MainViewOpts 86 | 87 | -- | Options for 'mainSubmit' 88 | data MainSubmitOpts = MSO { _msoSpec :: !ChallengeSpec 89 | , _msoTest :: !Bool -- ^ Run tests before submitting? (Default: True) 90 | , _msoForce :: !Bool -- ^ Force submission even if bad? (Default: False) 91 | , _msoLock :: !Bool -- ^ Lock answer if submission succeeded? (Default: True) 92 | } 93 | deriving stock Show 94 | 95 | makeClassy ''MainSubmitOpts 96 | 97 | -- | Default options for 'mainRun'. 98 | defaultMRO :: TestSpec -> MainRunOpts 99 | defaultMRO ts = MRO { _mroSpec = ts 100 | , _mroActual = True 101 | , _mroTest = False 102 | , _mroBench = False 103 | , _mroLock = False 104 | , _mroInput = \_ _ -> pure Nothing 105 | } 106 | 107 | -- | Default options for 'mainView'. 108 | defaultMVO :: TestSpec -> MainViewOpts 109 | defaultMVO ts = MVO { _mvoSpec = ts 110 | , _mvoWait = False 111 | } 112 | 113 | -- | Default options for 'mainSubmit'. 114 | defaultMSO :: ChallengeSpec -> MainSubmitOpts 115 | defaultMSO cs = MSO { _msoSpec = cs 116 | , _msoTest = True 117 | , _msoForce = False 118 | , _msoLock = True 119 | } 120 | 121 | filterChallengeMap :: TestSpec -> Either String ChallengeMap 122 | filterChallengeMap = \case 123 | TSAll -> pure challengeMap 124 | TSDayAll d -> maybeToEither (printf "Day not yet avaiable: %d" (dayInt d)) $ 125 | M.singleton d <$> M.lookup d challengeMap 126 | TSDayPart (CS d p) -> do 127 | ps <- maybeToEither (printf "Day not yet available: %d" (dayInt d)) $ 128 | M.lookup d challengeMap 129 | c <- maybeToEither (printf "Part not found: %c" (partChar p)) $ 130 | M.lookup p ps 131 | pure $ M.singleton d (M.singleton p c) 132 | 133 | -- | Run, test, bench. 134 | mainRun 135 | :: (MonadIO m, MonadError [String] m) 136 | => Config 137 | -> MainRunOpts 138 | -> m (Map Day (Map Part (Maybe Bool, Either [String] String))) -- whether or not passed tests, and result 139 | mainRun Cfg{..} MRO{..} = do 140 | toRun <- liftEither . first (:[]) . filterChallengeMap $ _mroSpec 141 | liftIO . runAll _cfgSession _cfgYear _mroLock _mroInput toRun $ \c inp0 cd@CD{..} -> do 142 | testRes <- fmap join . forM (guard _mroTest) $ \_ -> 143 | runTestSuite c cd 144 | 145 | let inp1 = maybe _cdInput Right inp0 146 | ans1 = maybe _cdAnswer (const Nothing) inp0 147 | case inp1 of 148 | Right inp 149 | | _mroBench -> do 150 | _ <- evaluate (force inp) 151 | let res = (testRes, Left ["Ran benchmark, so no result"]) 152 | res <$ case c of 153 | MkSomeSolWH _ -> 154 | benchmark (nf (runSomeSolution c) inp) 155 | MkSomeSolNF MkSol{..} 156 | | Just x <- sParse inp -> do 157 | _ <- evaluate (force x) 158 | benchmark (nf (let ?dyno = mempty in sSolve) x) 159 | putStrLn "* parsing and formatting times excluded" 160 | putStrLn "" 161 | | otherwise -> 162 | putStrLn "(No parse)" 163 | | _mroActual -> (second . first) ((:[]) . show) <$> testCase False c inp (TM ans1 M.empty) 164 | | otherwise -> pure (testRes, Left ["Actual input skipped"]) 165 | Left e 166 | | _mroTest -> pure (testRes, Left ["Ran tests, so no result"]) 167 | | otherwise -> (testRes, Left e) <$ putStrLn "[INPUT ERROR]" <* mapM_ putStrLn e 168 | 169 | -- | View prompt 170 | mainView 171 | :: (MonadIO m, MonadError [String] m) 172 | => Config 173 | -> MainViewOpts 174 | -> m (Map Day (Map Part Text)) 175 | mainView Cfg{..} MVO{..} = do 176 | let toRun = maybe S.empty (M.keysSet . pullMap) 177 | . eitherToMaybe 178 | . filterChallengeMap 179 | $ _mvoSpec 180 | allRun = foldMap S.singleton singleTest <> toRun 181 | fmap pushMap . sequenceA . flip M.fromSet allRun $ \(d,p) -> do 182 | pmpt <- waitFunc d $ do 183 | CD{..} <- liftIO $ challengeData _cfgSession _cfgYear (CS d p) 184 | liftEither . first ("[PROMPT ERROR]":) $ _cdPrompt 185 | liftIO $ do 186 | withColor ANSI.Dull ANSI.Blue $ 187 | printf ">> Day %02d%c\n" (dayInt d) (partChar p) 188 | T.putStrLn pmpt 189 | putStrLn "" 190 | pure pmpt 191 | where 192 | waitFunc d 193 | | _mvoWait = countdownConsole _cfgYear d . (liftIO (threadDelay 500000) *>) 194 | | otherwise = id 195 | singleTest = case _mvoSpec of 196 | TSAll -> Nothing 197 | TSDayAll d -> Just (d, Part1) 198 | TSDayPart cs -> Just (_csDay cs, _csPart cs) 199 | 200 | -- | Submit and analyze result 201 | mainSubmit 202 | :: (MonadIO m, MonadError [String] m) 203 | => Config 204 | -> MainSubmitOpts 205 | -> m (Text, SubmitRes) 206 | mainSubmit Cfg{..} MSO{..} = do 207 | cd@CD{..} <- liftIO $ challengeData _cfgSession _cfgYear _msoSpec 208 | dMap <- maybeToEither [printf "Day not yet available: %d" d'] $ 209 | M.lookup _csDay challengeMap 210 | c <- maybeToEither [printf "Part not found: %c" (partChar _csPart)] $ 211 | M.lookup _csPart dMap 212 | inp <- liftEither . first ("[PROMPT ERROR]":) $ _cdInput 213 | opts <- defaultAoCOpts _cfgYear <$> 214 | maybeToEither ["ERROR: Session Key Required to Submit"] 215 | _cfgSession 216 | 217 | when _msoTest $ do 218 | testRes <- liftIO $ runTestSuite c cd 219 | unless (and testRes) $ 220 | if _msoForce 221 | then liftIO $ putStrLn "Proceeding with submission despite test failures (--force)" 222 | else do 223 | conf <- liftIO . H.runInputT H.defaultSettings $ 224 | H.getInputChar "Some tests failed. Are you sure you wish to proceed? y/(n) " 225 | case toLower <$> conf of 226 | Just 'y' -> pure () 227 | _ -> throwError ["Submission aborted."] 228 | 229 | resEither <- liftIO . evaluate . force . runSomeSolution c $ inp 230 | res <- liftEither . first (("[SOLUTION ERROR]":) . (:[]) . show) $ resEither 231 | liftIO $ printf "Submitting solution: %s\n" res 232 | 233 | output@(resp, status) <- liftEither . first showAoCError 234 | =<< liftIO (runAoC opts (AoCSubmit _csDay _csPart res)) 235 | let resp' = formatResp 236 | . either (map T.pack) T.lines 237 | . htmlToMarkdown False 238 | $ resp 239 | (color, lock, out) = displayStatus status 240 | liftIO $ do 241 | withColor ANSI.Vivid color $ 242 | putStrLn out 243 | putStrLn resp' 244 | when lock $ 245 | if _msoLock 246 | then putStrLn "Locking correct answer." >> writeFile _cpAnswer res 247 | else putStrLn "Not locking correct answer (--no-lock)" 248 | zt <- getZonedTime 249 | appendFile _cpLog $ printf logFmt (show zt) res (showSubmitRes status) resp resp' 250 | pure output 251 | where 252 | CS{..} = _msoSpec 253 | CP{..} = challengePaths _cfgYear _msoSpec 254 | d' = dayInt _csDay 255 | formatResp = T.unpack . T.intercalate "\n" . map ("> " <>) 256 | logFmt = unlines [ "[%s]" 257 | , "Submission: %s" 258 | , "Status: %s" 259 | , "Raw: %s" 260 | , "%s" 261 | ] 262 | 263 | displayStatus :: SubmitRes -> (ANSI.Color, Bool, String) 264 | displayStatus = \case 265 | SubCorrect r -> ( ANSI.Green , True , correctMsg r ) 266 | SubIncorrect t h -> ( ANSI.Red , False, incorrectMsg t h ) 267 | SubWait t -> let (m, s) = t `divMod` 60 268 | resp = printf "Answer re-submitted too soon. Please wait %dmin %dsec" m s 269 | in ( ANSI.Yellow, False, resp ) 270 | SubInvalid{} -> ( ANSI.Blue , False 271 | , "Submission was rejected. Maybe not unlocked yet, or already answered?" 272 | ) 273 | SubUnknown{} -> ( ANSI.Magenta, False 274 | , "Response from server was not recognized." 275 | ) 276 | where 277 | correctMsg Nothing = "Answer was correct!" 278 | correctMsg (Just r) = 279 | printf "Answer was correct, and you made the global leaderboard at rank %d !!" 280 | r 281 | incorrectMsg t h = 282 | printf "Answer was incorrect!%s Please wait %d before submitting again" 283 | hintStr 284 | (t `div` 60) 285 | where 286 | hintStr :: String 287 | hintStr = case h of 288 | Nothing -> "" 289 | Just s -> printf " Hint: Answer was %s." s 290 | 291 | runAll 292 | :: Maybe String -- ^ session key 293 | -> Integer -- ^ year 294 | -> Bool -- ^ run and lock answer 295 | -> (Day -> Part -> IO (Maybe String)) -- ^ replacements 296 | -> ChallengeMap 297 | -> (SomeSolution -> Maybe String -> ChallengeData -> IO a) -- ^ callback. given solution, "replacement" input, and data 298 | -> IO (Map Day (Map Part a)) 299 | runAll sess yr lock rep cm f = flip M.traverseWithKey cm $ \d -> 300 | M.traverseWithKey $ \p c -> do 301 | let CP{..} = challengePaths yr (CS d p) 302 | inp0 <- rep d p 303 | withColor ANSI.Dull ANSI.Blue $ 304 | printf ">> Day %02d%c\n" (dayInt d) (partChar p) 305 | when lock $ do 306 | CD{..} <- challengeData sess yr (CS d p) 307 | forM_ (inp0 <|> eitherToMaybe _cdInput) $ \inp -> 308 | mapM_ (writeFile _cpAnswer) =<< evaluate (force (runSomeSolution c inp)) 309 | f c inp0 =<< challengeData sess yr (CS d p) 310 | 311 | runTestSuite :: SomeSolution -> ChallengeData -> IO (Maybe Bool) 312 | runTestSuite c CD{..} = do 313 | testRes <- mapMaybe fst <$> mapM (uncurry (testCase True c)) _cdTests 314 | unless (null testRes) $ do 315 | let (mark, color) 316 | | and testRes = ('✓', ANSI.Green) 317 | | otherwise = ('✗', ANSI.Red ) 318 | withColor ANSI.Vivid color $ 319 | printf "[%c] Passed %d out of %d test(s)\n" 320 | mark 321 | (length (filter id testRes)) 322 | (length testRes) 323 | pure $ and testRes <$ guard (not (null testRes)) 324 | 325 | 326 | -- | Run a single test case 327 | testCase 328 | :: Bool -- ^ is just an example 329 | -> SomeSolution 330 | -> String 331 | -> TestMeta 332 | -> IO (Maybe Bool, Either SolutionError String) 333 | testCase emph c inp TM{..} = do 334 | withColor ANSI.Dull color $ 335 | printf "[%c]" mark 336 | if emph 337 | then printf " (%s)\n" resStr 338 | else printf " %s\n" resStr 339 | forM_ showAns $ \a -> 340 | withColor ANSI.Vivid ANSI.Red $ 341 | printf "(Expected: %s)\n" a 342 | return (status, res) 343 | where 344 | res = runSomeSolutionWith _tmData c inp 345 | resStr = case res of 346 | Right r -> r 347 | Left SEParse -> "ERROR: No parse" 348 | Left SESolve -> "ERROR: No solution" 349 | (mark, showAns, status) = case _tmAnswer of 350 | Just (strip->ex) -> case res of 351 | Right (strip->r) 352 | | r == ex -> ('✓', Nothing, Just True ) 353 | | otherwise -> ('✗', Just ex, Just False) 354 | Left _ -> ('✗', Just ex, Just False) 355 | Nothing -> ('?', Nothing, Nothing ) 356 | color = case status of 357 | Just True -> ANSI.Green 358 | Just False -> ANSI.Red 359 | Nothing -> ANSI.Blue 360 | 361 | -- | Do the action with a given ANSI foreground color and intensity. 362 | withColor 363 | :: ANSI.ColorIntensity 364 | -> ANSI.Color 365 | -> IO () 366 | -> IO () 367 | withColor ci c act = do 368 | ANSI.setSGR [ ANSI.SetColor ANSI.Foreground ci c ] 369 | act 370 | ANSI.setSGR [ ANSI.Reset ] 371 | 372 | pullMap 373 | :: Map a (Map b c) 374 | -> Map (a, b) c 375 | pullMap = M.fromDistinctAscList 376 | . concatMap (uncurry go . second M.toAscList) 377 | . M.toAscList 378 | where 379 | go x = (map . first) (x,) 380 | 381 | pushMap 382 | :: Eq a 383 | => Map (a, b) c 384 | -> Map a (Map b c) 385 | pushMap = fmap M.fromDistinctAscList 386 | . M.fromAscListWith (flip (++)) 387 | . map (uncurry go) 388 | . M.toAscList 389 | where 390 | go (x, y) z = (x, [(y, z)]) 391 | -------------------------------------------------------------------------------- /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 | mainSubmit cfg . defaultMSO $ cs 112 | 113 | -- | Result-suppressing version of 'execSolution'. 114 | execSolution_ :: ChallengeSpec -> IO () 115 | execSolution_ = void . execSolution 116 | 117 | -- | Result-suppressing version of 'execSolutionWith'. 118 | execSolutionWith_ 119 | :: ChallengeSpec 120 | -> String -- ^ custom puzzle input 121 | -> IO () 122 | execSolutionWith_ cs = void . execSolutionWith cs 123 | 124 | -- | Result-suppressing version of 'testSolution'. 125 | testSolution_ :: ChallengeSpec -> IO () 126 | testSolution_ = void . testSolution 127 | 128 | -- | Result-suppressing version of 'viewPrompt'. 129 | viewPrompt_ :: ChallengeSpec -> IO () 130 | viewPrompt_ = void . viewPrompt 131 | 132 | -- | Result-suppressing version of 'waitForPrompt'. 133 | waitForPrompt_ :: ChallengeSpec -> IO () 134 | waitForPrompt_ = void . waitForPrompt 135 | 136 | -- | Result-suppressing version of 'submitSolution'. 137 | submitSolution_ :: ChallengeSpec -> IO () 138 | submitSolution_ = void . submitSolution 139 | 140 | -- | Run the parser of a solution, given its 'ChallengeSpec'. 141 | -- 142 | -- @ 143 | -- 'loadParseInput' (solSpec 'day01a) day01a 144 | -- @ 145 | loadParseInput :: ChallengeSpec -> a :~> b -> IO a 146 | loadParseInput cs s = eitherIO $ do 147 | i <- liftIO $ loadInput cs 148 | maybeToEither ["No parse"] $ sParse s i 149 | 150 | -- | Run the parser of a solution on test data, given its 'ChallengeSpec'. 151 | -- 152 | -- @ 153 | -- 'loadParseTests' (solSpec 'day01a) day01a 154 | -- @ 155 | loadParseTests :: ChallengeSpec -> a :~> b -> IO [(Maybe a, TestMeta)] 156 | loadParseTests cs s = (map . first) (sParse s) <$> loadTests cs 157 | 158 | -- | Load input for a given challenge 159 | loadInput :: ChallengeSpec -> IO String 160 | loadInput cs = eitherIO $ do 161 | CD{..} <- liftIO $ do 162 | Cfg{..} <- configFile defConfPath 163 | challengeData _cfgSession _cfgYear cs 164 | liftEither _cdInput 165 | 166 | -- | Load test cases for a given challenge 167 | loadTests :: ChallengeSpec -> IO [(String, TestMeta)] 168 | loadTests cs = do 169 | Cfg{..} <- configFile defConfPath 170 | _cdTests <$> challengeData _cfgSession _cfgYear cs 171 | 172 | -- | Unsafely create a 'ChallengeSpec' from a day number and part. 173 | -- 174 | -- Is undefined if given a day number out of range (1-25). 175 | mkSpec :: Integer -> Part -> ChallengeSpec 176 | mkSpec i = CS (mkDay_ i) 177 | 178 | eitherIO :: ExceptT [String] IO a -> IO a 179 | eitherIO act = runExceptT act >>= \case 180 | Right x -> pure x 181 | Left es -> fail $ unlines es 182 | 183 | -------------------------------------------------------------------------------- /src/AOC/Run/Load.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# LANGUAGE TypeApplications #-} 3 | {-# LANGUAGE TypeFamilies #-} 4 | 5 | -- | 6 | -- Module : AOC.Run.Load 7 | -- Copyright : (c) Justin Le 2021 8 | -- License : BSD3 9 | -- 10 | -- Maintainer : justin@jle.im 11 | -- Stability : experimental 12 | -- Portability : non-portable 13 | -- 14 | -- Loading challenge data and prompts. 15 | -- 16 | 17 | module AOC.Run.Load ( 18 | ChallengePaths(..), challengePaths 19 | , ChallengeData(..), challengeData 20 | , Day(..) 21 | , countdownConsole 22 | , timeToRelease 23 | , showNominalDiffTime 24 | , charPart 25 | , showAoCError 26 | , htmlToMarkdown 27 | , mkDay, mkDay_, dayInt 28 | , TestMeta(..) 29 | -- * Parsers 30 | , parseMeta 31 | , parseTests 32 | -- * Printers 33 | , printMeta 34 | , printTests 35 | ) where 36 | 37 | import AOC.Challenge 38 | import AOC.Util 39 | import AOC.Util.DynoMap 40 | import Advent 41 | import Advent.API 42 | import Control.Applicative 43 | import Control.Concurrent 44 | import Control.DeepSeq 45 | import Control.Exception 46 | import Control.Monad 47 | import Control.Monad.Except 48 | import Data.Bifunctor 49 | import Data.Char 50 | import Data.Dependent.Sum 51 | import Data.Foldable 52 | import Data.Functor.Identity 53 | import Data.Map (Map) 54 | import Data.Maybe 55 | import Data.Text (Text) 56 | import Data.Time hiding (Day) 57 | import Data.Void 58 | import System.Console.ANSI as ANSI 59 | import System.Directory 60 | import System.FilePath 61 | import System.IO 62 | import System.IO.Error 63 | import Text.Printf 64 | import Text.Read (readMaybe) 65 | import qualified Control.Monad.Combinators as MP 66 | import qualified Data.Map as M 67 | import qualified Data.Text as T 68 | import qualified Data.Text.IO as T 69 | import qualified Text.HTML.TagSoup.Tree as H 70 | import qualified Text.Megaparsec as MP 71 | import qualified Text.Megaparsec.Char as MP 72 | import qualified Text.Pandoc as P 73 | 74 | -- | A record of paths corresponding to a specific challenge. 75 | data ChallengePaths = CP { _cpPrompt :: !FilePath 76 | , _cpCodeBlocks :: !FilePath 77 | , _cpInput :: !FilePath 78 | , _cpAnswer :: !FilePath 79 | , _cpTests :: !FilePath 80 | , _cpLog :: !FilePath 81 | } 82 | deriving stock Show 83 | 84 | -- | A record of data (test inputs, answers) corresponding to a specific 85 | -- challenge. 86 | data ChallengeData = CD { _cdPrompt :: !(Either [String] Text) 87 | , _cdCodeBlocks :: !(Either [String] [Text]) 88 | , _cdInput :: !(Either [String] String) 89 | , _cdAnswer :: !(Maybe String) 90 | , _cdTests :: ![(String, TestMeta)] 91 | } 92 | 93 | -- | Generate a 'ChallengePaths' from a specification of a challenge. 94 | challengePaths :: Integer -> ChallengeSpec -> ChallengePaths 95 | challengePaths y (CS d p) = CP 96 | { _cpPrompt = "prompt" printf "%02d%c" d' p' <.> "md" 97 | , _cpCodeBlocks = "data/code-blocks" printf "%02d%c" d' p' <.> "txt" 98 | , _cpInput = "data" printf "%02d" d' <.> "txt" 99 | , _cpAnswer = "data/ans" printf "%02d%c" d' p' <.> "txt" 100 | , _cpTests = "test-data" printf "%04d/%02d%c" y d' p' <.> "txt" 101 | , _cpLog = "logs/submission" printf "%02d%c" d' p' <.> "txt" 102 | } 103 | where 104 | d' = dayInt d 105 | p' = partChar p 106 | 107 | makeChallengeDirs :: ChallengePaths -> IO () 108 | makeChallengeDirs CP{..} = 109 | mapM_ (createDirectoryIfMissing True . takeDirectory) 110 | [_cpPrompt, _cpCodeBlocks, _cpInput, _cpAnswer, _cpTests, _cpLog] 111 | 112 | -- | Load data associated with a challenge from a given specification. 113 | -- Will fetch answers online and cache if required (and if giten a session 114 | -- token). 115 | challengeData 116 | :: Maybe String -- ^ session key 117 | -> Integer -- ^ year 118 | -> ChallengeSpec 119 | -> IO ChallengeData 120 | challengeData sess yr spec = do 121 | makeChallengeDirs ps 122 | inp <- runExceptT . asum $ 123 | [ maybeToEither [printf "Input file not found at %s" _cpInput] 124 | =<< liftIO (readFileMaybe _cpInput) 125 | , fetchInput 126 | ] 127 | blocks <- runExceptT . asum $ 128 | [ maybeToEither [printf "Input file not found at %s" _cpCodeBlocks] 129 | =<< liftIO (fmap (T.splitOn codeBlockSep . T.pack) <$> readFileMaybe _cpCodeBlocks) 130 | , fetchCodeBlocks 131 | ] 132 | prompt <- runExceptT . asum $ 133 | [ maybeToEither [printf "Prompt file not found at %s" _cpPrompt] 134 | =<< liftIO (fmap T.pack <$> readFileMaybe _cpPrompt) 135 | , fetchPrompt 136 | ] 137 | ans <- readFileMaybe _cpAnswer 138 | ts <- readFileMaybe _cpTests >>= \case 139 | Nothing -> pure [] 140 | Just str -> case MP.parse parseTests _cpTests str of 141 | Left e -> [] <$ putStrLn (MP.errorBundlePretty e) 142 | Right r -> pure r 143 | return CD 144 | { _cdPrompt = prompt 145 | , _cdCodeBlocks = blocks 146 | , _cdInput = inp 147 | , _cdAnswer = ans 148 | , _cdTests = ts 149 | } 150 | where 151 | ps@CP{..} = challengePaths yr spec 152 | readFileMaybe :: FilePath -> IO (Maybe String) 153 | readFileMaybe = 154 | (traverse (evaluate . force) . eitherToMaybe =<<) 155 | . tryJust (guard . isDoesNotExistError) 156 | . readFile 157 | fetchInput :: ExceptT [String] IO String 158 | fetchInput = do 159 | s <- maybeToEither ["Session key needed to fetch input"] 160 | sess 161 | let opts = defaultAoCOpts yr s 162 | inp <- liftEither . bimap showAoCError T.unpack 163 | =<< liftIO (runAoC opts a) 164 | liftIO $ writeFile _cpInput inp 165 | pure inp 166 | where 167 | a = AoCInput $ _csDay spec 168 | fetchPrompt :: ExceptT [String] IO Text 169 | fetchPrompt = do 170 | prompts <- liftEither . first showAoCError 171 | =<< liftIO (runAoC opts a) 172 | promptH <- maybeToEither [e] 173 | . M.lookup (_csPart spec) 174 | $ prompts 175 | prompt <- liftEither $ htmlToMarkdown True promptH 176 | liftIO $ T.writeFile _cpPrompt prompt 177 | pure prompt 178 | where 179 | opts = defaultAoCOpts yr $ fold sess 180 | a = AoCPrompt $ _csDay spec 181 | e = case sess of 182 | Just _ -> "Part not yet released" 183 | Nothing -> "Part not yet released, or may require session key" 184 | fetchCodeBlocks :: ExceptT [String] IO [Text] 185 | fetchCodeBlocks = do 186 | prompts <- liftEither . first showAoCError 187 | =<< liftIO (runAoC opts a) 188 | promptH <- maybeToEither [e] 189 | . M.lookup (_csPart spec) 190 | $ prompts 191 | let blocks = pullCodeBlocks promptH 192 | liftIO $ T.writeFile _cpCodeBlocks $ T.intercalate codeBlockSep blocks 193 | pure blocks 194 | where 195 | opts = defaultAoCOpts yr $ fold sess 196 | a = AoCPrompt $ _csDay spec 197 | e = case sess of 198 | Just _ -> "Part not yet released" 199 | Nothing -> "Part not yet released, or may require session key" 200 | codeBlockSep = "\n>>>>>>>>>>>>\n" 201 | 202 | showAoCError :: AoCError -> [String] 203 | showAoCError = \case 204 | AoCClientError e -> [ "Error contacting Advent of Code server to fetch input" 205 | , "Possible invalid session key" 206 | , printf "Server response: %s" (show e) 207 | ] 208 | AoCReleaseError t -> [ "Challenge not yet released!" 209 | , printf "Please wait %s" (showNominalDiffTime t) 210 | ] 211 | AoCThrottleError -> [ "Too many requests at a time. Please slow down." ] 212 | 213 | -- | Pretty-print a 'NominalDiffTime' 214 | showNominalDiffTime :: NominalDiffTime -> String 215 | showNominalDiffTime (round @Double @Int . realToFrac -> rawSecs) = 216 | printf "%02dd %02d:%02d:%02d" days hours mins secs 217 | where 218 | (rawMins , secs ) = rawSecs `divMod` 60 219 | (rawHours, mins ) = rawMins `divMod` 60 220 | (days , hours) = rawHours `divMod` 24 221 | 222 | -- | Run a countdown on the console. 223 | countdownConsole 224 | :: MonadIO m 225 | => Integer -- ^ year of challenge 226 | -> Day -- ^ day to count down to 227 | -> m a -- ^ callback on release 228 | -> m a 229 | countdownConsole yr d = countdownWith yr d 250000 $ \ttr -> liftIO $ do 230 | ANSI.clearFromCursorToScreenEnd 231 | printf "> Day %d release in: %s" (dayInt d) (showNominalDiffTime ttr) 232 | ANSI.setCursorColumn 0 233 | hFlush stdout 234 | 235 | -- | Run a countdown with a given callback on each tick. 236 | countdownWith 237 | :: MonadIO m 238 | => Integer -- ^ year of challenge 239 | -> Day -- ^ day to count down to 240 | -> Int -- ^ interval (milliseconds) 241 | -> (NominalDiffTime -> m ()) -- ^ callback on each tick 242 | -> m a -- ^ callback on release 243 | -> m a 244 | countdownWith yr d delay callback release = go 245 | where 246 | go = do 247 | ttr <- liftIO $ timeToRelease yr d 248 | if ttr <= 0 249 | then release 250 | else do 251 | callback ttr 252 | liftIO $ threadDelay delay 253 | go 254 | 255 | htmlToMarkdown :: Bool -> Text -> Either [String] T.Text 256 | htmlToMarkdown pretty html = first ((:[]) . show) . P.runPure $ do 257 | p <- P.readHtml (P.def { P.readerExtensions = exts }) 258 | html 259 | writer (P.def { P.writerExtensions = exts }) p 260 | where 261 | writer 262 | | pretty = P.writeMarkdown 263 | | otherwise = P.writePlain 264 | exts = P.disableExtension P.Ext_header_attributes 265 | . P.disableExtension P.Ext_smart 266 | $ P.pandocExtensions 267 | 268 | pullCodeBlocks :: Text -> [Text] 269 | pullCodeBlocks = concatMap pullEm . processHTML "code" 270 | where 271 | pullEm :: Text -> [Text] 272 | pullEm str = fmap H.renderTree $ concat cleared : ems 273 | where 274 | (ems, cleared) = traverse go (H.parseTree str) 275 | go br = case br of 276 | H.TagBranch b _ xs 277 | | b == "em" -> ([xs], xs) 278 | _ -> ([], [br]) 279 | 280 | 281 | type Parser = MP.Parsec Void String 282 | 283 | data TestMeta = TM { _tmAnswer :: Maybe String 284 | , _tmData :: Map String (DSum TestType Identity) 285 | } 286 | deriving stock Show 287 | 288 | parseTests :: Parser [(String, TestMeta)] 289 | parseTests = MP.many parseTest <* MP.eof 290 | where 291 | parseTest = do 292 | inp <- MP.manyTill MP.anySingle $ MP.lookAhead (MP.string "\n>>>") 293 | _ <- MP.char '\n' 294 | met <- optional (MP.try parseMeta) MP. "Metadata Block" 295 | pure (inp, fromMaybe (TM Nothing M.empty) met) 296 | 297 | printTests :: [(String, TestMeta)] -> Text 298 | printTests = T.intercalate "\n" . concatMap (\(x, y) -> T.pack x : printMeta y) 299 | 300 | parseMeta :: Parser TestMeta 301 | parseMeta = do 302 | dats <- MP.many (MP.try parseData) MP. "Data Block" 303 | ans <- parseAnswer MP. "Expected Answer" 304 | pure $ TM ans (M.fromList dats) 305 | where 306 | parseAnswer = 307 | MP.string ">>>" *> 308 | asum 309 | [ Just <$> MP.try (MP.space1 *> MP.many (MP.noneOf ['\n']) <* "\n") 310 | , Nothing <$ (MP.space *> "\n") 311 | ] 312 | parseData = do 313 | MP.string ">>>" 314 | sym <- MP.manyTill (MP.try MP.letterChar) (MP.try (MP.char ':')) 315 | val <- MP.manyTill (MP.try MP.alphaNumChar) (MP.try (MP.char ':')) 316 | typ <- MP.many (MP.try MP.letterChar) 317 | MP.space 318 | case toLower <$> typ of 319 | "int" -> maybe (fail "Could not parse metadata value") (pure . (sym,) . (TTInt :=>) . Identity) 320 | . readMaybe 321 | $ val 322 | "string" -> pure (sym, TTString :=> Identity val) 323 | _ -> fail $ "Unrecognized type " ++ typ 324 | 325 | printMeta :: TestMeta -> [Text] 326 | printMeta TM{..} = 327 | map printData (M.toList _tmData) 328 | <> [case _tmAnswer of 329 | Just x -> ">>> " <> T.pack x 330 | Nothing -> ">>>" 331 | ] 332 | where 333 | printData :: (String, DSum TestType Identity) -> Text 334 | printData (sym,dynval) = ">>>" <> T.pack sym <> ":" <> val <> ":" <> typ 335 | where 336 | (val, typ) = case dynval of 337 | TTInt :=> Identity x -> (T.pack (show x), "int") 338 | TTString :=> Identity x -> (T.pack x, "string") 339 | -------------------------------------------------------------------------------- /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-19.33 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.1 42 | - haskell-names-0.9.9 43 | 44 | allow-newer: true 45 | 46 | # Override default flag values for local packages and extra-deps 47 | # flags: {} 48 | 49 | # Extra package databases containing global packages 50 | # extra-package-dbs: [] 51 | 52 | # Control whether we use the GHC we find on the path 53 | # system-ghc: true 54 | # 55 | # Require a specific version of stack, using version ranges 56 | # require-stack-version: -any # Default 57 | # require-stack-version: ">=1.9" 58 | # 59 | # Override the architecture used by stack, especially useful on Windows 60 | # arch: i386 61 | # arch: x86_64 62 | # 63 | # Extra directories used by stack for building 64 | # extra-include-dirs: [/path/to/dir] 65 | # extra-lib-dirs: [/path/to/dir] 66 | # 67 | # Allow a newer minor version of GHC than the snapshot specifies 68 | # compiler-check: newer-minor 69 | -------------------------------------------------------------------------------- /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.7.0@sha256:d16c39e5f149bb046b05adf94ba2c3232a24d21587cb75b3c4c4480f1e270564,2351 9 | pantry-tree: 10 | size: 1185 11 | sha256: 1688c03a530c5eaf1581e1d5104e8481d3b2199ce973eb8599eb64168edc3908 12 | original: 13 | hackage: advent-of-code-api-0.2.7.0 14 | snapshots: 15 | - completed: 16 | size: 532832 17 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/23.yaml 18 | sha256: fbb2a0519008533924c7753bd7164ddd1009f09504eb06674acad6049b46db09 19 | original: lts-16.23 20 | -------------------------------------------------------------------------------- /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 | day${day}a :: _ :~> _ 32 | day${day}a = MkSol 33 | { sParse = Just 34 | , sShow = show 35 | , sSolve = Just 36 | } 37 | 38 | day${day}b :: _ :~> _ 39 | day${day}b = MkSol 40 | { sParse = Just 41 | , sShow = show 42 | , sSolve = Just 43 | } 44 | -------------------------------------------------------------------------------- /template/README.md.template: -------------------------------------------------------------------------------- 1 | Advent of Code ${year} 2 | ===================== 3 | 4 | 7 | 8 | ${other_years} 9 | 10 | ${other_links} 11 | 12 | It's the most wonderful time of the year! 13 | 14 | My [Advent of Code ${year}][aoc] Haskell solutions here, along with an automated 15 | fetching, testing, running environment (powered by the 16 | *[advent-of-code-api][]* library). The interactive development environment and 17 | runner/bench marker/viewer/tester has been pulled out [here][dev], so this is 18 | implemented as "fork" of it with my own solutions and reflections. 19 | 20 | Check out the [reflections][] (with [rss feed][rss]) and [package 21 | haddocks][haddock] --- more info below! 22 | 23 | [aoc]: https://adventofcode.com/${year} 24 | [haddock]: https://${github}.github.io/advent-of-code-${year}/ 25 | [advent-of-code-api]: https://hackage.haskell.org/package/advent-of-code-api 26 | [dev]: https://github.com/${github}/advent-of-code-dev 27 | 28 | [Reflections and Benchmarks][reflections] 29 | ----------------------------------------- 30 | 31 | [Available as RSS Feed][rss] 32 | 33 | [rss]: ${rss} 34 | 35 | ${table} 36 | 37 | "Rendered" links go to haddock source renders for code, with reflections in the 38 | documentation. Haddock source renders have hyperlinked identifiers, 39 | so you can follow any unrecognized identifiers to see where I have defined them 40 | in the library. 41 | 42 | [reflections]: https://github.com/${github}/advent-of-code-${year}/blob/master/reflections.md 43 | 44 | ### `:~>` type 45 | 46 | If you're looking at my actual github solutions, you'll notice that this year 47 | I'm implementing my solutions in terms of a `:~>` record type: 48 | 49 | ```haskell 50 | data a :~> b = MkSol 51 | { sParse :: String -> Maybe a -- ^ parse input into an `a` 52 | , sSolve :: a -> Maybe b -- ^ solve an `a` input to a `b` solution 53 | , sShow :: b -> String -- ^ print out the `b` solution for submission 54 | } 55 | ``` 56 | 57 | An `a :~> b` is a solution to a challenge expecting input of type `a` and 58 | producing answers of type `b`. It also packs in functions to parse a `String` 59 | into an `a`, and functions to show a `b` as a `String` to submit as an answer. 60 | 61 | This helps me mentally separate out parsing, solving, and showing, allowing for 62 | some cleaner code and an easier time planning my solution. 63 | 64 | Such a challenge can be "run" on string inputs by feeding the string into 65 | `sParse`, then `sSolve`, then `sShow`: 66 | 67 | ```haskell 68 | -- | Run a ':~>' on some input, retuning 'Maybe' 69 | runSolution :: Challenge -> String -> Maybe String 70 | runSolution MkSol{..} s = do 71 | x <- sParse s 72 | y <- sSolve x 73 | pure (sShow y) 74 | ``` 75 | 76 | In the actual library, I have `runSolution` return an `Either` so I can debug 77 | which stage the error happened in. 78 | 79 | You might also notice the function `dyno_`, used like `dyno_ "limit" 10000`. This 80 | is how I implement parameters in problems that vary between test data and 81 | actual input. For example, Day 6 involved finding points that had a total 82 | distance of less than 10000, but for the test input, we found the points that 83 | had a total distance of less than 32. So, I have a system that lets me write 84 | `dyno_ "limit" 10000` in my code instead of hard-coding in `10000`. This 85 | `10000` would be replaced by `32` when running with test data (which is parsed 86 | from [this file][7btest]) 87 | 88 | [7btest]: https://github.com/${github}/advent-of-code-2018/blob/master/test-data/06b.txt 89 | 90 | Interactive 91 | ----------- 92 | 93 | The *[AOC.Run.Interactive][interactive]* module has code (powered by 94 | *[advent-of-code-api][]*) for testing your solutions and submitting within 95 | GHCI, so you don't have to re-compile. If you edit your solution programs, they 96 | are automatically updated when you hit `:r` in ghci. 97 | 98 | [interactive]: https://${github}.github.io/advent-of-code-${year}/AOC${year}-Run-Interactive.html 99 | 100 | ```haskell 101 | ghci> execSolution_ $$ solSpec 'day02a -- get answer for challenge based on solution 102 | ghci> testSolution_ $$ solSpec 'day02a -- run solution against test suite 103 | ghci> viewPrompt_ $$ solSpec 'day02a -- view the prompt for a part 104 | ghci> waitForPrompt_ $$ solSpec 'day02a -- count down to the prompt for a part 105 | ghci> submitSolution_ $$ solSpec 'day02a -- submit a solution, and retry after cooldown automatically 106 | ``` 107 | 108 | These are loaded with session key stored in the configuration file (see next 109 | section). 110 | 111 | Executable 112 | ---------- 113 | 114 | Comes with test examples given in problems. 115 | 116 | You can install using `stack`: 117 | 118 | ```bash 119 | $$ git clone https://github.com/${github}/advent-of-code-${year} 120 | $$ cd advent-of-code-${year} 121 | $$ stack setup 122 | $$ stack install 123 | ``` 124 | 125 | The executable `aoc${year}` includes a testing and benchmark suite, as well as a 126 | way to view prompts within the command line: 127 | 128 | ``` 129 | $$ aoc${year} --help 130 | aoc${year} - Advent of Code ${year} challenge runner 131 | 132 | Usage: aoc${year} [-c|--config PATH] COMMAND 133 | Run challenges from Advent of Code ${year}. Available days: 1, 2, 3 (..) 134 | 135 | Available options: 136 | -c,--config PATH Path to configuration file (default: aoc-conf.yaml) 137 | -h,--help Show this help text 138 | 139 | Available commands: 140 | run Run, test, and benchmark challenges 141 | view View a prompt for a given challenge 142 | submit Test and submit answers for challenges 143 | test Alias for run --test 144 | bench Alias for run --bench 145 | countdown Alias for view --countdown 146 | 147 | $$ aoc${year} run 3 b 148 | >> Day 03b 149 | >> [✓] 243 150 | ``` 151 | 152 | You can supply input via stdin with `--stdin`: 153 | 154 | ``` 155 | $$ aoc${year} run 1 --stdin 156 | >> Day 01a 157 | +1 158 | +2 159 | +1 160 | -3 161 | 162 | [?] 1 163 | >> Day 01b 164 | [?] 1 165 | ``` 166 | 167 | Benchmarking is implemented using *criterion* 168 | 169 | ``` 170 | $$ aoc${year} bench 2 171 | >> Day 02a 172 | benchmarking... 173 | time 1.317 ms (1.271 ms .. 1.392 ms) 174 | 0.982 R² (0.966 R² .. 0.999 R²) 175 | mean 1.324 ms (1.298 ms .. 1.373 ms) 176 | std dev 115.5 μs (77.34 μs .. 189.0 μs) 177 | variance introduced by outliers: 65% (severely inflated) 178 | 179 | >> Day 02b 180 | benchmarking... 181 | time 69.61 ms (68.29 ms .. 72.09 ms) 182 | 0.998 R² (0.996 R² .. 1.000 R²) 183 | mean 69.08 ms (68.47 ms .. 69.99 ms) 184 | std dev 1.327 ms (840.8 μs .. 1.835 ms) 185 | ``` 186 | 187 | Test suites run the example problems given in the puzzle description, and 188 | outputs are colorized in ANSI terminals. 189 | 190 | ``` 191 | $$ aoc${year} test 1 192 | >> Day 01a 193 | [✓] (3) 194 | [✓] (3) 195 | [✓] (0) 196 | [✓] (-6) 197 | [✓] Passed 4 out of 4 test(s) 198 | [✓] 416 199 | >> Day 01b 200 | [✓] (2) 201 | [✓] (0) 202 | [✓] (10) 203 | [✓] (5) 204 | [✓] (14) 205 | [✓] Passed 5 out of 5 test(s) 206 | [✓] 56752 207 | ``` 208 | 209 | This should only work if you're running `aoc${year}` in the project directory. 210 | 211 | **To run on actual inputs**, the executable expects inputs to be found in the 212 | folder `data/XX.txt` in the directory you are running in. That is, the input 213 | for Day 7 will be expected at `data/07.txt`. 214 | 215 | *aoc${year} will download missing input files*, but requires a session token. 216 | This can be provided in `aoc-conf.yaml`: 217 | 218 | ```yaml 219 | session: [[ session token goes here ]] 220 | ``` 221 | 222 | Session keys are also required to download "Part 2" prompts for each challenge. 223 | 224 | You can "lock in" your current answers (telling the executable that those are 225 | the correct answers) by passing in `--lock`. This will lock in any final 226 | puzzle solutions encountered as the verified official answers. Later, if you 227 | edit or modify your solutions, they will be checked on the locked-in answers. 228 | 229 | These are stored in `data/ans/XXpart.txt`. That is, the target output for Day 7 230 | (Part 2, `b`) will be expected at `data/ans/07b.txt`. You can also manually 231 | edit these files. 232 | 233 | You can view prompts: (use `--countdown` to count down until a prompt is 234 | released, and display immediately) 235 | 236 | ``` 237 | $$ aoc${year} view 3 b 238 | >> Day 03b 239 | --- Part Two --- 240 | ---------------- 241 | 242 | Amidst the chaos, you notice that exactly one claim doesn't overlap by 243 | even a single square inch of fabric with any other claim. If you can 244 | somehow draw attention to it, maybe the Elves will be able to make 245 | Santa's suit after all! 246 | 247 | For example, in the claims above, only claim `3` is intact after all 248 | claims are made. 249 | 250 | *What is the ID of the only claim that doesn't overlap?* 251 | ``` 252 | 253 | You can also submit answers: 254 | 255 | ``` 256 | $$ aoc${year} submit 1 a 257 | ``` 258 | 259 | Submissions will automatically run the test suite. If any tests fail, you will 260 | be asked to confirm submission or else abort. The submit command will output 261 | the result of your submission: The message from the AoC website, and whether or 262 | not your answer was correct (or invalid or ignored). Answers that are 263 | confirmed correct will be locked in and saved for future testing against, in 264 | case you change your solution. 265 | 266 | All networking features are powered by *[advent-of-code-api][]*. 267 | 268 | ${links} 269 | -------------------------------------------------------------------------------- /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/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 | --------------------------------------------------------------------------------