import Aoc import Data.List.Split ( splitOn ) parseFile :: [String] -> [(String, (Int, Int), (Int, Int))] parseFile = map (\line -> let line' = words line in let (command, [pos1, "through", pos2]) = splitAt (length line' - 3) line' in (unwords command, pos pos1, pos pos2)) where pos = pair . map read . splitOn "," doCommands :: (a -> String -> a) -> a -> [(String, (Int, Int), (Int, Int))] -> (Int, Int) -> a doCommands doCommand start commands pos = foldl (doCommand' pos) start commands where within (x0, y0) (x1, y1) (x, y) = x0 <= x && x <= x1 && y0 <= y && y <= y1 doCommand' pos state (command, pos1, pos2) = if within pos1 pos2 pos then doCommand state command else state part1 :: [(String, (Int, Int), (Int, Int))] -> Int part1 commands = length $ filter isOn [(x, y) | x <- [0..999], y <- [0..999]] where doCommand state "turn off" = False doCommand state "turn on" = True doCommand state "toggle" = not state isOn = doCommands doCommand False commands part2 :: [(String, (Int, Int), (Int, Int))] -> Int part2 commands = sum [brightness (x, y) | x <- [0 .. 999], y <- [0 .. 999]] where doCommand state "turn off" = max 0 (state - 1) doCommand state "turn on" = state + 1 doCommand state "toggle" = state + 2 brightness = doCommands doCommand 0 commands main :: IO () main = aocMain parseFile part1 part2