Skip to content

Commit 5a3439f

Browse files
committed
day 22 reflections and cleanup
1 parent ec6beeb commit 5a3439f

File tree

3 files changed

+90
-78
lines changed

3 files changed

+90
-78
lines changed

2024/AOC2024/Day22.hs

+32-69
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
{-# OPTIONS_GHC -Wno-unused-imports #-}
2-
{-# OPTIONS_GHC -Wno-unused-top-binds #-}
3-
41
-- |
52
-- Module : AOC2024.Day22
63
-- License : BSD3
@@ -9,87 +6,53 @@
96
-- Portability : non-portable
107
--
118
-- Day 22. See "AOC.Solver" for the types used in this module!
12-
--
13-
-- After completing the challenge, it is recommended to:
14-
--
15-
-- * Replace "AOC.Prelude" imports to specific modules (with explicit
16-
-- imports) for readability.
17-
-- * Remove the @-Wno-unused-imports@ and @-Wno-unused-top-binds@
18-
-- pragmas.
19-
-- * Replace the partial type signatures underscores in the solution
20-
-- types @_ :~> _@ with the actual types of inputs and outputs of the
21-
-- solution. You can delete the type signatures completely and GHC
22-
-- will recommend what should go in place of the underscores.
239
module AOC2024.Day22 (
2410
day22a,
2511
day22b,
2612
)
2713
where
2814

29-
import AOC.Prelude
30-
import Data.Bits
31-
import qualified Data.Graph.Inductive as G
15+
import AOC.Common (strictIterate, (!!!))
16+
import AOC.Common.Parser (pDecimal, parseMaybe', sepByLines)
17+
import AOC.Solver (noFail, type (:~>) (..))
18+
import Data.Bits (Bits (shift, xor, (.&.)))
19+
import Data.Foldable (Foldable (toList))
3220
import qualified Data.IntMap as IM
33-
import qualified Data.IntMap.NonEmpty as NEIM
34-
import qualified Data.IntSet as IS
35-
import qualified Data.IntSet.NonEmpty as NEIS
36-
import qualified Data.List.NonEmpty as NE
37-
import qualified Data.List.PointedList as PL
38-
import qualified Data.List.PointedList.Circular as PLC
39-
import qualified Data.Map as M
40-
import qualified Data.Map.NonEmpty as NEM
41-
import qualified Data.OrdPSQ as PSQ
42-
import qualified Data.Sequence as Seq
43-
import qualified Data.Sequence.NonEmpty as NESeq
44-
import qualified Data.Set as S
45-
import qualified Data.Set.NonEmpty as NES
46-
import qualified Data.Text as T
47-
import qualified Data.Vector as V
48-
import qualified Linear as L
49-
import qualified Text.Megaparsec as P
50-
import qualified Text.Megaparsec.Char as P
51-
import qualified Text.Megaparsec.Char.Lexer as PP
52-
53-
day22a :: _ :~> _
54-
day22a =
55-
MkSol
56-
{ sParse =
57-
parseMaybe' $
58-
sepByLines pDecimal
59-
, -- noFail $
60-
-- lines
61-
sShow = show
62-
, sSolve =
63-
noFail $
64-
sum . map ((!! 2000) . iterate step)
65-
}
21+
import Safe.Foldable (maximumMay)
6622

6723
step :: Int -> Int
68-
step n = n'''
24+
step = prune . phase3 . prune . phase2 . prune . phase1
6925
where
70-
n' = prune $ (n `shift` 6) `xor` n
71-
n'' = prune $ (n' `shift` (-5)) `xor` n'
72-
n''' = prune $ (n'' `shift` 11) `xor` n''
26+
phase1 n = (n `shift` 6) `xor` n
27+
phase2 n = (n `shift` (-5)) `xor` n
28+
phase3 n = (n `shift` 11) `xor` n
7329
prune = (.&. 16777215)
7430

75-
day22b :: _ :~> _
31+
day22a :: [Int] :~> Int
32+
day22a =
33+
MkSol
34+
{ sParse = parseMaybe' $ sepByLines pDecimal
35+
, sShow = show
36+
, sSolve = noFail $ sum . map ((!!! 2000) . strictIterate step)
37+
}
38+
39+
day22b :: [Int] :~> Int
7640
day22b =
7741
MkSol
7842
{ sParse = sParse day22a
7943
, sShow = show
80-
, sSolve =
81-
noFail $ \xs ->
82-
let serieses =
83-
xs <&> \x ->
84-
let ps = take 2000 $ map (`mod` 10) $ iterate step x
85-
dPs = zipWith (\p0 p1 -> (p1, p1 - p0)) ps (drop 1 ps)
86-
windows = slidingWindows 4 dPs <&> \w -> (encodeSeq $ snd <$> w, fst $ last (toList w))
87-
seqMap = IM.fromListWith (const id) windows
88-
in seqMap
89-
bests = toList $ IM.unionsWith (+) serieses
90-
in maximum bests
91-
-- bests = M.unionsWith (<>) $ map (fmap (:[])) serieses
92-
-- in maximumBy (comparing (sum . snd)) (M.toList bests)
44+
, sSolve = maximumMay . toList . IM.unionsWith (+) . map genSeries
9345
}
9446
where
95-
encodeSeq = sum . zipWith (\i x -> x * 19^(i :: Int)) [0..] . map (+ 9) . toList
47+
encodeSeq = sum . zipWith (\i x -> x * 19 ^ (i :: Int)) [0 ..] . map (+ 9)
48+
genSeries = IM.fromListWith (const id) . chompChomp . take 2000 . map (`mod` 10) . strictIterate step
49+
where
50+
chompChomp :: [Int] -> [(Int, Int)]
51+
chompChomp (a : b : c : d : e : fs) =
52+
(encodeSeq [da, db, dc, dd], e) : chompChomp (b : c : d : e : fs)
53+
where
54+
da = b - a
55+
db = c - b
56+
dc = d - c
57+
dd = e - d
58+
chompChomp _ = []

bench-results/2024/day22.txt

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
>> Day 22a
22
benchmarking...
3-
time 84.47 ms (82.54 ms .. 88.08 ms)
4-
0.998 R² (0.993 R² .. 1.000 R²)
5-
mean 83.30 ms (82.68 ms .. 84.79 ms)
6-
std dev 1.625 ms (543.2 μs .. 2.715 ms)
3+
time 28.09 ms (27.10 ms .. 28.65 ms)
4+
0.994 R² (0.979 R² .. 1.000 R²)
5+
mean 29.09 ms (28.60 ms .. 30.31 ms)
6+
std dev 1.589 ms (387.5 μs .. 2.463 ms)
7+
variance introduced by outliers: 21% (moderately inflated)
78

89
* parsing and formatting times excluded
910

1011
>> Day 22b
1112
benchmarking...
12-
time 5.653 s (5.314 s .. 6.426 s)
13-
0.998 R² (NaN R² .. 1.000 R²)
14-
mean 6.488 s (6.099 s .. 7.163 s)
15-
std dev 642.2 ms (58.08 ms .. 808.9 ms)
16-
variance introduced by outliers: 23% (moderately inflated)
13+
time 3.095 s (3.060 s .. 3.129 s)
14+
1.000 R² (1.000 R² .. 1.000 R²)
15+
mean 3.086 s (3.073 s .. 3.090 s)
16+
std dev 8.510 ms (919.8 μs .. 10.82 ms)
17+
variance introduced by outliers: 19% (moderately inflated)
1718

1819
* parsing and formatting times excluded
1920

reflections/2024/day22.md

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
First let's set up the RNG step:
2+
3+
```haskell
4+
step :: Int -> Int
5+
step = prune . phase3 . prune . phase2 . prune . phase1
6+
where
7+
phase1 n = (n `shift` 6) `xor` n
8+
phase2 n = (n `shift` (-5)) `xor` n
9+
phase3 n = (n `shift` 11) `xor` n
10+
prune = (.&. 16777215)
11+
```
12+
13+
Part 1 is just running and summing:
14+
15+
16+
```haskell
17+
part1 :: [Int] -> Int
18+
part1 = sum . map ((!! 2000) . iterate)
19+
```
20+
21+
Part 2 is a little more interesting. We want to make a map of 4-sequences to
22+
the first price they would get. On a chain of iterations, we can iteratively
23+
chomp on runs of 4:
24+
25+
```haskell
26+
chompChomp :: [Int] -> [([Int], Int)]
27+
chompChomp (a : b : c : d : e : fs) =
28+
([da, db, dc, dd], e) : chompChomp (b : c : d : e : fs)
29+
where
30+
da = b - a
31+
db = c - b
32+
dc = d - c
33+
dd = e - d
34+
chompChomp _ = []
35+
36+
priceForChain :: Int -> Map [Int] Int
37+
priceForChain = M.fromListWith (const id) . chompChomp . take 2000 . map (`mod` 10) . iterate step
38+
```
39+
40+
Then we can sum all of the sequence prices and get the maximum:
41+
42+
```haskell
43+
part2 :: [Int] -> Int
44+
part2 = maximum . M.elems . M.fromListWith (+) . map priceForChain
45+
```
46+
47+
I'm not super happy with the fact that this takes 3 seconds (even after
48+
optimizing to using `IntMap` on a base-19 encoding of the sequence).

0 commit comments

Comments
 (0)