diff --git a/aoc2023.cabal b/aoc2023.cabal index 8cb70dd..98c59a7 100644 --- a/aoc2023.cabal +++ b/aoc2023.cabal @@ -382,6 +382,8 @@ executable day23 executable day24 main-is: Day24.hs + other-modules: + Util.Parser hs-source-dirs: src ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -main-is Day24 @@ -464,6 +466,7 @@ test-suite spec TestDay21 TestDay22 TestDay23 + TestDay24 Paths_aoc2023 autogen-modules: Paths_aoc2023 diff --git a/package.yaml b/package.yaml index 877993b..77d5740 100644 --- a/package.yaml +++ b/package.yaml @@ -150,7 +150,7 @@ executables: day24: source-dirs: src main: Day24 - other-modules: [] + other-modules: [Util.Parser] day25: source-dirs: src diff --git a/src/Day24.hs b/src/Day24.hs index aba8d49..04ee56e 100644 --- a/src/Day24.hs +++ b/src/Day24.hs @@ -1,7 +1,66 @@ module Day24 (main, part1, part2) where +import Util.Parser (Parser, parse) +import qualified Text.Parsec as P + +type Vec2 = (Double, Double) +type Vec3 = (Double, Double, Double) +type Particle2D = (Vec2, Vec2) +type Particle3D = (Vec3, Vec3) + +dropZ :: Particle3D -> Particle2D +dropZ ((x, y, _), (dx, dy, _)) = ((x, y), (dx, dy)) + +negP :: Parser Double +negP = negate . read <$> (P.char '-' *> P.many1 P.digit) + +posP :: Parser Double +posP = read <$> P.many1 P.digit + +numP :: Parser Double +numP = negP P.<|> posP + +vec3P :: Parser Vec3 +vec3P = do + x <- numP <* P.char ',' <* P.spaces + y <- numP <* P.char ',' <* P.spaces + z <- numP + return (x, y, z) + +particleP :: Parser Particle3D +particleP = do + p <- vec3P <* P.spaces <* P.char '@' <* P.spaces + v <- vec3P + return (p, v) + +inputP :: Parser [Particle3D] +inputP = P.many1 (particleP <* P.spaces) <* P.eof + +border :: (Num a, Ord a) => a -> Bool +border x = 200000000000000 <= x && x <= 400000000000000 + +det :: Vec2 -> Vec2 -> Double +det (x1, y1) (x2, y2) = x1 * y2 - x2 * y1 + +intersect2D :: Particle2D -> Particle2D -> Bool +intersect2D ((x1, y1), (dx1, dy1)) ((x2, y2), (dx2, dy2)) = + let xd = (-dx1, -dx2) + yd = (-dy1, -dy2) + dv = det xd yd + d = (det (x1, y1) (x1 + dx1, y1 + dy1), det (x2, y2) (x2 + dx2, y2 + dy2)) + x = det d xd / dv + y = det d yd / dv + in + dv /= 0 && + (x - x1 > 0) == (dx1 > 0) && (y - y1 > 0) == (dy1 > 0) && + (x - x2 > 0) == (dx2 > 0) && (y - y2 > 0) == (dy2 > 0) && + border x && border y + +solution :: [Particle2D] -> Int +solution particles = length $ filter (uncurry intersect2D) [(p1, p2) | p1 <- particles, p2 <- particles, p1 /= p2] + part1 :: String -> String -part1 = id +part1 = show . solution . map dropZ . parse inputP part2 :: String -> String part2 = const "" diff --git a/test/Main.hs b/test/Main.hs index 5f1052b..ef5aa44 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -23,6 +23,7 @@ import TestDay20 import TestDay21 import TestDay22 import TestDay23 +import TestDay24 main :: IO () main = do @@ -49,3 +50,4 @@ main = do TestDay21.test TestDay22.test TestDay23.test + TestDay24.test diff --git a/test/TestDay24.hs b/test/TestDay24.hs new file mode 100644 index 0000000..f8ac348 --- /dev/null +++ b/test/TestDay24.hs @@ -0,0 +1,23 @@ +module TestDay24 (test) where + +import Day24 +import Test.Hspec + +input :: String +input = "19, 13, 30 @ -2, 1, -2\n18, 19, 22 @ -1, -1, -2\n20, 25, 34 @ -2, -2, -4\n12, 31, 28 @ -1, -2, -1\n20, 19, 15 @ 1, -5, -3" + +test1 :: Expectation +test1 = part1 input `shouldBe` "2" + +test2 :: Expectation +test2 = part2 input `shouldBe` "" + +test :: IO () +test = hspec $ do + describe "day24" $ do + describe "part1" $ do + it "should work for the examples" test1 + + describe "part2" $ do + it "should work for the examples" test2 +