82 lines
2.9 KiB
Haskell
82 lines
2.9 KiB
Haskell
module Lib (aoc, listToTuple, fork) where
|
|
|
|
import Control.Applicative
|
|
import Control.Monad
|
|
import Data.Bool
|
|
import Data.Functor
|
|
import qualified Data.Text
|
|
import System.Environment
|
|
import System.Exit
|
|
|
|
aoc :: (Print b, Print c) => Int -> Int -> (String -> a) -> (a -> b) -> (a -> c) -> [String] -> IO ()
|
|
aoc year day setup part1 part2 examples = do
|
|
let run f = f year day setup part1 part2
|
|
args <- getArgs
|
|
if args == ["test"]
|
|
then run test examples >>= bool exitFailure exitSuccess
|
|
else run main >> exitSuccess
|
|
|
|
main :: (Print b, Print c) => Int -> Int -> (String -> a) -> (a -> b) -> (a -> c) -> IO ()
|
|
main year day setup part1 part2 = do
|
|
args <- getArgs
|
|
let path = if length args == 1 then head args else inputPath year day
|
|
input <- readFile path <&> setup
|
|
(putStrLn . toString . part1) input
|
|
(putStrLn . toString . part2) input
|
|
|
|
test :: (Print b, Print c) => Int -> Int -> (String -> a) -> (a -> b) -> (a -> c) -> [String] -> IO Bool
|
|
test year day setup part1 part2 examples = do
|
|
let inp = inputPath year day
|
|
let out = outputPath year day
|
|
a <- testInput "test_input_part1" inp (out 1) setup part1
|
|
b <- testInput "test_input_part2" inp (out 2) setup part2
|
|
cs <- sequence [testExample year day ex setup part1 part2 | ex <- examples]
|
|
pure $ a && b && and cs
|
|
|
|
testExample :: (Print b, Print c) => Int -> Int -> String -> (String -> a) -> (a -> b) -> (a -> c) -> IO Bool
|
|
testExample year day ex setup part1 part2 = do
|
|
let inp = exampleInputPath year day ex
|
|
let out = exampleOutputPath year day ex
|
|
a <- testInput ("test_ex" ++ ex ++ "_part1") inp (out 1) setup part1
|
|
b <- testInput ("test_ex" ++ ex ++ "_part2") inp (out 2) setup part2
|
|
pure $ a && b
|
|
|
|
testInput :: (Print b) => String -> String -> String -> (String -> a) -> (a -> b) -> IO Bool
|
|
testInput name inputPath outputPath setup solve = do
|
|
actual <- readFile inputPath <&> strip . toString . solve . setup
|
|
expected <- readFile outputPath <&> strip
|
|
let ok = actual == expected
|
|
if ok
|
|
then putStrLn $ name ++ ": ok"
|
|
else putStrLn $ name ++ ": fail: " ++ show actual ++ " /= " ++ show expected
|
|
pure ok
|
|
|
|
inputPath :: Int -> Int -> String
|
|
inputPath year day = "../.cache/" ++ show year ++ "/" ++ show day
|
|
|
|
outputPath :: Int -> Int -> Int -> String
|
|
outputPath year day part = inputPath year day ++ "." ++ show part
|
|
|
|
exampleInputPath :: Int -> Int -> String -> String
|
|
exampleInputPath year day ex = "../examples/" ++ show year ++ "/" ++ show day ++ "/" ++ ex
|
|
|
|
exampleOutputPath :: Int -> Int -> String -> Int -> String
|
|
exampleOutputPath year day ex part = exampleInputPath year day ex ++ "." ++ show part
|
|
|
|
strip :: String -> String
|
|
strip = Data.Text.unpack . Data.Text.strip . Data.Text.pack
|
|
|
|
listToTuple :: [a] -> (a, a)
|
|
listToTuple = fork (curry id) head last
|
|
|
|
fork :: (b -> c -> d) -> (a -> b) -> (a -> c) -> a -> d
|
|
fork = liftA2
|
|
|
|
class Print a where
|
|
toString :: a -> String
|
|
|
|
instance Print String where
|
|
toString = id
|
|
|
|
instance Print Int where
|
|
toString = show
|