@@ -1462,6 +1462,66 @@ Day 15
1462
1462
1463
1463
[ d15c ] : https://github.com/mstksg/advent-of-code-2017/blob/master/src/AOC2017/Day15.hs
1464
1464
1465
+ This one is a really "easy" one from a Haskell perspective. We can just
1466
+ generate the outputs of each stream as an infinite lazily linked list, take the
1467
+ number of items we need, and count the pairs that match a specific predicate.
1468
+
1469
+ In particular, the predicate we care about is whether or not two items have the
1470
+ same final 16 bits. This is the same as checking if two integers have value
1471
+ when converted to ` Word16 ` 's (16-bit words).
1472
+
1473
+ The generating function, given a "factor" and a "seed", is:
1474
+
1475
+ ``` haskell
1476
+ generate :: Int -> Int -> Int
1477
+ generate fac = (`mod` 2147483647 ) . (* fac)
1478
+ ```
1479
+
1480
+ We can then just generate them infinitely (using ` iterate ` and an initial
1481
+ seed), zip the two streams together, take the first 40000000 items, filter for
1482
+ the ones where the two items match, and count the length of the resulting list.
1483
+
1484
+ ``` haskell
1485
+ match :: Int -> Int -> Bool
1486
+ match = (==) @ Word16 `on` fromIntegral
1487
+
1488
+ day15a :: Int -> Int -> Int
1489
+ day15a seedA seedB = length
1490
+ . filter (uncurry match)
1491
+ . take 4e7
1492
+ $ zip (iterate (generate 16807 ) seedA)
1493
+ (iterate (generate 48271 ) seedB)
1494
+ ```
1495
+
1496
+ Part 2 is pretty much the same thing, except we filter for things that are
1497
+ divisible by 4 in the first list, and things that are divisible by 8 in the
1498
+ second list. To gain the "asynchronous" behavior that the problem is asking
1499
+ for, we have to do this on the lists before they are zipped. That way, all
1500
+ ` zip ` ever sees (and pairs) are the pre-filtered lists.
1501
+
1502
+ ``` haskell
1503
+ divBy :: Int -> Int -> Bool
1504
+ x `divBy` b = x `mod` b == 0
1505
+
1506
+ day15b :: Int -> Int -> Int
1507
+ day15b seedA seedB = length
1508
+ . filter (uncurry match)
1509
+ . take 5e6
1510
+ $ zip (filter (`divBy` 4 ) . iterate (generate 16807 ) $ seedA)
1511
+ (filter (`divBy` 8 ) . iterate (generate 48271 ) $ seedB)
1512
+ ```
1513
+
1514
+ All in all a very nice "functional" problem with a functional solution :)
1515
+
1516
+ Parsing is basically finding the seeds as the only numeric values on each line:
1517
+
1518
+ ```
1519
+ parse :: String -> (Int, Int)
1520
+ parse inp = (a, b)
1521
+ where
1522
+ a:b:_ = read . filter isDigit <$> lines inp
1523
+ ```
1524
+
1465
1525
### Day 15 Benchmarks
1466
1526
1467
1527
```
0 commit comments