Advent of Code 2021
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
2.3 KiB

module Day4 where
import System.IO (openFile, IOMode (ReadMode), hClose, hGetContents)
import Data.Text (splitOn, unpack, pack)
import Prelude hiding (splitAt)
type Board = [[Int]]
column :: Board -> Int -> [Int]
column board i = map (!! i) board
splitAt :: String -> String -> [String]
splitAt delimiter str = map unpack $ splitOn (pack delimiter) $ pack str
-- remove padding spaces and turn a board line into ints
lineToInts :: String -> [Int]
lineToInts str = map read $ filter (/= "") $ splitAt " " str
isLineWinning :: [Int] -> [Int] -> Bool
isLineWinning nums = all (`elem` nums)
-- in: random numbers, board
-- out: only unmarked numbers in board
unmarkedNums :: [Int] -> Board -> Board
unmarkedNums nums = map (filter (`notElem` nums))
-- in: random numbers, board
-- out: sum of all unmarked numbers
unmarkedSum :: [Int] -> Board -> Int
unmarkedSum nums board = sum . concat $ unmarkedNums nums board
-- in: board, list of random numbers
-- out: true if board won
checkBoard :: Board -> [Int] -> Bool
checkBoard board nums = any (isLineWinning nums) allLines
where
allLines = board ++ map (column board) [0..4]
-- in: list of boards, list of random numbers
-- out: board that won, number of iterations
checkBoards' :: [Board] -> [Int] -> (Board, Int)
checkBoards' [] nums = ([], length nums)
checkBoards' (x:xs) nums
| checkBoard x nums = (x, length nums)
| otherwise = checkBoards' xs nums
-- in: list of boards, list of random numbers
-- out: board that won, number of iterations
-- will error if none of the tables are solvable
checkBoards :: [Board] -> [Int] -> Int -> (Board, Int)
checkBoards boards nums iter
| null $ fst $ checkBoards' boards list = checkBoards boards nums (iter + 1)
| otherwise = checkBoards' boards list
where
list = take iter nums
main :: IO ()
main = do
input <- openFile "inputs/4.txt" ReadMode
contents <- hGetContents input
-- random numbers from the first line
let list = map read $ splitAt "," $ head $ splitAt "\n\n" contents :: [Int]
-- the remaining lines are bingo boards
let bingus = map lines $ tail $ splitAt "\n\n" contents
let boards = map (map lineToInts) bingus
let (board, num) = checkBoards boards list 1
let sum = unmarkedSum (take num list) board
print $ sum * list !! (num - 1)
hClose input