advent-2021/4/solution.hs

75 lines
1.8 KiB
Haskell

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 ]