From 9b0b8eb1f63d538dd78d6415002f6999b6145350 Mon Sep 17 00:00:00 2001 From: Stefan Harmuth Date: Tue, 8 Dec 2020 08:55:18 +0100 Subject: [PATCH 1/6] makes parsing minimally faster (and also more readable) --- day07.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/day07.py b/day07.py index 51d4279..0329efa 100644 --- a/day07.py +++ b/day07.py @@ -21,11 +21,8 @@ def getBagContent(bag_line): return return_dict for content in contents.split(", "): - match_groups = re.match(content_re, content) - if not match_groups: - print("ERROR: content '%s' cannot be matched" % content) - - return_dict[color][match_groups.group(2)] = int(match_groups.group(1)) + content_count, content_color = re.match(content_re, content).groups() + return_dict[color][content_color] = int(content_count) return return_dict From aa5ae674ec83224d32f983ec6a5d383ce58e06ae Mon Sep 17 00:00:00 2001 From: Stefan Harmuth Date: Tue, 8 Dec 2020 09:04:38 +0100 Subject: [PATCH 2/6] only int-cast param when needed --- day08.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/day08.py b/day08.py index 611c4f0..c1a1fca 100644 --- a/day08.py +++ b/day08.py @@ -19,15 +19,13 @@ def runCode(lines, infinite_returns_value=False): lines_executed.add(line_pointer) command, param = lines[line_pointer].split() - param = int(param) if command == 'nop': - pass line_pointer += 1 elif command == 'acc': - accumulator = accumulator + param + accumulator = accumulator + int(param) line_pointer += 1 elif command == 'jmp': - line_pointer = line_pointer + param + line_pointer = line_pointer + int(param) return accumulator From b4ff7c58d023412c72dc3914ae3a5a4662a8af47 Mon Sep 17 00:00:00 2001 From: Stefan Harmuth Date: Tue, 8 Dec 2020 09:09:50 +0100 Subject: [PATCH 3/6] TIL: enumerate() exists; no more manual counting --- day08.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/day08.py b/day08.py index c1a1fca..aca46df 100644 --- a/day08.py +++ b/day08.py @@ -37,9 +37,7 @@ def part1(test_mode=False): def part2(test_mode=False): my_input = aoclib.getInputAsArray(day=DAY, test=test_mode) - counter = -1 - for check_command in my_input: - counter += 1 + for counter, check_command in enumerate(my_input): if not check_command.startswith('nop') and not check_command.startswith('jmp'): continue From 761e3bf975d975c9fd3ea1563c2eb58387ca282a Mon Sep 17 00:00:00 2001 From: Stefan Harmuth Date: Tue, 8 Dec 2020 09:12:24 +0100 Subject: [PATCH 4/6] only check for 'nop' if the check for 'jmp' failes. Saves 0.5ms :) --- day08.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/day08.py b/day08.py index aca46df..eda100f 100644 --- a/day08.py +++ b/day08.py @@ -42,11 +42,10 @@ def part2(test_mode=False): continue check_code = my_input[:] - if check_command.startswith('nop'): - check_code[counter] = check_code[counter].replace('nop', 'jmp') - if check_command.startswith('jmp'): check_code[counter] = check_code[counter].replace('jmp', 'nop') + elif check_command.startswith('nop'): + check_code[counter] = check_code[counter].replace('nop', 'jmp') check_answer = runCode(check_code) if check_answer is not None: From 8277dfe0f9b098e337e8b12cdc7eae3f1c5f5bde Mon Sep 17 00:00:00 2001 From: Stefan Harmuth Date: Tue, 8 Dec 2020 09:43:28 +0100 Subject: [PATCH 5/6] we only need to check the instructions up to the infinite loop - everything else cannot contain the error --- day08.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/day08.py b/day08.py index eda100f..add5300 100644 --- a/day08.py +++ b/day08.py @@ -5,7 +5,7 @@ TEST_SOLUTION_PART1 = 5 TEST_SOLUTION_PART2 = 8 -def runCode(lines, infinite_returns_value=False): +def runCode(lines, infinite_returns_value=False, infinite_returns_set=False): accumulator = 0 line_pointer = 0 lines_executed = set() @@ -14,6 +14,8 @@ def runCode(lines, infinite_returns_value=False): if line_pointer in lines_executed: if infinite_returns_value: return accumulator + elif infinite_returns_set: + return lines_executed else: return None @@ -37,15 +39,15 @@ def part1(test_mode=False): def part2(test_mode=False): my_input = aoclib.getInputAsArray(day=DAY, test=test_mode) - for counter, check_command in enumerate(my_input): - if not check_command.startswith('nop') and not check_command.startswith('jmp'): + for exec_line in runCode(my_input, infinite_returns_set=True): + if not my_input[exec_line].startswith('nop') and not my_input[exec_line].startswith('jmp'): continue check_code = my_input[:] - if check_command.startswith('jmp'): - check_code[counter] = check_code[counter].replace('jmp', 'nop') - elif check_command.startswith('nop'): - check_code[counter] = check_code[counter].replace('nop', 'jmp') + if my_input[exec_line].startswith('jmp'): + check_code[exec_line] = check_code[exec_line].replace('jmp', 'nop') + elif my_input[exec_line].startswith('nop'): + check_code[exec_line] = check_code[exec_line].replace('nop', 'jmp') check_answer = runCode(check_code) if check_answer is not None: From a8a0873eec02329ba8ba5fcd253075c677a4dde3 Mon Sep 17 00:00:00 2001 From: Stefan Harmuth Date: Tue, 8 Dec 2020 10:51:26 +0100 Subject: [PATCH 6/6] pre-optimizing the opcode makes p1 take twice the time, but p2 almost twice as fast --- day08.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/day08.py b/day08.py index add5300..faec4da 100644 --- a/day08.py +++ b/day08.py @@ -5,6 +5,16 @@ TEST_SOLUTION_PART1 = 5 TEST_SOLUTION_PART2 = 8 +def optimizeInput(input_array): + return_array = [] + for line in input_array: + command, param = line.split() + param = int(param) + return_array.append((command, param)) + + return return_array + + def runCode(lines, infinite_returns_value=False, infinite_returns_set=False): accumulator = 0 line_pointer = 0 @@ -20,34 +30,35 @@ def runCode(lines, infinite_returns_value=False, infinite_returns_set=False): return None lines_executed.add(line_pointer) - command, param = lines[line_pointer].split() + command, param = lines[line_pointer] if command == 'nop': line_pointer += 1 elif command == 'acc': - accumulator = accumulator + int(param) + accumulator = accumulator + param line_pointer += 1 elif command == 'jmp': - line_pointer = line_pointer + int(param) + line_pointer = line_pointer + param return accumulator def part1(test_mode=False): my_input = aoclib.getInputAsArray(day=DAY, test=test_mode) - return runCode(my_input, infinite_returns_value=True) + return runCode(optimizeInput(my_input), infinite_returns_value=True) def part2(test_mode=False): my_input = aoclib.getInputAsArray(day=DAY, test=test_mode) - for exec_line in runCode(my_input, infinite_returns_set=True): - if not my_input[exec_line].startswith('nop') and not my_input[exec_line].startswith('jmp'): + opt_input = optimizeInput(my_input) + for exec_line in runCode(opt_input, infinite_returns_set=True): + if opt_input[exec_line][0] not in ['nop', 'jmp']: continue - check_code = my_input[:] - if my_input[exec_line].startswith('jmp'): - check_code[exec_line] = check_code[exec_line].replace('jmp', 'nop') - elif my_input[exec_line].startswith('nop'): - check_code[exec_line] = check_code[exec_line].replace('nop', 'jmp') + check_code = opt_input[:] + if opt_input[exec_line][0] == 'jmp': + check_code[exec_line] = ('nop', check_code[exec_line][1]) + elif opt_input[exec_line][0] == 'nop': + check_code[exec_line] = ('jmp', check_code[exec_line][1]) check_answer = runCode(check_code) if check_answer is not None: