Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2020-12-14 - Docking Data

(original .ipynb)

Day 14 puzzle input is a sequence of assembly-like instructions (mine is here) which can be either writes to memory locations or adjustments of a "mask" register. Part 1 involves interpreting the mask as modifying the values being written to memory (0 and 1 set the bits in those positions, X is ignored). Part 2 involves interpreting the mask as modifying the memory address being written, with "X"s being wildcards meaning both 0 and 1 (so one write instruction can result in multiple addresses being modified).

from collections import defaultdict

test_input_str = """mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0"""

puzzle_input_str = open("puzzle_input/day14.txt", "r").read()

def parse_mask(mask):
    fixed_bits = []
    floating_bits = []
    
    for i, digit in enumerate(mask):
        if digit == "X":
            floating_bits.append(i)
        elif digit == "1":
            fixed_bits.append((int(i), digit))
        else:
            # do nothing for digit == "0"
            pass
            
    return fixed_bits, floating_bits

def pad_value(value_str):
    padding = (36 - len(value_str)) * "0"
    return padding + value_str

def apply_mask(value_str, mask_str):
    value_bin_str = bin(int(value_str))[2:]
    value_lst = [v for v in pad_value(value_bin_str)]
    
    fixed_bits, _ = parse_mask(mask_str)
    
    for position, digit in fixed_bits:
        value_lst[position] = digit
    return "".join(value_lst)

def execute_instruction_v1(instruction, mask, mem):
    lhs, rhs = instruction.split(" = ")
    
    if lhs == "mask":
        return rhs
    
    location = int(lhs.removeprefix("mem[")[:-1])
    value = apply_mask(rhs, mask)
    
    mem[location] = int(value, 2)
    
    return mask

def solve(input_str, execute_func):
    mask = ()
    mem = defaultdict(lambda:0)
    
    for instruction in input_str.split("\n"):
        mask = execute_func(instruction, mask, mem)
    
    total = 0
    for addr in mem:
        total += mem[addr]
        
    return total

assert 165 == solve(test_input_str, execute_instruction_v1)

print(solve(puzzle_input_str, execute_instruction_v1))
15407292426155
%%time

def apply_fixed_bits(fixed_bits, address_lst):
    addr_lst_copy = [v for v in address_lst]
    
    for position, digit in fixed_bits:
        addr_lst_copy[position] = digit
    return addr_lst_copy

def apply_floating_bit(address_lst, index, val):
    addr_lst_copy = [v for v in address_lst]
    addr_lst_copy[index] = val
    return addr_lst_copy

def apply_mask_v2(original_address, mask_str):
    fixed_bits, floating_bits = parse_mask(mask_str)
    
    original_address_str = bin(original_address)[2:]
    original_address_lst = [v for v in pad_value(original_address_str)]
    
    addresses = [
        apply_fixed_bits(fixed_bits, original_address_lst)
    ]    
    
    for position in floating_bits:
        new_addresses = []
        for address in addresses:
            new_addresses.append(apply_floating_bit(address, position, "0"))
            new_addresses.append(apply_floating_bit(address, position, "1"))
        
        addresses = new_addresses
    
    return [int("".join(address), 2) for address in addresses]


def execute_instruction_v2(instruction, mask, mem):
    lhs, rhs = instruction.split(" = ")
    
    if lhs == "mask":
        return rhs
    
    addresses = apply_mask_v2(int(lhs.removeprefix("mem[")[:-1]), mask)
    value = int(rhs)
    
    for address in addresses:
        mem[address] = value

    return mask


test_input_str_2 = """mask = 000000000000000000000000000000X1001X
mem[42] = 100
mask = 00000000000000000000000000000000X0XX
mem[26] = 1"""

# fun fact, because my solution is horrifically inefficient
# and test_input_str has huge amount of floating bits, trying
# to calculate part2(test_input_str) takes rather a long time
# to finish. this cost me quite a lot of time - I didn't realise
# they changed the example input they used :-/
assert 208 == solve(test_input_str_2, execute_instruction_v2)

print(solve(puzzle_input_str, execute_instruction_v2))
3260587250457
CPU times: user 384 ms, sys: 3.5 ms, total: 388 ms
Wall time: 398 ms