import Data.List import Debug.Trace main :: IO () main = readFile "4/input.txt" >>= print . solve . parse . lines type Input = ([Int], [Board]) parse :: [String] -> Input parse (l : ls) = (n, b) where n = map read (words [ if c == ',' then ' ' else c | c <- l ]) b = parseBoards (filter (/= "") ls) solve :: Input -> (Int, Int) solve input = (f input, g input) where f = uncurry winBingo g = uncurry loseBingo parseBoards :: [String] -> [Board] parseBoards [] = [] parseBoards list = Board (map (map (Unmarked . read) . words) a) : parseBoards b where (a, b) = splitAt 5 list newtype Board = Board [[Cell]] deriving Show data Cell = Unmarked Int | Marked Int deriving Show hasWon :: Board -> Bool hasWon (Board b) = completed b || completed (transpose b) where completed = any (all isMarked) isMarked :: Cell -> Bool isMarked (Marked _) = True isMarked _ = False winBingo :: [Int] -> [Board] -> Int winBingo (n : ns) (b : bs) = if any hasWon r then score n $ head (filter hasWon r) else winBingo ns r where r = iter n (b : bs) loseBingo :: [Int] -> [Board] -> Int loseBingo (n : ns) [board] | hasWon b = score n b | otherwise = loseBingo ns [b] where [b] = iter n [board] loseBingo (n : ns) (b : bs) = loseBingo ns (filter (not . hasWon) r) where r = iter n (b : bs) iter :: Int -> [Board] -> [Board] iter n (b : bs) = callNumber n b : iter n bs iter _ [] = [] callNumber :: Int -> Board -> Board callNumber n (Board b) = Board (map (map mark) b) where mark (Unmarked x) | x == n = Marked x mark v = v score :: Int -> Board -> Int score n (Board b) = n * sum [ x | c <- b, Unmarked x <- c ]