2015-12-07 - Some Assembly Required
(original .ipynb)
Puzzle input is a series of instructions (see test_input_str
) that set some "wires" (named values, like registers) according to some very simple bitwise operations - and, or, left-shift, right-shift and not. In part one we try evaluate the instructions to find the final value written to wire "a". In part two we have to override the value written to the wire "b" to the value written to part "a" in the first part, and then re-run and find what's written to "a".
This implementation is kinda nasty - I just repeatedly loop through the instructions to see if we can evaluate anything, and if we changed anything we go through the remaining instructions. There's clearly a nicer way to do this :)
from collections import defaultdict from ctypes import c_uint16 as ushort puzzle_input_str = open("./puzzle_input/day7.txt").read() test_input_str = """123 -> x 456 -> y x AND y -> d x OR y -> e x LSHIFT 2 -> f y RSHIFT 2 -> g NOT x -> h NOT y -> i""" const_op = lambda arg: arg not_op = lambda arg: ~arg binary_ops = { "AND": lambda arg1, arg2: arg1 & arg2, "OR": lambda arg1, arg2: arg1 | arg2, "LSHIFT": lambda arg1, arg2: arg1 << arg2, "RSHIFT": lambda arg1, arg2: arg1 >> arg2 } def get_value(loc, wires): if type(loc) == int: return loc if type(loc) == ushort: return int(loc) elif loc.isnumeric(): return int(loc) return wires[loc] def parse_calc(calc_str): tokens = calc_str.split(" ") if len(tokens) == 1: return const_op, [tokens[0]] elif len(tokens) == 2: return not_op, [tokens[1]] else: return binary_ops[tokens[1]], [tokens[0], tokens[2]] def parse_instruction(instruction_str): lhs_str, rhs_str = instruction_str.split(" -> ") calc = parse_calc(lhs_str) return calc, rhs_str def all_args_set(args): none_args = list(filter(lambda x: x == None, args)) return len(none_args) == 0 def part_one(input_str, b_override=None): instructions = [parse_instruction(line) for line in input_str.split("\n")] wires = defaultdict(lambda: None) if b_override: instructions = [(instruction,dest) for (instruction,dest) in instructions if dest != "b"] wires["b"] = b_override executed_instruction = True while executed_instruction: executed_instruction = False todo_instructions = [] for instruction, dest in instructions: op, args = instruction args = [ get_value(arg, wires) for arg in args ] if all_args_set(args): res = op(*[ushort(get_value(arg, wires)).value for arg in args]) wires[dest] = res executed_instruction = True else: todo_instructions.append((instruction, dest)) instructions = todo_instructions return wires["a"] print("part one:", part_one(puzzle_input_str))
part one: 3176
def part_two(input_str): return part_one(input_str, part_one(input_str)) print("part two:", part_two(puzzle_input_str))
part two: 14710