From de0a2639992898f7baa9ce45b8c2457fb48c1924 Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Mon, 2 Dec 2024 00:40:52 -0500 Subject: [PATCH] Day 2: Red-Nosed Reports --- README.md | 1 + hs/aoc2024.cabal | 2 ++ hs/app/Main.hs | 2 ++ hs/bench/Main.hs | 7 +++++++ hs/src/Day2.hs | 26 ++++++++++++++++++++++++++ hs/test/Day2Spec.hs | 28 ++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+) create mode 100644 hs/src/Day2.hs create mode 100644 hs/test/Day2Spec.hs diff --git a/README.md b/README.md index 8aa70bc..c09ec1e 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,4 @@ Development occurs in language-specific directories: |[Haskell](hs) ![Haskell CI](https://github.com/ephemient/aoc2024/workflows/Haskell%20CI/badge.svg)|[Kotlin](kt) ![Kotlin CI](https://github.com/ephemient/aoc2024/workflows/Kotlin%20CI/badge.svg)|[Python](py) ![Python CI](https://github.com/ephemient/aoc2024/workflows/Python%20CI/badge.svg)|[Rust](rs) ![Rust CI](https://github.com/ephemient/aoc2024/workflows/Rust%20CI/badge.svg)| |--:|--:|--:|--:| |[Day1.hs](hs/src/Day1.hs)|[Day1.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day1.kt)|[day1.py](py/aoc2024/day1.py)|[day1.rs](rs/src/day1.rs)| +|[Day2.hs](hs/src/Day2.hs)|||| diff --git a/hs/aoc2024.cabal b/hs/aoc2024.cabal index c40790c..61960fc 100644 --- a/hs/aoc2024.cabal +++ b/hs/aoc2024.cabal @@ -22,6 +22,7 @@ library hs-source-dirs: src exposed-modules: Day1 + Day2 other-modules: Common @@ -57,6 +58,7 @@ test-suite aoc2024-test main-is: Main.hs other-modules: Day1Spec + Day2Spec build-depends: aoc2024, diff --git a/hs/app/Main.hs b/hs/app/Main.hs index f5fa2f6..e515839 100644 --- a/hs/app/Main.hs +++ b/hs/app/Main.hs @@ -8,6 +8,7 @@ import Data.Maybe (fromMaybe) import Data.Text (Text) import Data.Text.IO qualified as TIO (readFile) import Day1 qualified (part1, part2) +import Day2 qualified (part1, part2) import System.Environment (getArgs, lookupEnv) import System.FilePath (combine) @@ -31,3 +32,4 @@ run' day name showIO funcs = do main :: IO () main = do run 1 (either fail print) [Day1.part1, Day1.part2] + run 2 (either fail print) [Day2.part1, Day2.part2] diff --git a/hs/bench/Main.hs b/hs/bench/Main.hs index 49ef457..c5b3d66 100644 --- a/hs/bench/Main.hs +++ b/hs/bench/Main.hs @@ -7,6 +7,7 @@ import Data.Maybe (fromMaybe) import Data.Text (Text) import Data.Text.IO qualified as TIO (readFile) import Day1 qualified (part1, part2) +import Day2 qualified (part1, part2) import System.Environment.Blank (getEnv, setEnv, unsetEnv) import System.FilePath (combine) @@ -29,5 +30,11 @@ main = "Day 1" [ bench "part 1" $ nf Day1.part1 input, bench "part 2" $ nf Day1.part2 input + ], + env (getDayInput 2) $ \input -> + bgroup + "Day 2" + [ bench "part 1" $ nf Day2.part1 input, + bench "part 2" $ nf Day2.part2 input ] ] diff --git a/hs/src/Day2.hs b/hs/src/Day2.hs new file mode 100644 index 0000000..69b58c4 --- /dev/null +++ b/hs/src/Day2.hs @@ -0,0 +1,26 @@ +-- | +-- Module: Day2 +-- Description: +module Day2 (part1, part2) where + +import Common (readEntire) +import Data.Ix (inRange) +import Data.List (inits, tails) +import Data.Text (Text) +import Data.Text qualified as T (lines, words) +import Data.Text.Read qualified as T (decimal) + +parse :: Text -> Either String [[Int]] +parse = mapM (mapM (readEntire T.decimal) . T.words) . T.lines + +isSafe, isSafe' :: [Int] -> Bool +isSafe report = all (inRange (-3, -1)) delta || all (inRange (1, 3)) delta + where + delta = zipWith (-) report $ drop 1 report +isSafe' report = any isSafe [a ++ b | (a, _ : b) <- zip (inits report) (tails report)] + +part1 :: Text -> Either String Int +part1 input = length . filter isSafe <$> parse input + +part2 :: Text -> Either String Int +part2 input = length . filter isSafe' <$> parse input diff --git a/hs/test/Day2Spec.hs b/hs/test/Day2Spec.hs new file mode 100644 index 0000000..408158c --- /dev/null +++ b/hs/test/Day2Spec.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Day2Spec (spec) where + +import Data.Text (Text) +import Data.Text qualified as T (unlines) +import Day2 (part1, part2) +import Test.Hspec (Spec, describe, it, shouldBe) + +example :: Text +example = + T.unlines + [ "7 6 4 2 1", + "1 2 7 8 9", + "9 7 6 2 1", + "1 3 2 4 5", + "8 6 4 4 1", + "1 3 6 7 9" + ] + +spec :: Spec +spec = do + describe "part 1" $ do + it "examples" $ do + part1 example `shouldBe` Right 2 + describe "part 2" $ do + it "examples" $ do + part2 example `shouldBe` Right 4