<reponame>kws/AdventOfCode
#!/usr/bin/env python
# coding: utf-8

'''
  I probably could do this better using binary operations,
  but this worked, and gave me the solution too, so...
'''

import itertools

testProgram1 = '''mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0'''

testProgram2 = '''mask = 000000000000000000000000000000X1001X
mem[42] = 100
mask = 00000000000000000000000000000000X0XX
mem[26] = 1
'''

# Implementing a structure like this will keep things
# easy to loop over

'''[
    {
        mask: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X'
        values: [
            {address: 8, value: 11},
            {address: 7, value: 101},
            {address: 8, value: 0},
        ]
    },
    {
        mask: '1X0110X0X110000100X01100000011101111'
        values: [
            {address: 5228, value: 409649},
            {address: 64037, value: 474625}
        ]
    },
]'''

def getBinaryString(value):
    binary = format(value, "036b")
    return binary

def parseInput(inputString):
    lines = inputString.split("\n")
    instructions = []
    instr = False
    for line in lines:
        if "mask" in line:
            # start a new object
            if instr:
                instructions.append(instr)
            instr = {
                'mask': line[line.index("=")+2:],
                'values': []
            }
        elif "mem" in line:
            # Add value to object
            instr["values"].append({
                'address': line[line.index("[")+1:line.index("]")],
                'value': line[line.index("=")+2:]
            })
    
    instructions.append(instr)        
    return instructions

def applyMask(mask, value):
    # Probably could use binary operators to do this
    newValue = []
    for idx, c in enumerate(mask):
        if c == 'X':
            newValue.append(value[idx])
        else:
            newValue.append(c)
    newValue = ''.join(newValue)
    return newValue

def applyAddressMask(mask, value):
    # Probably could use binary operators to do this
    newValue = []
    for idx, c in enumerate(mask):
        if c == 'X':
            newValue.append(c)
        else:
            newValue.append(str(int(c) | int(value[idx])))
    newValue = ''.join(newValue)
    return newValue
        
def runV1Instructions(instructionList, memory):
    for i in instructionList:
        for v in i["values"]:
            binResult = applyMask(i["mask"], getBinaryString(int(v["value"])))
            # print("input value: %d, result: %d" % (int(v["value"]), int(binResult,2)))
            memory[int(v["address"])] = int(binResult,2)

def getAddressesFromFloating(floatingAddress):
    addressList = []
    splitAddress = floatingAddress.split("X")
    
    possibleBits = list(itertools.product([0, 1], repeat=len(splitAddress)-1))
    for bit in possibleBits:
        result = splitAddress[0]
        for idx, b in enumerate(bit):    
            result += str(b)
            result += splitAddress[idx+1]
        addressList.append(result)
    return addressList

def runV2Instructions(instructionList, memory):
    for i in instructionList:
        for v in i["values"]:
            # First get the memory address as a binary string
            a = getBinaryString(int(v["address"]))
            # Next, apply the mask
            floatingAddress = applyAddressMask(i["mask"], a)
            addressList = getAddressesFromFloating(floatingAddress)
            
            # Now we need to store the found value in the list of addresses provided
            for addr in addressList:
                #print("Storing value in address: %d" % (int(addr,2)))
                memory[int(addr,2)] = int(v["value"])         
            
def openFile(filename):
    with open(filename, 'r') as file:
        data = file.read()
    return data


# Part 1 Test 1
print("Part 1 Test")
memory = {}
instructionList = parseInput(testProgram1)
runV1Instructions(instructionList, memory)
print(sum(memory.values()))

# Part 1
print("Part 1 Real")
memory = {}
instructionList = parseInput(openFile('day14.txt'))
runV1Instructions(instructionList, memory)
print(sum(memory.values()))

# Part 2 Test 1
print("Part 2 Test")
memory = {}
instructionList = parseInput(testProgram2)
runV2Instructions(instructionList, memory)
print(sum(memory.values()))

# Part 2
print("Part 2 Real")
memory = {}
instructionList = parseInput(openFile('day14.txt'))
runV2Instructions(instructionList, memory)
print(sum(memory.values()))


#!/usr/bin/env python
# coding: utf-8

'''
  I probably could do this better using binary operations,
  but this worked, and gave me the solution too, so...
'''

import itertools

testProgram1 = '''mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0'''

testProgram2 = '''mask = 000000000000000000000000000000X1001X
mem[42] = 100
mask = 00000000000000000000000000000000X0XX
mem[26] = 1
'''

# Implementing a structure like this will keep things
# easy to loop over

