Initial commit

main
Hamothy 2 years ago committed by GitHub
commit 513641d092
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
SESSION=YOUR_SESSION_COOKIE
YEAR=2022

4
.gitignore vendored

@ -0,0 +1,4 @@
dist*
.env
.inputs/
data/

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2022, IndecisionTree
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,95 @@
cabal-version: 3.0
name: advent-of-haskell
version: 0.1.0.0
synopsis: Framework for AoC
description: Run, submit, test, and benchmark Advent of Code solutions.
license: BSD-3-Clause
license-file: LICENSE
author: IndecisionTree
build-type: Simple
common common
default-language: GHC2021
ghc-options: -Wall
default-extensions: RecordWildCards
executable runner
import: common
main-is: Main.hs
build-depends:
, aoc
, base ^>=4.16.3.0
, containers ^>=0.6.5
, directory ^>=1.3.6
, dotenv ^>=0.9.0
, filepath ^>=1.4.2
, megaparsec ^>=9.3.0
, optparse-applicative ^>=0.17.0
, solutions
, tasty ^>=1.4.2
, tasty-bench ^>=0.3.2
, tasty-hunit ^>=0.10.0
, text ^>=2.0
other-modules: Tests
hs-source-dirs: runner
library aoc
import: common
exposed-modules: AOC
other-modules:
AOC.API
AOC.Types
build-depends:
, base ^>=4.16.3.0
, bytestring ^>=0.11.3
, http-api-data ^>=0.5
, http-client ^>=0.7.13
, http-client-tls ^>=0.3.6
, http-media ^>=0.8.0
, servant ^>=0.19.1
, servant-client ^>=0.19
, text ^>=2.0
, time ^>=1.11.1
hs-source-dirs: aoc
library solutions
import: common
-- cabal-fmt: expand solutions
exposed-modules:
Days.Day01
Days.Day02
Days.Day03
Days.Day04
Days.Day05
Days.Day06
Days.Day07
Days.Day08
Days.Day09
Days.Day10
Days.Day11
Days.Day12
Days.Day13
Days.Day14
Days.Day15
Days.Day16
Days.Day17
Days.Day18
Days.Day19
Days.Day20
Days.Day21
Days.Day22
Days.Day23
Days.Day24
Solutions
build-depends:
, aoc
, base ^>=4.16.3.0
, containers ^>=0.6.5
, text ^>=2.0
hs-source-dirs: solutions

@ -0,0 +1,7 @@
module AOC (
module AOC.Types,
mkAocClient
) where
import AOC.Types
import AOC.API

