1 import Data.List.Split (chunksOf)
3 import Text.Printf (printf)
5 import qualified Data.Map.Strict as M
6 import qualified Data.Graph as G
7 -- import Control.Parallel.Strategies (parMap, rpar)
8 type CellMap = M.Map (Int, Int) Bool
10 puzzleKey = "xlqgujun"
14 print $ part1 puzzleKey
15 print $ part2 puzzleKey
18 part1 :: String -> Int
19 part1 key = sum rowCounts
20 where rowCounts = map countSetBits $ binHashes key
23 part2 :: String -> Int
24 part2 key = length $ cellEdges cells
25 where cells = presentCells $ binHashes key
27 binHashes :: String -> [String]
28 binHashes key = map binHash $ rowSpecs key
31 binHash :: String -> String
32 binHash = binify . knotHash
34 numKey :: (Int, Int) -> Int
35 numKey (r, c) = 128 * r + c
38 presentCells :: [String] -> CellMap
39 presentCells bhs = M.fromList [((r, c), True) | r <- [0..127], c <- [0..127], (bhs!!r)!!c == '1']
41 adjacentCells :: CellMap -> (Int, Int) -> [(Int, Int)]
42 adjacentCells cells (r, c) = filter (\k -> M.member k cells) possibles
43 where possibles = [(r, c - 1), (r, c + 1), (r - 1, c), (r + 1, c)]
46 cellEdges :: CellMap -> [G.SCC (Int, Int)]
47 cellEdges cells = G.stronglyConnComp [(k, numKey k, map numKey $ adjacentCells cells k) | k <- M.keys cells]
49 rowSpecs :: String -> [String]
50 rowSpecs key = map (((key ++ "-") ++) . show) ([0..127] :: [Integer])
52 countSetBits :: String -> Int
53 countSetBits = length . filter (== '1')
57 knotHash :: String -> [Int]
58 knotHash input = densify tied
59 where (tied, _, _) = foldl step ([0..255], 0, 0) hashTerms
60 hashTerms = mkHashTerms input
62 step :: ([Int], Int, Int) -> Int -> ([Int], Int, Int)
63 step (original, start, skip) len = (replaced, start', skip + 1)
64 where replaced = tie original start len
65 start' = (start + len + skip) `mod` (length original)
67 tie :: [a] -> Int -> Int -> [a]
68 tie original start len = replace original replacement start
69 where replacement = reverse $ extract original start len
71 extract :: [a] -> Int -> Int -> [a]
72 extract items from len = take len $ drop from $ items ++ items
74 replace :: [a] -> [a] -> Int -> [a]
75 replace original replacement from = take (length original) (start ++ replacement ++ remainder)
76 where excess = drop (length original - from) replacement
77 stub = drop (length excess) original
78 start = take from (excess ++ stub)
79 remainder = drop (length $ start ++ replacement) original
82 mkHashTerms :: String -> [Int]
83 mkHashTerms text = take (length chunk * 64) $ cycle chunk
84 where chunk = map ord text ++ [17, 31, 73, 47, 23]
86 -- hexify :: [Int] -> String
87 -- hexify = concatMap (printf "%02x")
89 binify :: [Int] -> String
90 binify = concatMap (printf "%08b")
92 densify :: [Int] -> [Int]
94 where chunks = chunksOf 16 ns
96 codes = map compress chunks