80 lines
2.3 KiB
Haskell
80 lines
2.3 KiB
Haskell
import Control.Exception (assert)
|
|
import Data.Bits (bit)
|
|
import Data.Char (digitToInt)
|
|
import Data.List (group, intersect, maximumBy, minimumBy,
|
|
sort, transpose)
|
|
import Data.Ord (comparing)
|
|
|
|
main :: IO ()
|
|
main = do
|
|
diagnostics <- getContents
|
|
putStrLn (solution1 diagnostics)
|
|
putStrLn (solution2 diagnostics)
|
|
|
|
solution1 :: String -> String
|
|
solution1 diagnostics = show (calcGamma * calcEpsilon)
|
|
where
|
|
calcGamma = listToBit (map most (transpose (parseDiagnostics diagnostics)))
|
|
calcEpsilon = listToBit (map least (transpose (parseDiagnostics diagnostics)))
|
|
|
|
solution2 :: String -> String
|
|
solution2 diagnostics = show (calcOxygen * calcCO2)
|
|
where
|
|
calcOxygen = listToBit (head (filterForLife most (parseDiagnostics diagnostics)))
|
|
calcCO2 = listToBit (head (filterForLife least (parseDiagnostics diagnostics)))
|
|
|
|
parseDiagnostics :: String -> [[Int]]
|
|
parseDiagnostics diagnostics = map (map digitToInt) (lines diagnostics)
|
|
|
|
enumerate :: [a] -> [(Int, a)]
|
|
enumerate = zip [0..]
|
|
|
|
most :: [Int] -> Int
|
|
most = head . (maximumBy (comparing length)) . group . sort
|
|
|
|
least :: [Int] -> Int
|
|
least = head . (minimumBy (comparing length)) . group . sort
|
|
|
|
listToBit :: [Int] -> Int
|
|
listToBit list = foldr (\b acc -> (acc + toBit b)) 0 (enumerate (reverse list))
|
|
where
|
|
-- Takes a location and value to converts it to the appropriate
|
|
-- integer.
|
|
toBit :: (Int, Int) -> Int
|
|
toBit (i, b) = if b == 1
|
|
then bit i
|
|
else 0
|
|
|
|
filterForLife :: ([Int] -> Int) -> [[Int]] -> [[Int]]
|
|
filterForLife targetFilter list = foldl (filterIndex targetFilter) list [0..length(head list)-1]
|
|
|
|
filterIndex :: ([Int] -> Int) -> [[Int]] -> Int -> [[Int]]
|
|
filterIndex targetFilter list index = filter ((==filterTarget) . (!! index)) list
|
|
where
|
|
filterTarget :: Int
|
|
filterTarget = targetFilter ((transpose list) !! index)
|
|
|
|
-- Tests
|
|
|
|
testInput1 :: String
|
|
testInput1 = unlines [
|
|
"00100",
|
|
"11110",
|
|
"10110",
|
|
"10111",
|
|
"10101",
|
|
"01111",
|
|
"00111",
|
|
"11100",
|
|
"10000",
|
|
"11001",
|
|
"00010",
|
|
"01010"
|
|
]
|
|
|
|
test1 :: String
|
|
test1 = assert ((solution1 testInput1) == "198") "success"
|
|
|
|
test2 :: String
|
|
test2 = assert ((solution2 testInput1) == "230") "success"
|