|
5 | 5 | -- Description: <https://adventofcode.com/2024/day/14 Day 14: Restroom Redoubt>
|
6 | 6 | module Day14 (part1, part1', part2) where
|
7 | 7 |
|
8 |
| -import Common (groupConsecutiveBy) |
9 |
| -import Control.Monad (join, liftM2) |
10 |
| -import Control.Parallel.Strategies (parList, rdeepseq, withStrategy) |
11 |
| -import Data.Char (intToDigit) |
12 |
| -import Data.Map qualified as Map (findWithDefault) |
13 |
| -import Data.Map.Strict qualified as Map (fromListWith) |
14 |
| -import Data.Ord (Down (Down)) |
15 |
| -import Data.Set qualified as Set (fromList, toList) |
| 8 | +import Common (crt) |
| 9 | +import Control.Monad (ap) |
| 10 | +import Data.Foldable (foldMap') |
| 11 | +import Data.Semigroup (Arg (Arg), Sum (Sum)) |
16 | 12 | import Data.String (IsString)
|
17 | 13 | import Data.Text (Text)
|
18 | 14 | import Data.Void (Void)
|
19 |
| -import Debug.Trace (traceM) |
20 | 15 | import Text.Megaparsec (MonadParsec, ParseErrorBundle, Stream (Token, Tokens), parse, sepEndBy1)
|
21 | 16 | import Text.Megaparsec.Char (char, newline, string)
|
22 | 17 | import Text.Megaparsec.Char.Lexer qualified as L (decimal, signed)
|
23 | 18 |
|
24 | 19 | parser :: (MonadParsec e s m, IsString (Tokens s), Token s ~ Char, Num a) => m [((a, a), (a, a))]
|
25 | 20 | parser = line `sepEndBy1` newline
|
26 | 21 | where
|
27 |
| - line = (,) <$> (string "p=" *> v2) <*> (string " v=" *> v2) |
28 |
| - v2 = (,) <$> (L.signed (pure ()) L.decimal <* char ',') <*> L.signed (pure ()) L.decimal |
| 22 | + line = do |
| 23 | + x <- string "p=" *> L.signed (pure ()) L.decimal |
| 24 | + y <- char ',' *> L.signed (pure ()) L.decimal |
| 25 | + vx <- string " v=" *> L.signed (pure ()) L.decimal |
| 26 | + vy <- char ',' *> L.signed (pure ()) L.decimal |
| 27 | + pure ((x, vx), (y, vy)) |
29 | 28 |
|
30 | 29 | part1 :: Text -> Either (ParseErrorBundle Text Void) Int
|
31 | 30 | part1 = part1' 101 103
|
32 | 31 |
|
33 | 32 | part1' :: Int -> Int -> Text -> Either (ParseErrorBundle Text Void) Int
|
34 | 33 | part1' width height input = do
|
35 | 34 | robots <- parse parser "" input
|
36 |
| - let totals = |
37 |
| - Map.fromListWith (+) $ |
38 |
| - [ ((compare x $ width `div` 2, compare y $ height `div` 2), 1) |
39 |
| - | ((x0, y0), (vx, vy)) <- robots, |
40 |
| - let x = (x0 + vx * t) `mod` width |
41 |
| - y = (y0 + vy * t) `mod` height |
42 |
| - ] |
43 |
| - pure $ product [Map.findWithDefault 0 k totals | k <- join (liftM2 (,)) [LT, GT]] |
| 35 | + let (Sum q1, Sum q2, Sum q3, Sum q4) = foldMap' f robots |
| 36 | + pure $ q1 * q2 * q3 * q4 |
44 | 37 | where
|
45 | 38 | t = 100
|
| 39 | + f ((x, vx), (y, vy)) = case ( compare ((x + t * vx) `mod` width) (width `div` 2), |
| 40 | + compare ((y + t * vy) `mod` height) (height `div` 2) |
| 41 | + ) of |
| 42 | + (LT, LT) -> (Sum 1, Sum 0, Sum 0, Sum 0) |
| 43 | + (LT, GT) -> (Sum 0, Sum 1, Sum 0, Sum 0) |
| 44 | + (GT, LT) -> (Sum 0, Sum 0, Sum 1, Sum 0) |
| 45 | + (GT, GT) -> (Sum 0, Sum 0, Sum 0, Sum 1) |
| 46 | + _ -> mempty |
46 | 47 |
|
47 | 48 | part2 :: Text -> Either (ParseErrorBundle Text Void) Int
|
48 | 49 | part2 input = do
|
49 |
| - robots <- parse parser "" input |
50 |
| - let (_, bestTime) = |
51 |
| - minimum . withStrategy (parList rdeepseq) $ |
52 |
| - [ (Down $ maximum $ map length verticalLines, t) |
53 |
| - | t <- [0 .. lcm width height - 1], |
54 |
| - let verticalLines = |
55 |
| - groupConsecutiveBy isLine . Set.toList . Set.fromList $ |
56 |
| - [ ((y0 + vy * t) `mod` height, (x0 + vx * t) `mod` width) |
57 |
| - | ((x0, y0), (vx, vy)) <- robots |
58 |
| - ] |
59 |
| - isLine (y0, x0) (y1, x1) = y0 == y1 && x0 + 1 == x1 |
60 |
| - ] |
61 |
| - positions = |
62 |
| - Map.fromListWith (+) $ |
63 |
| - [ (((x0 + vx * bestTime) `mod` width, (y0 + vy * bestTime) `mod` height), 1) |
64 |
| - | ((x0, y0), (vx, vy)) <- robots |
65 |
| - ] |
66 |
| - line y = |
67 |
| - [ case Map.findWithDefault 0 (x, y) positions of |
68 |
| - 0 -> '.' |
69 |
| - n -> if n < 10 then intToDigit n else '+' |
70 |
| - | x <- [0 .. width - 1] |
71 |
| - ] |
72 |
| - mapM_ (traceM . line) [0 .. height - 1] |
73 |
| - pure bestTime |
| 50 | + (xrobots, yrobots) <- unzip <$> parse parser "" input |
| 51 | + let Arg _ x = maximum $ (flip Arg `ap` score xrobots width) <$> [0 .. width - 1] |
| 52 | + Arg _ y = maximum $ (flip Arg `ap` score yrobots height) <$> [0 .. height - 1] |
| 53 | + pure $ fst $ crt (x, width) (y, height) |
74 | 54 | where
|
75 |
| - width = 101 |
76 |
| - height = 103 |
| 55 | + (width, height) = (101, 103) |
| 56 | + score robots m t = max h1 h2 :: Int |
| 57 | + where |
| 58 | + (Sum h1, Sum h2) = foldMap' f robots |
| 59 | + f (p, v) = case compare ((p + t * v) `mod` m) (m `div` 2) of |
| 60 | + LT -> (Sum 1, Sum 0) |
| 61 | + EQ -> (Sum 0, Sum 0) |
| 62 | + GT -> (Sum 0, Sum 1) |
0 commit comments