Skip to content

Commit 0128cea

Browse files
committed
day 17 reflections
1 parent 0a2219a commit 0128cea

File tree

2 files changed

+147
-10
lines changed

2 files changed

+147
-10
lines changed

2024/AOC2024/Day17.hs

+12-10
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,22 @@ day17a =
8989
pure (a, b, c, p, fromIntegral <$> d)
9090
, sShow = intercalate "," . map (show . getFinite)
9191
, sSolve = noFail \(a0, b0, c0, instrs, _) ->
92-
appEndo (stepWith instrs (Endo . (:)) 0 a0 b0 c0) []
92+
appEndo (stepWith instrs (Endo . (:)) a0 b0 c0) []
9393
}
9494

9595
stepWith ::
9696
Monoid a =>
9797
SV.Vector 8 Instr ->
9898
-- | out
9999
(Finite 8 -> a) ->
100-
Finite 8 ->
100+
-- | Starting a
101101
Word ->
102+
-- | Starting b
102103
Word ->
104+
-- | Starting c
103105
Word ->
104106
a
105-
stepWith tp out = go
107+
stepWith tp out = go 0
106108
where
107109
go i !a !b !c = case tp `SV.index` i of
108110
ADV r -> withStep go (a `div` (2 ^ combo r)) b c
@@ -131,16 +133,16 @@ searchStep :: SV.Vector 8 Instr -> [Finite 8] -> [Word]
131133
searchStep tp outs = do
132134
JNZ 0 <- pure $ tp `SV.index` maxBound
133135
[CReg _] <- pure [r | OUT r <- toList tp]
134-
let search a = \case
135-
o : os -> do
136-
a' <- stepBack a
137-
guard $ stepForward a' == Just o
138-
search a' os
139-
[] -> pure a
140136
search 0 (reverse outs)
141137
where
138+
search a = \case
139+
o : os -> do
140+
a' <- stepBack a
141+
guard $ stepForward a' == Just o
142+
search a' os
143+
[] -> pure a
142144
stepForward :: Word -> Maybe (Finite 8)
143-
stepForward a0 = getAlt $ stepWith tp (Alt . Just) 0 a0 0 0
145+
stepForward a0 = getAlt $ stepWith tp (Alt . Just) a0 0 0
144146
stepBack :: Word -> [Word]
145147
stepBack = go' maxBound
146148
where

