Here is my attempt at Advent of Code Day 4 using Haskell. A file contains list of number in first line. And subsequent lines have the bingo Cards
7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 22 13 17 11 0 8 2 23 4 24 21 9 14 16 7 6 10 3 18 5 1 12 20 15 19 3 15 0 2 22 9 18 13 17 5 19 8 7 25 23 20 11 10 24 4 14 21 16 12 6
A number(first line is the list of numbers that are drawn) is drawn at a time and is marked on the bingo Card.
Problem 1: find the Winning bingo Card.
Problem 2: Find the last Winning bingo Card.
Answer can be found by taking the winning bingo card. Adding all the numbers that were not drawn and multiplying with the last number drawn. I tried to add comments to each code block.
module Main where import qualified Data.List as List import qualified Data.Text as T import qualified Data.Set as Set -- | Tuple of cell. Contains the -- Parameter 1 is the number on the bingoboard -- Parameter 2 is the bool value if the number is drawn -- Parameter 3 is the Maybe Int of drawn number type Cell = (Int, Bool, Maybe Int) type BingoBoard = [[Cell]] newline = T.pack ("\n") -- | Parses the first line of the file into list of int parse::T.Text -> [Int] parse arg0 = map (read::String-> Int) $ map (T.unpack) $ T.splitOn (T.pack ",") arg0 -- | Parses the bingo board. This takes list of lines -- and parses them into bingo board -- 22 13 17 11 0 -- 8 2 23 4 24 -- 21 9 14 16 7 -- 6 10 3 18 5 -- 1 12 20 15 19 -- 3 15 0 2 22 -- 9 18 13 17 5 -- 19 8 7 25 23 -- 20 11 10 24 4 -- 14 21 16 12 6 parsebingos ::[T.Text] -> [BingoBoard] parsebingos value = map parsebingo value where parsebingo::T.Text -> BingoBoard parsebingo arg0 = parseStringToInt (map words (map (T.unpack) (T.splitOn newline arg0))) parseStringToInt::[[String]] -> BingoBoard parseStringToInt = fmap (map cellParse) cellParse :: String -> Cell cellParse value = ((read::String -> Int) value, False, Nothing) -- | Solution 1: find the first winner -- findWinner takes the list of List of Bingo boards. And tries to find the winner. -- if any row or column's number matches the numbers that are drawn would be the winner. findWinner::[[BingoBoard]] -> BingoBoard findWinner  =  findWinner (x:xs) = case findWinnerHelper x of Just v -> v _ -> findWinner xs -- | Helper function to go through each bingo board to find the winner findWinnerHelper :: [BingoBoard] -> Maybe BingoBoard findWinnerHelper  = Nothing findWinnerHelper (g:gs) = case isWinner g of True -> Just g _ -> findWinnerHelper gs -- | Solution 2 : To find the last board that wins the bingo. takes list of Bingo Boards solve2 :: [[BingoBoard]] -> BingoBoard solve2 arg0 = findWinner2 ((Set.empty), ) arg0 -- | findWinner2 takes a tuple of Set and BingoBoard accumulator -- If the BingoBoard is not in the Set it tries to find the winner -- if the BingoBoard is a winner it adds to accumulator and the set -- first board in the accumulator is the Last winning -- The idea here is to discard all the BingoBoard which already have won findWinner2::((Set.Set String), [BingoBoard]) -> [[BingoBoard]] -> BingoBoard -- parameter 1 is accumulator for bingoboard String and bingoBoard findWinner2 (_, (x:xs))  = x -- Answer is the first element of list Set is ignored findWinner2 arg0 (x:xss) = findWinner2 (findwinner arg0 x) xss where findwinner::(Set.Set String, [BingoBoard]) -> [BingoBoard] -> (Set.Set String, [BingoBoard]) findwinner acc  = acc findwinner (set, acc) (g:gs) = case (Set.notMember f set) && (isWinner g) of True -> findwinner (Set.insert (makereadableBingoBoard "" g) set, (g : acc)) gs False -> findwinner (set, acc) gs where f :: String f = makereadableBingoBoard "" g -- | makes the string out of the bingo board number to put in the set makereadableBingoBoard::String -> BingoBoard -> String makereadableBingoBoard boardStr  = boardStr makereadableBingoBoard boardStr (x:xs) = makereadableBingoBoard (boardStr ++ makereadableCells "" x) xs where makereadableCells::String -> [Cell] -> String makereadableCells acc  = acc makereadableCells acc ((val, _, _):gs) = makereadableCells (acc ++ show val) gs -- | isWinner checks to see if the BingoBoard is the winner -- It does it by checking all the rows. -- Then transposing the BingoBoard and checking all the rows isWinner::BingoBoard -> Bool isWinner arg0 = (List.any (== True) $ map isRowWinner arg0) || (List.any (== True) $ map isRowWinner $ List.transpose arg0) -- | isRowWinner checks if all the Cells in the row are winner isRowWinner::[Cell] -> Bool isRowWinner arg0 = List.all (== True) (map cell arg0) where cell::Cell -> Bool cell (_, True, _) = True cell _ = False -- | findAnswer Sums all the number that are marked False & multiplies it by the last drawn number findAnswer ::BingoBoard -> Int findAnswer value@(x:xs) = (*) (findbaseValue x) (findFalseCell 0 value) where findFalseCell::Int -> BingoBoard -> Int findFalseCell acc  = acc findFalseCell acc (x:xs) = findFalseCell (sum' acc x) xs sum'::Int -> [Cell] -> Int sum' acc  = acc sum' acc ((a, False, _): gs) = sum' (acc + a) gs sum' acc ((_, True, _): gs) = sum' acc gs findbaseValue ::[Cell] -> Int findbaseValue  = 0 findbaseValue [(_,_, Just v)] = v findbaseValue ((_,_,Nothing): xs) = 0 findbaseValue ((_,_, Just v): xs) = v -- | This creates the infinite list of Bingo boards for all the numbers that are drawn drawBingo ::[Int] -> [BingoBoard] -> [[BingoBoard]] drawBingo  _ =  drawBingo (x:xs) bingoBoards = k: drawBingo xs k where k = updateBingoBoards x bingoBoards -- | Creates Bingoboards for each number drawn updateBingoBoards ::Int -> [BingoBoard] -> [BingoBoard] updateBingoBoards draw boards = map (\x -> updateBingoBoard draw x) boards -- | Matches each cell of the bingo board if it is found makes the Cell value true -- and all the cell with number drawn updateBingoBoard :: Int -> BingoBoard -> BingoBoard updateBingoBoard arg0 arg1 = fmap (map flipcell) arg1 where flipcell ::Cell -> Cell flipcell value@(a, b, _) | a == arg0 && b == False = (a, True, Just arg0) | otherwise = (a, b, Just arg0) main::IO() main = do content <- readFile "input.txt" let input = T.splitOn (T.pack "\n\n") $ T.pack content let draws = parse (head input) let bingoboards = parsebingos (tail input) let game = drawBingo draws bingoboards let updated = findAnswer $ findWinner $ game print updated let winner2 = findAnswer $ solve2 game print winner2 ```