Advent of Code 2020 day 8
Advent of Code 2020 day 8. Happy with the logic, but not particularly happy with the implementation: lots of while
loops and if
statements that all look the same when you squint at the code.
Rather than reuse the same Console
object for each iteration in part two and reset it’s state, I wish I’d created a new object for each iteration. But this works and I’m behind, so on to day 9.
from pathlib import Path
import unittest
def get_raw_input():
return (Path(__file__).parent/'day_08_input.txt').read_text()
class Console():
def __init__(self, raw_input):
self.instructions = self.parse_raw_input(raw_input)
self.accumulator = 0
self.next_instruction = 0
self.instruction_log = []
self.mutate_line = -1
@staticmethod
def parse_raw_input(raw_input):
return [
(line.strip().split()[0], int(line.strip().split()[1]))
for line
in raw_input.strip().splitlines()
]
def reset_state(self):
self.accumulator = 0
self.next_instruction = 0
self.instruction_log = []
def run(self):
while self.next_instruction not in self.instruction_log:
try:
current_instruction = self.instructions[self.next_instruction]
except IndexError:
return (self.accumulator, 0)
self.instruction_log.append(self.next_instruction)
self.next_instruction += 1
if current_instruction[0] == 'acc':
self.accumulator += int(current_instruction[1])
elif current_instruction[0] == 'jmp':
self.next_instruction += (int(current_instruction[1]) - 1)
elif current_instruction[0] == 'nop':
pass
return self.accumulator
def mutate_input(self):
last_line_mutated = self.mutate_line
if last_line_mutated >= 0:
# Revert previous mutation
if self.instructions[last_line_mutated][0] == 'nop':
self.instructions[last_line_mutated] = ('jmp', self.instructions[last_line_mutated][1])
else:
self.instructions[last_line_mutated] = ('nop', self.instructions[last_line_mutated][1])
self.reset_state()
self.mutate_line += 1
while self.instructions[self.mutate_line][0] not in ['nop', 'jmp']:
self.mutate_line += 1
if self.instructions[self.mutate_line][0] == 'nop':
self.instructions[self.mutate_line] = ('jmp', self.instructions[self.mutate_line][1])
else:
self.instructions[self.mutate_line] = ('nop', self.instructions[self.mutate_line][1])
return True
def part_one(raw_input):
return Console(raw_input).run()
def part_two(raw_input):
c = Console(raw_input)
while type(c.run()) != tuple:
c.mutate_input()
return c.accumulator
class TestExamples(unittest.TestCase):
def setUp(self):
self.example_input = """nop +0
acc +1
jmp +4
acc +3
jmp -3
acc -99
acc +1
jmp -4
acc +6"""
def test_part_one(self):
self.assertEqual(part_one(self.example_input), 5)
def test_part_one(self):
self.assertEqual(part_two(self.example_input), 8)
if __name__ == '__main__':
print(f'Part one: {part_one(get_raw_input())}')
print(f'Part two: {part_two(get_raw_input())}')