reflections/2024/day17.md

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
This one is a cute little interpreter problem, a staple of advent of code.
2+
Let's write Part 1 in a way that makes Part 2 easy, where we will have to
3+
eventually "run" it backwards. We can use `Finite n` as the type with `n`
4+
inhabitants, so `Finite 8` will, for example, have the numbers 0 to 7. And also
5+
`Vector n a` from `Data.Vector.Sized`, which contains `n` items.
6+
7+
```haskell
8+
data Combo
9+
= CLiteral (Finite 4)
10+
| CReg (Finite 3)
11+
12+
data Instr
13+
= ADV Combo
14+
| BXL (Finite 8)
15+
| BST Combo
16+
| JNZ (Finite 4)
17+
| BXC
18+
| OUT Combo
19+
| BDV Combo
20+
| CDV Combo
21+
```
22+
23+
We can then write a function to interpret the outputs into a monoid.
24+
25+
```haskell
26+
stepWith ::
27+
Monoid a =>
28+
Vector 8 Instr ->
29+
-- | out
30+
(Finite 8 -> a) ->
31+
-- | Starting a
32+
Word ->
33+
-- | Starting b
34+
Word ->
35+
-- | Starting c
36+
Word ->
37+
a
38+
stepWith prog out = go 0
39+
where
40+
go i !a !b !c = case prog `SV.index` i of
41+
ADV r -> withStep go (a `div` (2 ^ combo r)) b c
42+
BXL l -> withStep go a (b `xor` fromIntegral l) c
43+
BST r -> withStep go a (combo r `mod` 8) c
44+
JNZ l
45+
| a == 0 -> withStep go 0 b c
46+
| otherwise -> go (weakenN l) a b c -- weakenN :: Finite 4 -> Finite 8
47+
BXC -> withStep go a (b `xor` c) c
48+
OUT r ->
49+
let o = modulo (fromIntegral (combo r))
50+
in out o <> withStep go a b c
51+
BDV r -> withStep go a (a `div` (2 ^ combo r)) c
52+
CDV r -> withStep go a b (a `div` (2 ^ combo r))
53+
where
54+
combo = \case
55+
CLiteral l -> fromIntegral l
56+
CReg 0 -> a
57+
CReg 1 -> b
58+
CReg _ -> c
59+
withStep p
60+
| i == maxBound = \_ _ _ -> mempty
61+
| otherwise = p (i + 1)
62+
```
63+
64+
Part 1 is a straightforward application, although we can use a difflist to get
65+
O(n) concats instead of O(n^2)
66+
67+
```haskell
68+
import Data.DList as DL
69+
70+
part1 :: Vector 8 Instr -> Word -> Word -> Word -> [Finite 8]
71+
part1 prog a b c = DL.toList $ stepWith prog DL.singleton a b c
72+
```
73+
74+
Part 2 it gets a bit interesting. We can solve it "in general" under the
75+
conditions:
76+
77+
78+
1. The final instruction is JNZ 0
79+
2. There is one `OUT` per loop, with a register
80+
3. b and c are overwritten at the start of each loop
81+
82+
The plan would be:
83+
84+
85+
1. Start from the end with a known `a` and move backwards, accumulating all
86+
possible values of `a` that would lead to the end value, ignoring b and c
87+
2. For each of those possible a's, start from the beginning with that `a` and
88+
filter the ones that don't produce the correct `OUT`.
89+
90+
We have to write a "step backwards" from scratch, but we can actually use our
91+
original `stepWith` to write a version that _bails_ after the first output, by
92+
having our monoid be `Data.Monoid.First`. Then in the line `out o <> withStep
93+
go a abc`, it'll just completely ignore the right hand side and output the
94+
first `OUT` result.
95+
96+
```haskell
97+
searchStep :: Vector 8 Instr -> [Finite 8] -> [Word]
98+
searchStep prog outs = do
99+
-- enforce the invariants
100+
JNZ 0 <- pure $ prog `SV.index` maxBound
101+
[CReg _] <- pure [r | OUT r <- toList prog]
102+
search 0 (reverse outs)
103+
where
104+
search a = \case
105+
o : os -> do
106+
a' <- stepBack a
107+
guard $ stepForward a' == Just o
108+
search a' os
109+
[] -> pure a
110+
-- doesn't enforce that b and c are reset, because i'm lazy
111+
stepForward :: Word -> Maybe (Finite 8)
112+
stepForward a0 = getFirst $ stepWith tp (First . Just) a0 0 0
113+
stepBack :: Word -> [Word]
114+
stepBack = go' maxBound
115+
where
116+
go' i a = case tp `SV.index` i of
117+
ADV r -> do
118+
a' <- case r of
119+
CLiteral l -> ((a `shift` fromIntegral l) +) <$> [0 .. 2 ^ getFinite l - 1]
120+
CReg _ -> []
121+
go' (pred i) a'
122+
OUT _ -> pure a
123+
_ -> go' (pred i) a
124+
```
125+
126+
We really only have to handle the `ADV r` case because that's the only
127+
instruction that modifies `A`. If we `ADV 3`, that means that the possible
128+
"starting A's" are `known_a * 8 + x`, where `x` is between 0 and 7.
129+
130+
Wrapping it all up:
131+
132+
```haskell
133+
part2 :: Vector 8 Instr -> [Finite 8] -> Maybe Word
134+
part2 instrs = listToMaybe . searchStep instrs
135+
```

0 commit comments

Comments
 (0)