day 14
docking data
approach
lines are split on = into mask updates and mem[...] writes, with a regex pulling out the address. part 1 applies the 36-character mask to each value: walking the mask from its least significant end, 0 clears that bit and 1 sets it, X leaves it. memory is a dictionary, and the answer is the sum of its values.
part 2 masks the address instead, and X is floating: it can be either bit. addresses builds every resulting address from the most significant bit down, shifting each partial address left and appending a bit; on an X it shifts all of them and appends a copy of each with the low bit set, doubling the list. the list starts from [1] as a leading sentinel bit, which the final & 0xfffffffff strips off (a comment calls this out).
the trick
expand floating bits by doubling the list of partial addresses at every X.
def addresses(n, mask):
res = [1]
for i,c in enumerate(mask):
if c == '0':
inmap(lambda a: (a << 1) | (get(n, 35 - i)), res)
elif c == '1':
inmap(lambda a: (a << 1) | 1, res)
else:
inmap(lambda a: (a << 1), res)
for i in range(len(res)):
res.append(res[i] | 1)
return res