Initial commit
commit
513641d092
@ -0,0 +1,2 @@
|
|||||||
|
SESSION=YOUR_SESSION_COOKIE
|
||||||
|
YEAR=2022
|
@ -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…
Reference in New Issue