@ -0,0 +1,101 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
module AOC.API (
mkAocClient,
) where
import AOC.Types
import Control.Monad ((>=>))
import Data.ByteString qualified as BS
import Data.Proxy (Proxy (..))
import Data.Text qualified as T
import Data.Text.Encoding qualified as T
import Data.Time.Clock (addUTCTime, getCurrentTime)
import GHC.Conc (atomically, newTVar)
import Network.HTTP.Client (Cookie (..), createCookieJar)
import Network.HTTP.Client.TLS (newTlsManager)
import Network.HTTP.Media ((//), (/:))
import Servant.API
import Servant.Client hiding ((//), (/:))
mkAocClient :: String -> Int -> Int -> IO (IO T.Text, Submission -> IO Answer)
mkAocClient session year day = do
env <- mkAocEnv session
let aocInput :<|> aocSubmit = aocClient year day
f = flip runClientM env >=> pure . either (error . show) id
aocInput' = f aocInput
aocSubmit' = f . aocSubmit
return (aocInput', aocSubmit')
mkAocEnv :: String -> IO ClientEnv
mkAocEnv session = do
current <- getCurrentTime
manager <- newTlsManager
let
base = BaseUrl Https "adventofcode.com" 443 ""
env = mkClientEnv manager base
year = 60 * 60 * 24 * 365
expiry = addUTCTime year current
cookie =
Cookie
{ cookie_name = "session",
cookie_value = T.encodeUtf8 . T.pack $ session,
cookie_expiry_time = expiry,
cookie_domain = T.encodeUtf8 . T.pack $ baseUrlHost base,
cookie_path = "/",
cookie_creation_time = current,
cookie_last_access_time = current,
cookie_persistent = True,
cookie_host_only = True,
cookie_secure_only = True,
cookie_http_only = True
}
cookies <- atomically $ newTVar (createCookieJar [cookie])
return $ env {cookieJar = Just cookies}
aocClient :: Int -> Int -> (ClientM T.Text :<|> (Submission -> ClientM Answer))
aocClient = client (Proxy @API)
type API =
Capture "year" Int :>
("day" :> Capture "day" Int :>
(
-- GET /:year/day/:day/input
("input" :> Get '[RawPlainText] T.Text) :<|>
-- POST /:year/day/:day/answer level=<1|2>&answer=_
("answer" :> ReqBody '[FormUrlEncoded] Submission :> Post '[HTML] Answer)
)
)
-- This is silly: https://github.com/haskell-servant/servant/issues/1002
data RawPlainText
instance Accept RawPlainText where
contentType _ = "text" // "plain"
instance MimeUnrender RawPlainText T.Text where
mimeUnrender _ = Right . T.decodeUtf8 . BS.toStrict
-- TODO implement marshalling from HTML to 'Day', 'Year', and 'Answer'
-- types which extracts yearly calendars, day prompts, and
-- pre-existing correct answer submissions via scraping the received
-- HTML.
data HTML
instance Accept HTML where
contentType _ = "text" // "html" /: ("charset", "utf-8")
data Answer = Correct | Low | High | Empty
instance MimeUnrender HTML Answer where
mimeUnrender _ bs
| "correct" `BS.isInfixOf` bs' = Right Correct
| "low" `BS.isInfixOf` bs' = Right Low
| "high" `BS.isInfixOf` bs' = Right High
| "provide an answer" `BS.isInfixOf` bs' = Right Empty
| otherwise = Left "Unknown answer response"
where
bs' = BS.toStrict bs

@ -0,0 +1,19 @@
{-# language GADTs #-}
module AOC.Types (
Solution (..),
Submission (..)
) where
import Data.Text (Text)
import GHC.Generics (Generic)
import Web.Internal.FormUrlEncoded (ToForm)
data Solution where
Solution :: Show b => (Text -> a) -> (a -> b) -> (a -> b) -> Solution
data Submission = Submission {
part :: Int,
answer :: String
} deriving (Generic)
instance ToForm Submission

@ -0,0 +1,3 @@
with-compiler: ghc-9.2.4
packages: .

@ -0,0 +1,112 @@
module Main where
import AOC (Solution (..), mkAocClient)
import Configuration.Dotenv (defaultConfig, loadFile)
import Control.Exception (IOException, catch)
import Control.Monad (when)
import Data.Functor (void)
import Data.IntMap.Strict qualified as M
import Data.Text qualified as T
import Data.Text.IO qualified as T
import Options.Applicative
import Solutions (solutions)
import System.Directory (createDirectoryIfMissing)
import System.Environment (getEnv)
import Tests (test)
import Text.Printf (printf)
-- | CLI Options
data Options = Option
{ _command :: Action,
_day :: Int,
_part :: Maybe Int
}
-- | CLI Commands
data Action = Run | Test | Bench | Submit
deriving (Show)
main :: IO ()
main = do
void $ loadFile defaultConfig
session <- getEnv "SESSION"
year <- read <$> getEnv "YEAR"
Option {..} <- customExecParser (prefs showHelpOnEmpty) opts
when (_day < 1 || _day > 24) $ do
fail $ printf "Day '%d' is out of range (1-24)" _day
(aocInput, aocSubmit) <- mkAocClient session year _day
input <- getPuzzleInput _day aocInput
let day = solutions M.! _day
case _command of
Run -> putStr $ run day _part input
Test -> test day _day _part
a -> fail $ "Unimplemented: " ++ show a
-- | Retrieve puzzle input for a given day from a file. If no file is
-- found, hit the api.
getPuzzleInput :: Int -> IO T.Text -> IO T.Text
getPuzzleInput day aocInput =
T.readFile fp `catch` \(_ :: IOException) -> fetchInput
where
fp = printf ".inputs/%d.txt" day
fetchInput = do
input <- aocInput
createDirectoryIfMissing True ".inputs"
T.writeFile fp input
return input
-- | Run solution (optionally just part) on input and return the
-- result as a string ready for submission
run :: Solution -> Maybe Int -> T.Text -> String
run (Solution pInput part1 part2) part input =
case part of
Nothing -> printf "Part 1: %s\nPart 2: %s\n" part1' part2'
Just n -> printf "Part %d: %s\n" n $ if n == 1 then part1' else part2'
where
parsed = pInput input
part1' = show $ part1 parsed
part2' = show $ part2 parsed
-- | CLI parser
opts :: ParserInfo Options
opts =
info
(commands <**> helper)
( fullDesc
<> progDesc "Run, benchmark, test, or submit an AOC day"
<> header "runner - an AOC solution runner"
)
where
commands = subparser $ runCmd <> testCmd <> benchCmd <> submitCmd
runCmd =
mkCmd
"run"
(Option Run <$> day <*> optional part)
"Run a given day, optionally specifying which part"
testCmd =
mkCmd
"test"
(Option Test <$> day <*> optional part)
"Test a given day, optionally specifying which part"
benchCmd =
mkCmd
"bench"
(Option Bench <$> day <*> optional part)
"Benchmark a given day, optionally specifying which part"
submitCmd =
mkCmd
"submit"
(Option Submit <$> day <*> (Just <$> part))
"Run and submit the answer to a given day and part"
day = argument auto (metavar "DAY")
part = argument auto (metavar "PART")
mkCmd cmd f desc = command cmd (info f (progDesc desc))

@ -0,0 +1,77 @@
{-# LANGUAGE OverloadedStrings #-}
module Tests (test) where
import AOC (Solution (..))
import Data.Text qualified as T
import Data.Text.IO qualified as T
import Data.Void (Void)
import System.FilePath ((<.>), (</>))
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, (@=?))
import Test.Tasty.Ingredients (tryIngredients)
import Test.Tasty.Ingredients.Basic (consoleTestReporter)
import Text.Megaparsec
import Text.Megaparsec.Char (newline, space)
import Text.Megaparsec.Char.Lexer qualified as L
data TestInput = TestInput
{ _testName :: T.Text,
_testInput :: T.Text,
_testExpected :: T.Text
}
deriving (Show)
type Parser = Parsec Void T.Text
-- | Test solution via tests in 'data/examples/(Day #)/part{1,2}.txt'
test :: Solution -> Int -> Maybe Int -> IO ()
test (Solution pInput part1 part2) day part = do
p1 <- T.readFile $ "data" </> "examples" </> show day </> "part1" <.> "txt"
p2 <- T.readFile $ "data" </> "examples" </> show day </> "part2" <.> "txt"
let p1Tests =
testGroup "Part 1"
. fmap (mkTest pInput part1)
. either (error . errorBundlePretty) id
. parse pTests "part1.txt"
$ p1
p2Tests =
testGroup "Part 2"
. fmap (mkTest pInput part2)
. either (error . errorBundlePretty) id
. parse pTests "part2.txt"
$ p2
tests = testGroup ("Day " ++ show day) $ case part of
Nothing -> [p1Tests, p2Tests]
Just 1 -> [p1Tests]
_ -> [p2Tests]
case tryIngredients [consoleTestReporter] mempty tests of
Nothing -> error $ "Error running tests for day " ++ show day
Just act -> do
res <- act
putStrLn $ if res then "Success" else "Failure"
-- | Given an input parser, part1 or part2 function, and a test input,
-- generate an HUnit test.
mkTest :: Show b => (T.Text -> a) -> (a -> b) -> TestInput -> TestTree
mkTest pInput part TestInput {..} =
testCase (T.unpack _testName) $ T.unpack _testExpected @=? result
where
result = show . part $ pInput _testInput
-- | Parse test files into TestInput's
pTests :: Parser [TestInput]
pTests = many pTest <* eof
where
pTest = TestInput <$> pName <*> pInput <*> pExpected <?> "Test"
pName = T.pack <$> (symbol "-" *> lexeme (some (anySingleBut '\n'))) <?> "Test Name"
pInput = T.pack <$> someTill anySingle (symbol "==") <?> "Input Lines"
pExpected = T.pack <$> many (anySingleBut '\n') <* newline <?> "Expected Output"
lexeme :: Parser a -> Parser a
lexeme = L.lexeme space
symbol :: T.Text -> Parser T.Text
symbol = L.symbol space

@ -0,0 +1,29 @@
#!/usr/bin/env bash
solutions="solutions/Days"
cd "$(git rev-parse --show-toplevel)"
mkdir "$solutions"
for i in $(printf "%02i " {1..24})
do
cat << EOF > "$solutions/Day$i.hs"
module Days.Day$i (day$i) where
import AOC (Solution (..))
import qualified Data.Text as T
day$i :: Solution
day$i = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day $i"
part1 :: a -> Int
part1 = error "part1 not defined for day $i"
part2 :: a -> Int
part2 = error "part2 not defined for day $i"
EOF
done

@ -0,0 +1,16 @@
module Days.Day01 (day01) where
import AOC (Solution (..))
import qualified Data.Text as T
day01 :: Solution
day01 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 01"
part1 :: a -> Int
part1 = error "part1 not defined for day 01"
part2 :: a -> Int
part2 = error "part2 not defined for day 01"

@ -0,0 +1,16 @@
module Days.Day02 (day02) where
import AOC (Solution (..))
import qualified Data.Text as T
day02 :: Solution
day02 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 02"
part1 :: a -> Int
part1 = error "part1 not defined for day 02"
part2 :: a -> Int
part2 = error "part2 not defined for day 02"

@ -0,0 +1,16 @@
module Days.Day03 (day03) where
import AOC (Solution (..))
import qualified Data.Text as T
day03 :: Solution
day03 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 03"
part1 :: a -> Int
part1 = error "part1 not defined for day 03"
part2 :: a -> Int
part2 = error "part2 not defined for day 03"

@ -0,0 +1,16 @@
module Days.Day04 (day04) where
import AOC (Solution (..))
import qualified Data.Text as T
day04 :: Solution
day04 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 04"
part1 :: a -> Int
part1 = error "part1 not defined for day 04"
part2 :: a -> Int
part2 = error "part2 not defined for day 04"

@ -0,0 +1,16 @@
module Days.Day05 (day05) where
import AOC (Solution (..))
import qualified Data.Text as T
day05 :: Solution
day05 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 05"
part1 :: a -> Int
part1 = error "part1 not defined for day 05"
part2 :: a -> Int
part2 = error "part2 not defined for day 05"

@ -0,0 +1,16 @@
module Days.Day06 (day06) where
import AOC (Solution (..))
import qualified Data.Text as T
day06 :: Solution
day06 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 06"
part1 :: a -> Int
part1 = error "part1 not defined for day 06"
part2 :: a -> Int
part2 = error "part2 not defined for day 06"

@ -0,0 +1,16 @@
module Days.Day07 (day07) where
import AOC (Solution (..))
import qualified Data.Text as T
day07 :: Solution
day07 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 07"
part1 :: a -> Int
part1 = error "part1 not defined for day 07"
part2 :: a -> Int
part2 = error "part2 not defined for day 07"

@ -0,0 +1,16 @@
module Days.Day08 (day08) where
import AOC (Solution (..))
import qualified Data.Text as T
day08 :: Solution
day08 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 08"
part1 :: a -> Int
part1 = error "part1 not defined for day 08"
part2 :: a -> Int
part2 = error "part2 not defined for day 08"

@ -0,0 +1,16 @@
module Days.Day09 (day09) where
import AOC (Solution (..))
import qualified Data.Text as T
day09 :: Solution
day09 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 09"
part1 :: a -> Int
part1 = error "part1 not defined for day 09"
part2 :: a -> Int
part2 = error "part2 not defined for day 09"

@ -0,0 +1,16 @@
module Days.Day10 (day10) where
import AOC (Solution (..))
import qualified Data.Text as T
day10 :: Solution
day10 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 10"
part1 :: a -> Int
part1 = error "part1 not defined for day 10"
part2 :: a -> Int
part2 = error "part2 not defined for day 10"

@ -0,0 +1,16 @@
module Days.Day11 (day11) where
import AOC (Solution (..))
import qualified Data.Text as T
day11 :: Solution
day11 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 11"
part1 :: a -> Int
part1 = error "part1 not defined for day 11"
part2 :: a -> Int
part2 = error "part2 not defined for day 11"

@ -0,0 +1,16 @@
module Days.Day12 (day12) where
import AOC (Solution (..))
import qualified Data.Text as T
day12 :: Solution
day12 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 12"
part1 :: a -> Int
part1 = error "part1 not defined for day 12"
part2 :: a -> Int
part2 = error "part2 not defined for day 12"

@ -0,0 +1,16 @@
module Days.Day13 (day13) where
import AOC (Solution (..))
import qualified Data.Text as T
day13 :: Solution
day13 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 13"
part1 :: a -> Int
part1 = error "part1 not defined for day 13"
part2 :: a -> Int
part2 = error "part2 not defined for day 13"

@ -0,0 +1,16 @@
module Days.Day14 (day14) where
import AOC (Solution (..))
import qualified Data.Text as T
day14 :: Solution
day14 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 14"
part1 :: a -> Int
part1 = error "part1 not defined for day 14"
part2 :: a -> Int
part2 = error "part2 not defined for day 14"

@ -0,0 +1,16 @@
module Days.Day15 (day15) where
import AOC (Solution (..))
import qualified Data.Text as T
day15 :: Solution
day15 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 15"
part1 :: a -> Int
part1 = error "part1 not defined for day 15"
part2 :: a -> Int
part2 = error "part2 not defined for day 15"

@ -0,0 +1,16 @@
module Days.Day16 (day16) where
import AOC (Solution (..))
import qualified Data.Text as T
day16 :: Solution
day16 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 16"
part1 :: a -> Int
part1 = error "part1 not defined for day 16"
part2 :: a -> Int
part2 = error "part2 not defined for day 16"

@ -0,0 +1,16 @@
module Days.Day17 (day17) where
import AOC (Solution (..))
import qualified Data.Text as T
day17 :: Solution
day17 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 17"
part1 :: a -> Int
part1 = error "part1 not defined for day 17"
part2 :: a -> Int
part2 = error "part2 not defined for day 17"

@ -0,0 +1,16 @@
module Days.Day18 (day18) where
import AOC (Solution (..))
import qualified Data.Text as T
day18 :: Solution
day18 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 18"
part1 :: a -> Int
part1 = error "part1 not defined for day 18"
part2 :: a -> Int
part2 = error "part2 not defined for day 18"

@ -0,0 +1,16 @@
module Days.Day19 (day19) where
import AOC (Solution (..))
import qualified Data.Text as T
day19 :: Solution
day19 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 19"
part1 :: a -> Int
part1 = error "part1 not defined for day 19"
part2 :: a -> Int
part2 = error "part2 not defined for day 19"

@ -0,0 +1,16 @@
module Days.Day20 (day20) where
import AOC (Solution (..))
import qualified Data.Text as T
day20 :: Solution
day20 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 20"
part1 :: a -> Int
part1 = error "part1 not defined for day 20"
part2 :: a -> Int
part2 = error "part2 not defined for day 20"

@ -0,0 +1,16 @@
module Days.Day21 (day21) where
import AOC (Solution (..))
import qualified Data.Text as T
day21 :: Solution
day21 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 21"
part1 :: a -> Int
part1 = error "part1 not defined for day 21"
part2 :: a -> Int
part2 = error "part2 not defined for day 21"

@ -0,0 +1,16 @@
module Days.Day22 (day22) where
import AOC (Solution (..))
import qualified Data.Text as T
day22 :: Solution
day22 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 22"
part1 :: a -> Int
part1 = error "part1 not defined for day 22"
part2 :: a -> Int
part2 = error "part2 not defined for day 22"

@ -0,0 +1,16 @@
module Days.Day23 (day23) where
import AOC (Solution (..))
import qualified Data.Text as T
day23 :: Solution
day23 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 23"
part1 :: a -> Int
part1 = error "part1 not defined for day 23"
part2 :: a -> Int
part2 = error "part2 not defined for day 23"

@ -0,0 +1,16 @@
module Days.Day24 (day24) where
import AOC (Solution (..))
import qualified Data.Text as T
day24 :: Solution
day24 = Solution parseInput part1 part2
parseInput :: T.Text -> a
parseInput = error "parseInput not defined for day 24"
part1 :: a -> Int
part1 = error "part1 not defined for day 24"
part2 :: a -> Int
part2 = error "part2 not defined for day 24"

@ -0,0 +1,56 @@
module Solutions (solutions) where
import qualified Data.IntMap.Strict as M
import AOC (Solution)
import Days.Day01 (day01)
import Days.Day02 (day02)
import Days.Day03 (day03)
import Days.Day04 (day04)
import Days.Day05 (day05)
import Days.Day06 (day06)
import Days.Day07 (day07)
import Days.Day08 (day08)
import Days.Day09 (day09)
import Days.Day10 (day10)
import Days.Day11 (day11)
import Days.Day12 (day12)
import Days.Day13 (day13)
import Days.Day14 (day14)
import Days.Day15 (day15)
import Days.Day16 (day16)
import Days.Day17 (day17)
import Days.Day18 (day18)
import Days.Day19 (day19)
import Days.Day20 (day20)
import Days.Day21 (day21)
import Days.Day22 (day22)
import Days.Day23 (day23)
import Days.Day24 (day24)
solutions :: M.IntMap Solution
solutions = M.fromList [
(1, day01),
(2, day02),
(3, day03),
(4, day04),
(5, day05),
(6, day06),
(7, day07),
(8, day08),
(9, day09),
(10, day10),
(11, day11),
(12, day12),
(13, day13),
(14, day14),
(15, day15),
(16, day16),
(17, day17),
(18, day18),
(19, day19),
(20, day20),
(21, day21),
(22, day22),
(23, day23),
(24, day24) ]
Loading…
Cancel
Save