2025-12-09 10:27:50 +00:00
|
|
|
module Puzzles.Day9 (puzzle) where
|
|
|
|
|
|
|
|
|
|
import Pre
|
|
|
|
|
|
|
|
|
|
puzzle :: Puzzle
|
|
|
|
|
puzzle =
|
|
|
|
|
Puzzle
|
|
|
|
|
{ number = 9
|
2025-12-09 13:37:39 +00:00
|
|
|
, parser = const $ (V2 <$> decimal <* single ',' <*> decimal) `sepEndBy1` newline
|
2025-12-09 10:27:50 +00:00
|
|
|
, parts =
|
2025-12-09 16:57:05 +00:00
|
|
|
[ maximum
|
2025-12-09 19:50:35 +00:00
|
|
|
. fmap (squareSize . uncurry Rectangle)
|
2025-12-09 13:34:48 +00:00
|
|
|
. fromMaybe (error "input too small")
|
2025-12-09 10:47:32 +00:00
|
|
|
. nonEmpty
|
2025-12-09 11:00:01 +00:00
|
|
|
. allUnorderedPairs False
|
2025-12-09 13:37:39 +00:00
|
|
|
, \points ->
|
|
|
|
|
let path =
|
|
|
|
|
fromMaybe (error "malformed line")
|
|
|
|
|
. traverse mkLine
|
|
|
|
|
$ (last points', head points') :| adjacentPairs points
|
|
|
|
|
where
|
|
|
|
|
points' = fromMaybe (error "empty input") $ nonEmpty points
|
2025-12-09 16:57:05 +00:00
|
|
|
in snd
|
2025-12-09 13:37:39 +00:00
|
|
|
. fromMaybe (error "no solutions")
|
|
|
|
|
. find (not . flip any path . lineIntersectsSquare . fst)
|
|
|
|
|
. sortOn (Down . snd)
|
2025-12-09 19:50:35 +00:00
|
|
|
. fmap ((id &&& squareSize) . uncurry Rectangle)
|
2025-12-09 13:37:39 +00:00
|
|
|
$ allUnorderedPairs False points
|
2025-12-09 10:27:50 +00:00
|
|
|
]
|
|
|
|
|
, extraTests = mempty
|
|
|
|
|
}
|
2025-12-09 13:37:39 +00:00
|
|
|
|
2025-12-09 19:50:35 +00:00
|
|
|
data Rectangle = Rectangle
|
2025-12-09 13:37:39 +00:00
|
|
|
{ corner1 :: V2 Int
|
|
|
|
|
, corner2 :: V2 Int
|
|
|
|
|
}
|
|
|
|
|
deriving (Show)
|
2025-12-09 19:50:35 +00:00
|
|
|
squareSize :: Rectangle -> Int
|
|
|
|
|
squareSize Rectangle{corner1, corner2} = (\(V2 x y) -> x * y) . (+ 1) . fmap abs $ corner1 - corner2
|
2025-12-09 13:37:39 +00:00
|
|
|
|
|
|
|
|
data Line
|
|
|
|
|
= LineHorizontal {y :: Int, x1 :: Int, x2 :: Int}
|
|
|
|
|
| LineVertical {x :: Int, y1 :: Int, y2 :: Int}
|
|
|
|
|
deriving (Show)
|
|
|
|
|
mkLine :: (V2 Int, V2 Int) -> Maybe Line
|
|
|
|
|
mkLine (V2 x1 y1, V2 x2 y2)
|
|
|
|
|
| y1 == y2 = Just $ LineHorizontal{y = y1, x1, x2}
|
|
|
|
|
| x1 == x2 = Just $ LineVertical{x = x1, y1, y2}
|
|
|
|
|
| otherwise = Nothing
|
|
|
|
|
|
|
|
|
|
data Interval = Interval Int Int deriving (Show)
|
|
|
|
|
compareToInterval :: Int -> Interval -> Ordering
|
|
|
|
|
compareToInterval n (Interval l u)
|
|
|
|
|
| n <= l = LT
|
|
|
|
|
| n >= u = GT
|
|
|
|
|
| otherwise = EQ
|
|
|
|
|
|
2025-12-09 19:50:35 +00:00
|
|
|
squareIntervals :: Rectangle -> V2 Interval
|
|
|
|
|
squareIntervals Rectangle{corner1, corner2} = uncurry Interval . sortPair <$> liftA2 (,) corner1 corner2
|
2025-12-09 13:37:39 +00:00
|
|
|
|
2025-12-09 19:50:35 +00:00
|
|
|
lineIntersectsSquare :: Rectangle -> Line -> Bool
|
2025-12-09 13:37:39 +00:00
|
|
|
lineIntersectsSquare (squareIntervals -> V2 intervalX intervalY) = \case
|
|
|
|
|
LineHorizontal{y, x1, x2} ->
|
|
|
|
|
compareToInterval y intervalY == EQ
|
|
|
|
|
&& compareToInterval x1 intervalX /= compareToInterval x2 intervalX
|
|
|
|
|
LineVertical{x, y1, y2} ->
|
|
|
|
|
compareToInterval x intervalX == EQ
|
|
|
|
|
&& compareToInterval y1 intervalY /= compareToInterval y2 intervalY
|