-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLib.hs
82 lines (66 loc) · 2.91 KB
/
Lib.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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