'''[
    {
        mask: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X'
        values: [
            {address: 8, value: 11},
            {address: 7, value: 101},
            {address: 8, value: 0},
        ]
    },
    {
        mask: '1X0110X0X110000100X01100000011101111'
        values: [
            {address: 5228, value: 409649},
            {address: 64037, value: 474625}
        ]
    },
]'''

def getBinaryString(value):
    binary = format(value, "036b")
    return binary

def parseInput(inputString):
    lines = inputString.split("\n")
    instructions = []
    instr = False
    for line in lines:
        if "mask" in line:
            # start a new object
            if instr:
                instructions.append(instr)
            instr = {
                'mask': line[line.index("=")+2:],
                'values': []
            }
        elif "mem" in line:
            # Add value to object
            instr["values"].append({
                'address': line[line.index("[")+1:line.index("]")],
                'value': line[line.index("=")+2:]
            })
    
    instructions.append(instr)        
    return instructions

def applyMask(mask, value):
    # Probably could use binary operators to do this
    newValue = []
    for idx, c in enumerate(mask):
        if c == 'X':
            newValue.append(value[idx])
        else:
            newValue.append(c)
    newValue = ''.join(newValue)
    return newValue

def applyAddressMask(mask, value):
    # Probably could use binary operators to do this
    newValue = []
    for idx, c in enumerate(mask):
        if c == 'X':
            newValue.append(c)
        else:
            newValue.append(str(int(c) | int(value[idx])))
    newValue = ''.join(newValue)
    return newValue
        
def runV1Instructions(instructionList, memory):
    for i in instructionList:
        for v in i["values"]:
            binResult = applyMask(i["mask"], getBinaryString(int(v["value"])))
            # print("input value: %d, result: %d" % (int(v["value"]), int(binResult,2)))
            memory[int(v["address"])] = int(binResult,2)

def getAddressesFromFloating(floatingAddress):
    addressList = []
    splitAddress = floatingAddress.split("X")
    
    possibleBits = list(itertools.product([0, 1], repeat=len(splitAddress)-1))
    for bit in possibleBits:
        result = splitAddress[0]
        for idx, b in enumerate(bit):    
            result += str(b)
            result += splitAddress[idx+1]
        addressList.append(result)
    return addressList

def runV2Instructions(instructionList, memory):
    for i in instructionList:
        for v in i["values"]:
            # First get the memory address as a binary string
            a = getBinaryString(int(v["address"]))
            # Next, apply the mask
            floatingAddress = applyAddressMask(i["mask"], a)
            addressList = getAddressesFromFloating(floatingAddress)
            
            # Now we need to store the found value in the list of addresses provided
            for addr in addressList:
                #print("Storing value in address: %d" % (int(addr,2)))
                memory[int(addr,2)] = int(v["value"])         
            
def openFile(filename):
    with open(filename, 'r') as file:
        data = file.read()
    return data


# Part 1 Test 1
print("Part 1 Test")
memory = {}
instructionList = parseInput(testProgram1)
runV1Instructions(instructionList, memory)
print(sum(memory.values()))

# Part 1
print("Part 1 Real")
memory = {}
instructionList = parseInput(openFile('day14.txt'))
runV1Instructions(instructionList, memory)
print(sum(memory.values()))

# Part 2 Test 1
print("Part 2 Test")
memory = {}
instructionList = parseInput(testProgram2)
runV2Instructions(instructionList, memory)
print(sum(memory.values()))

# Part 2
print("Part 2 Real")
memory = {}
instructionList = parseInput(openFile('day14.txt'))
runV2Instructions(instructionList, memory)
print(sum(memory.values()))
#!/usr/bin/env python # coding: utf-8 I probably could do this better using binary operations,   but this worked, and gave me the solution too, so... mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X mem[8] = 11 mem[7] = 101 mem[8] = 0 mask = 000000000000000000000000000000X1001X mem[42] = 100 mask = 00000000000000000000000000000000X0XX mem[26] = 1 # Implementing a structure like this will keep things # easy to loop over [     {         mask: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X'         values: [             {address: 8, value: 11},             {address: 7, value: 101},             {address: 8, value: 0},         ]     },     {         mask: '1X0110X0X110000100X01100000011101111'         values: [             {address: 5228, value: 409649},             {address: 64037, value: 474625}         ]     }, ] # start a new object # Add value to object # Probably could use binary operators to do this # Probably could use binary operators to do this # print("input value: %d, result: %d" % (int(v["value"]), int(binResult,2))) # First get the memory address as a binary string # Next, apply the mask # Now we need to store the found value in the list of addresses provided #print("Storing value in address: %d" % (int(addr,2))) # Part 1 Test 1 # Part 1 # Part 2 Test 1 # Part 2