import Data.List import Data.Char -- Задача 1 subtr :: String -> String -> String subtr a b = filter (\c -> not (c `elem` b)) a hasAllCharsIn :: String -> String -> Bool hasAllCharsIn a b = all (\c -> c `elem` b) a decode :: String -> Int decode f = sum (map (\l -> decodeLine l) (lines f)) decodeLine :: String -> Int decodeLine i = read $ parseNumbers (getSegments $ sortBy (\s1 s2 -> compare (length s1) (length s2)) (take 10 (words i))) (drop 11 (words i)) where -- След като написах огромна имплементация, която намира всеки сегмент какъв му е знака, забелязах че -- може да използваме само "групи" сегменти, за да различим всички цифри с еднакви дължини. -- Тоест, вземаме двойките сегменти bd, eg и cf (наименования от "работещия" циферблат), като не се интересуваме от тяхната наредба, -- и казваме че двойка се съдържа в дадена цифра, ако и двете букви се съдържат в цифрата (тоест, ако има само b, но не d, значи не съдържа bd). -- -- За 1, 4, 7, 8 не разглеждаме това свойство, понеже тях ги различаваме по дължина. -- При 5 сегментни цифри, забелязваме че 2 съдържа eg, 5 съдържа bd, 3 не съдържа нищо (и трите съдържат cf, но това не ни интересува) -- При 6 сегментни цифри, забелязваме че 6 съдържа bd и eg, 9 съдържа bd и cf, 0 съдържа eg и cf getSegments nums = [subtr (nums!!2) (nums!!0), -- сегменти b и d subtr (subtr (nums!!9) (nums!!1)) (nums!!2), -- сегменти e и g nums!!0] -- сегменти c и f parseNumbers _ [] = [] parseNumbers seg parse = (parseDigit seg (head parse)) : parseNumbers seg (tail parse) where parseDigit seg d | length d == 2 = '1' | length d == 3 = '7' | length d == 4 = '4' | length d == 7 = '8' | length d == 5 = if (seg!!0) `hasAllCharsIn` d then '5' -- ако bd се съдържа else if (seg!!1) `hasAllCharsIn` d then '2' -- ако eg се съдържа else '3' | otherwise = if not ((seg!!0) `hasAllCharsIn` d) then '0' -- ако bd не се съдържа (съдържа eg и cf) else if (seg!!2) `hasAllCharsIn` d then '9' -- ако cf се съдържа (и bd се съдържа) else '6' -- Задача 2 toBitString :: Char -> String toBitString c = calc (digitToInt c) 8 where calc :: Int -> Int -> String calc _ 0 = [] calc n p | n - p >= 0 = '1' : (calc (n - p) (p `div` 2)) | otherwise = '0' : (calc n (p `div` 2)) binaryToInt :: String -> Int binaryToInt b = calc b 0 where calc [] acc = acc calc b acc | (head b) == '1' = calc (tail b) (acc + 2^((length b) - 1)) | otherwise = calc (tail b) acc versionSum :: String -> Int versionSum i = calc (concat (map toBitString i)) 0 where calc [] vs = vs calc p vs | length p < 11 = vs -- Пакет-литерал | (take 3 (drop 3 p)) == "100" = calc (drop (6 + literalLength (drop 6 p) 0) p) (vs + binaryToInt (take 3 p)) -- Пакет-оператор | otherwise = if p!!6 == '0' then calc (drop 22 p) (vs + binaryToInt (take 3 p)) else calc (drop 18 p) (vs + binaryToInt (take 3 p)) literalLength li acc | head li == '0' = acc + 5 | otherwise = literalLength (drop 5 li) (acc + 5) -- main main :: IO() main = do sample <- readFile "sample.txt" print(decode sample) -- -> 61229 input <- readFile "input.txt" print(decode input) -- -> 983026 print(versionSum "8A004A801A8002F478") -- -> 16 print(versionSum "620080001611562C8802118E34") -- -> 12 print(versionSum "C0015000016115A2E0802F182340") -- -> 23 print(versionSum "A0016C880162017C3686B18A3D4780") -- -> 31