In [20]:
binary_command = {'NOT': '~', 'AND': '&', 'OR': '|', 'LSHIFT': '<<', 'RSHIFT': '>>'}
operators = binary_command.values()
In [21]:
import csv
def translate(l):
return [binary_command[a] if a in binary_command else a for a in l]
def display(input_file):
"""produce a dict mapping variables to expressions"""
commands = []
with open(input_file, 'rt') as f_input:
csv_reader = csv.reader(f_input, delimiter=' ')
for line in csv_reader:
commands.append((line[-1], ' '.join(list(translate(line[:-2])))))
return dict(commands)
In [32]:
import re
def extract_variables(expr):
varbls = []
regex_pattern = '\s|\\)|\\('
l = re.split(regex_pattern, expr)
for a in l:
if (a not in operators) and (not a.isnumeric()) and (a != ''):
varbls.append(a)
return set(varbls)
def create_instance(wire):
exec_python = commands[wire]
pending = extract_variables(commands[wire])
count = 0
while pending and (count < 200):
s = pending.pop()
expr = commands[s]
exec_python = re.sub('({0})'.format(s), '( {0} )'.format(expr), exec_python)
pending = pending.union(extract_variables(exec_python))
count += 1
return wire + ' = ' + exec_python
def evaluate(var):
instance = create_instance(var)
exec(instance)
return np.uint16(locals()[var])
In [40]:
commands = display('inputs/input7.test.txt')
In [41]:
def test():
assert(evaluate('d') == 72)
assert(evaluate('e') == 507)
assert(evaluate('f') == 492)
assert(evaluate('g') == 114)
assert(evaluate('h') == 65412)
assert(evaluate('i') == 65079)
assert(evaluate('x') == 123)
assert(evaluate('y') == 456)
In [42]:
test()
This approach seems correct, but it creates huge expressions along the way that become harder and harder to parse. Thus the time to a final expression that wraps up all the computations is very long. Two ideas to carry on: i) concurrent evaluation of expressions; ii) define lazy variables/functions that collect all the dependencies of the circuit and start firing upon request.
The solution provided hereto owes credit to this source: https://www.reddit.com/r/adventofcode/comments/5id6w0/2015_day_7_part_1_python_wrong_answer/
In [21]:
import numpy as np
def RSHIFT(a, b):
result = np.uint16(a) >> int(b)
return int(result)
def LSHIFT(a, b):
result = np.uint16(a) << int(b)
return int(result)
def OR(a, b):
result = np.uint16(a) | np.uint16(b)
return int(result)
def AND(a, b):
result = np.uint16(a) & np.uint16(b)
return int(result)
def NOT(a):
result = ~ np.uint16(a)
return int(result)
In [28]:
import csv
def display(input_file):
"""produce a dict mapping variables to expressions"""
commands = []
with open(input_file, 'rt') as f_input:
csv_reader = csv.reader(f_input, delimiter=' ')
for line in csv_reader:
commands.append((line[-1], line[:-2]))
return dict(commands)
In [105]:
def evaluate(wire):
known = {}
while wire not in known:
if wire in known:
break
for k, v in commands.items():
if (len(v) == 1) and (v[0].isnumeric()) and (k not in known):
known[k] = int(v[0])
elif (len(v) == 1) and (v[0] in known) and (k not in known):
known[k] = known[v[0]]
elif ('AND' in v) and (v[0] in known) and (v[2] in known):
known[k] = AND(known[v[0]], known[v[2]])
elif ('AND' in v) and (v[0].isnumeric()) and (v[2] in known):
known[k] = AND(int(v[0]), known[v[2]])
elif ('AND' in v) and (v[0] in known) and (v[2].isnumeric()):
known[k] = AND(known[v[0]], int(v[2]))
elif ('OR' in v) and (v[0] in known) and (v[2] in known):
known[k] = OR(known[v[0]], known[v[2]])
elif ('OR' in v) and (v[0].isnumeric()) and (v[2] in known):
known[k] = OR(int(v[0]), known[v[2]])
elif ('OR' in v) and (v[0] in known) and (v[2].isnumeric()):
known[k] = OR(known[v[0]], int(v[2]))
elif ('LSHIFT' in v) and (v[0] in known):
known[k] = LSHIFT(known[v[0]], v[2])
elif ('RSHIFT' in v) and (v[0] in known):
known[k] = RSHIFT(known[v[0]], v[2])
elif ('NOT' in v) and (v[1] in known):
known[k] = NOT(known[v[1]])
return known[wire]
In [107]:
commands = display('inputs/input7.test1.txt')
commands
Out[107]:
In [108]:
evaluate('a')
Out[108]:
In [109]:
commands = display('inputs/input7.test2.txt')
commands
Out[109]:
In [110]:
test()
In [111]:
commands = display('inputs/input7.txt')
evaluate('a')
Out[111]:
In [3]:
import csv
import numpy as np
def display(input_file):
"""produce a dict mapping variables to expressions"""
commands = []
with open(input_file, 'rt') as f_input:
csv_reader = csv.reader(f_input, delimiter=' ')
for line in csv_reader:
commands.append((line[-1], line[:-2]))
return dict(commands)
In [2]:
class LazyVar(object):
def __init__(self, func):
self.func = func
self.value = None
def __call__(self):
if self.value is None:
self.value = self.func()
return self.value
In [3]:
binary_command = {'NOT': '~', 'AND': '&', 'OR': '|', 'LSHIFT': '<<', 'RSHIFT': '>>'}
def translate(l):
translated = []
for a in l:
if a in binary_command:
b = binary_command[a]
elif a.isnumeric():
b = 'np.uint16({})'.format(a)
else:
b = '{}.func()'.format('var_' + a)
translated.append(b)
return translated
In [4]:
commands = display('inputs/input7.test2.txt')
In [5]:
commands = display('inputs/input7.test2.txt')
for k, v in commands.items():
command_str = '{0} = LazyVar(lambda: {1})'.format('var_' + k, ''.join(translate(v)))
print(command_str)
exec(command_str)
In [6]:
def test():
assert(var_d.func() == 72)
assert(var_e.func() == 507)
assert(var_f.func() == 492)
assert(var_g.func() == 114)
assert(var_h.func() == 65412)
assert(var_i.func() == 65079)
assert(var_x.func() == 123)
assert(var_y.func() == 456)
In [7]:
test()
Although the approach passes the test, it does not end in reasonable time for the full input.
In [35]:
def rscript_command(var, l):
vocab = {'AND' : 'bitwAnd',
'OR' : 'bitwOr',
'LSHIFT' : 'bitwShiftL',
'RSHIFT' : 'bitwShiftR'}
if len(l) == 3:
func = vocab[l[1]]
arg1 = l[0] if l[0].isdigit() else 'var_' + l[0] + '()'
arg2 = l[2] if l[2].isdigit() else 'var_' + l[2] + '()'
return 'var_{0} <- function(a={1}, b={2})'.format(var, arg1, arg2) + ' {' + '{0}(a,b)'.format(func) + '}'
elif len(l) == 2:
func = 'bitwNot'
arg1 = l[1] if l[1].isdigit() else 'var_' + l[1] + '()'
return 'var_{0} <- function(a={1})'.format(var, arg1) + ' {' + '{0}(a)'.format(func) + '}'
else:
arg1 = l[0] if l[0].isdigit() else 'var_' + l[0] + '()'
return 'var_{0} <- function(a={1})'.format(var, arg1) + ' {' + 'a' + '}'
def generate_rscript(commands, target):
with open('day7_commands.R', 'wt') as f:
for k, v in commands.items():
f.write(rscript_command(k, v)+'\n')
f.write('var_' + target + '()')
In [36]:
commands = display('inputs/input7.test2.txt')
generate_rscript(commands, 'd')
In [37]:
! cat day7_commands.R
In [38]:
!Rscript day7_commands.R
In [39]:
commands = display('inputs/input7.txt')
generate_rscript(commands, 'a')
In [40]:
! cat day7_commands.R
In [ ]:
!Rscript day7_commands.R
Although this approach is more natural than defining a LazyWrapper in Python, it takes quite a lot of time to execute, so this is not a very cool solution after all.
In [114]:
commands = display('inputs/input7.txt')
commands['b'] = ['16076']
evaluate('a')
Out[114]: