@@ -1072,33 +1072,37 @@ We can parse our input using `map read . splitOn "," :: String -> [Int]`,
1072
1072
Part 2 is pretty straightforward in that the * logic* is extremely simple, just
1073
1073
do a series of transformations.
1074
1074
1075
+ First we can make the "knot hash" itself:
1075
1076
1076
1077
``` haskell
1077
- day10b :: [Word8 ] -> String
1078
- day10b = toHex . process
1079
- . concat . replicate 64 . (++ salt)
1078
+ knothash :: String -> [Word8 ]
1079
+ knothash = map (foldr xor 0 ) . chunksOf 16 . V. toList . process
1080
+ . concat . replicate 64 . (++ salt)
1081
+ . map (fromIntegral . ord)
1080
1082
where
1081
1083
salt = [17 , 31 , 73 , 47 , 23 ]
1082
- toHex = concatMap (printf " %02x" . foldr xor 0 ) . chunksOf 16 . toList
1083
- strip = T. unpack . T. strip . T. pack
1084
1084
```
1085
1085
1086
1086
We:
1087
1087
1088
- 3 . Append the salt bytes at the end
1089
- 4 . ` concat . replicate 64 :: [a] -> [a] ` , replicate the list of inputs 64 times
1090
- 5 . ` process ` things like how we did in Part 1
1091
- 7 . Convert to hex:
1092
- * Break into chunks of 16 (using ` chunksOf ` from the * [ split] [ ] * library)
1093
- * ` foldr ` each chunk of 16 using ` xor `
1094
- * Convert the resulting ` Int ` to a hex string, using ` printf %02x `
1095
- * Aggregate all of the chunk results later
1088
+ 1 . Append the salt bytes at the end
1089
+ 2 . ` concat . replicate 64 :: [a] -> [a] ` , replicate the list of inputs 64 times
1090
+ 3 . ` process ` things like how we did in Part 1
1091
+ 4 . Break into chunks of 16 (using ` chunksOf ` from the * [ split] [ ] * library)
1092
+ 5 . ` foldr ` each chunk of 16 using ` xor `
1096
1093
1097
- Again, it's not super complicated, it's just that there are so many steps
1098
- described in the puzzle!
1094
+ And our actual ` day10b ` is then just applying this and printing this as hex:
1095
+
1096
+ ``` haskell
1097
+ day10b :: [Word8 ] -> String
1098
+ day10b = concatMap (printf " %02x" ) . knothash
1099
+ ```
1099
1100
1100
- We can parse our input using ` map (fromIntegral . ord) :: String -> [Word8] ` ,
1101
- taking care to also strip any leading and trailing whitespace first.
1101
+ We leverage the ` printf ` formatter from ` Text.Printf ` to generate the hex,
1102
+ being careful to ensure we pad the result.
1103
+
1104
+ Not super complicated, it's just that there are so many steps
1105
+ described in the puzzle!
1102
1106
1103
1107
### Day 10 Benchmarks
1104
1108
@@ -1225,7 +1229,6 @@ instance Monoid Disjoints where
1225
1229
overlaps = S. filter (not . IS. null . (`IS.intersection` z)) zs
1226
1230
disjoints = zs `S.difference` overlaps
1227
1231
newGroup = IS. unions $ z : S. toList overlaps
1228
-
1229
1232
```
1230
1233
1231
1234
The mappend action is union, but preserving disjoint connection property. If
@@ -1368,7 +1371,69 @@ Day 14
1368
1371
1369
1372
[ d14c ] : https://github.com/mstksg/advent-of-code-2017/blob/master/src/AOC2017/Day14.hs
1370
1373
1374
+ Part 1 is a simple application of the "knot hash" function we wrote:
1375
+ different inputs. We can make a row of a grid by running `knothash :: String
1376
+ -> [ Word8] ` on the seed, using ` printf` to format things as a binary string,
1377
+ and then using ` map (== '1') ` to convert our binary string into a list of
1378
+ ` Bool ` s. These represent a list of "on" or "off" cells.
1379
+
1380
+ ``` haskell
1381
+ mkRow :: String -> Int -> [Bool ]
1382
+ mkRow seed n = map (== ' 1' ) . concatMap (printf " %08b" ) . knothash
1383
+ $ seed ++ " -" ++ show n
1384
+ ```
1385
+
1386
+ Our grid is then just running this function for every row, to get a grid of
1387
+ on/off cells:
1388
+
1389
+ ``` haskell
1390
+ mkGrid :: String -> [[Bool ]]
1391
+ mkGrid seed = map (mkRow seed) [0 .. 127 ]
1392
+ ```
1393
+
1394
+ The actual challenge is then just counting all of the ` True ` s:
1395
+
1396
+ ``` haskell
1397
+ day14a :: String -> Int
1398
+ day14a = length . filter id . concat . mkGrid
1399
+ ```
1400
+
1401
+ For Part 2, we can actually re-use the same ` Disjoints ` monoid that we used for
1402
+ Day 12. We'll just add in sets of neighboring lit points, and count how many
1403
+ disjoint sets come out at the end.
1371
1404
1405
+ We're going to leverage ` Data.Ix ` , to let us enumerate over all cells in a grid
1406
+ with ` range :: ((Int, Int), (Int, Int)) -> [(Int, Int)] ` . ` Data.Ix ` also gives
1407
+ us ` index :: (Int, Int) -> Int ` , which allows us to "encode" a coordinate as an
1408
+ ` Int ` , so we can use it with the ` IntSet ` that we wrote earlier.
1409
+
1410
+ ``` haskell
1411
+ litGroups :: [[Bool ]] -> Disjoints
1412
+ litGroups grid = foldMap go (range r)
1413
+ where
1414
+ r = ((0 ,0 ),(127 ,127 ))
1415
+ isLit (x,y) = grid !! y !! x
1416
+ go p | isLit p = D . S. singleton . IS. fromList
1417
+ . map (index r) . (p: ) . filter isLit
1418
+ $ neighbors p
1419
+ | otherwise = mempty
1420
+
1421
+ neighbors :: (Int , Int ) -> [(Int , Int )]
1422
+ neighbors (x,y) = [ (x+ dx, y+ dy) | (dx, dy) <- [(0 ,1 ),(0 ,- 1 ),(1 ,0 ),(- 1 ,0 )]
1423
+ , x+ dx >= 0
1424
+ , y+ dy >= 0
1425
+ , x+ dx < 128
1426
+ , y+ dy < 128
1427
+ ]
1428
+ ```
1429
+
1430
+ So part 2 is just running ` litGroups ` and counting the resulting number of
1431
+ disjoint groups:
1432
+
1433
+ ``` haskell
1434
+ day14b :: String -> Int
1435
+ day14b = S. size . getD . litGroups . mkGrid
1436
+ ```
1372
1437
1373
1438
### Day 14 Benchmarks
1374
1439
0 commit comments