module Day9 import Prelude import Node import Aoc parsePoint : String → Maybe Point parsePoint text = case split text "," of a :: b :: Nil => Just (stringToInt a, stringToInt b) _ => Nothing parse : String → List Point parse text = mapMaybe parsePoint $ split (trim text) "\n" abs : Int → Int abs x = if x < 0 then 0 - x else x data Line : U where VL : (x y1 y2 : Int) → Line HL : (y x1 x2 : Int) → Line instance Show Line where show (VL x y1 y2) = "VL \{show (x,y1,y2)}" show (HL x y1 y2) = "HL \{show (x,y1,y2)}" data Box = B Int Int Int Int instance Show Box where show (B l r t b) = "Box \{show (l,r,t,b)}" area : Box → Int area (B l r t b) = (abs (l - r) + 1) * (abs (t - b) + 1) mkbox : Point → Point → Box mkbox (a,b) (c,d) = B (min a c) (max a c) (min b d) (max b d) boxes : List Point → List Box boxes pts = go pts Nil where go2 : Point → List Point → List Box → List Box go2 pt (x :: xs) acc = go2 pt xs (mkbox pt x :: acc) go2 pt _ acc = acc go : List Point → List Box → List Box go (pt :: rest) acc = go rest $ go2 pt rest acc go Nil acc = acc -- line intersects middle of a box isect : Box → Line → Bool isect (B l r t b) (VL x y1 y2) = x < r && l < x && y1 < b && t < y2 isect (B l r t b) (HL y x1 x2) = y < b && t < y && x1 < r && l < x2 getLines : List Point → List Line getLines points = go points Nil where go : List Point → List Line → List Line go ((a,b) :: rest@((c,d) :: _)) acc = if a == c then let seg = if b < d then VL a b d else VL a d b in go rest (seg :: acc) else if b == d then let seg = if a < c then HL b a c else HL b c a in go rest (seg :: acc) else Nil go _ acc = acc -- I'm assuming the winner isn't a single row/column part2 : List (Int × Box) → List Line → Int part2 Nil _ = 0 part2 ((size, box) :: rest) lines = if checkRec box then size else part2 rest lines where winds : Box → Line → Bool winds (B l r t b) (VL x y1 y2) = -- pick a point in the middle, the rest is connected because no intersection if t == b then False else r <= x && y1 < t + 1 && t + 1 < y2 winds (B l r t b) (HL x y1 y2) = False checkRec : Box → Bool checkRec box = let (Nothing) = find (isect box) lines | _ => False in let winding = length' $ filter (winds box) lines in mod winding 2 == 1 run : String -> IO Unit run fn = do putStrLn fn text <- readFile fn let (pts@(a :: _)) = parse text | _ => putStrLn "empty input" -- printLn pts let sortBoxes = qsort (\ a b => fst a > fst b) $ map (\box => (area box, box)) $ boxes pts let ((p1,_) :: _ ) = sortBoxes | _ => printLn "no boxes" putStrLn $ "part1 \{show p1}" let vl = getLines $ a :: reverse pts putStrLn $ "part2 " ++ show (part2 sortBoxes vl) main : IO Unit main = do run "aoc2025/day9/eg.txt" run "aoc2025/day9/input.txt"