1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
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
|