Summary Files Commits Branches @commit @diff @stats
path: root / Year 2 / Functional Programming / hw4.hs
blob: db7bf96a345666279d378263c9d71c6ddeac9574 (plain) (blame)
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