2020-12-8

Created on Monday, December 23, 2024.

Day 8 - Is this revenge of IntCode?

My first look at this and I'm beginning to dread that we'll have something similar to IntCode from last year. I hated my IntCode computer by half way through and it was one of the reasons for dropping out last year, as I spent more time debugging my virtual computer than I did trying to solve the problem.

Anyway, let's not do that this time.

This part 1 can almost certainly be solved by fairly simple means, let's not overcomplicate things. We load the lines into an list of strings, have a line number, and start at 1. For each string we parse the string, either add to the accumulator, or change the line number, add 1 to the line number and then go again. But we'll add a set of seen lines as we go over them, and check whether we've already seen the line, if we have, print out the value of the accumulator and quit.

import ipytest
ipytest.autoconfig()

lines = [
    "nop +0",
"acc +1",
"jmp +4",
"acc +3",
"jmp -3",
"acc -99",
"acc +1",
"jmp -4",
"acc +6"
]

def parse1(lines):
    line = 0
    seen = set()
    acc = 0
    while True:
        currentline = lines[line]
        seen.add(line)
        if currentline[:3] == "jmp":
            line += int(currentline[4:])
        else:
            if currentline[:3] == "acc":
                acc += int(currentline[4:])
            # Always increment line after acc or nop
            line += 1
        if line in seen:
            break
    return acc

assert parse1(lines) == 5

Great, that works, let's try that on the real data

lines = [line.strip() for line in open('day8.txt')]
print(parse1(lines))

1548

Part 2 - Mutate the program

This looks horrible at first sight, and it actually is. What we need to do is mutate each jmp line into a nop line, and rerun the program, keep doing this until we find one that ends properly.

In this case, there are now 2 ending conditions, we ran off the end of the program (success) and we hit a loop (fail), so we'll need to return not just the accumulator value, but which one we did.

We then need to iterate through each line, swapping a nop for a jmp and a jmp for a nop, and try the program, until we get to the end.

import ipytest
ipytest.autoconfig()

lines_loop = [
    "nop +0",
"acc +1",
"jmp +4",
"acc +3",
"jmp -3",
"acc -99",
"acc +1",
"jmp -4",
"acc +6"
]

lines_end = [
    "nop +0",
"acc +1",
"jmp +4",
"acc +3",
"jmp -3",
"acc -99",
"acc +1",
"nop -4",
"acc +6"
]

def parse2(lines):
    line = 0
    seen = set()
    acc = 0
    while True:
        currentline = lines[line]
        seen.add(line)
        if currentline[:3] == "jmp":
            line += int(currentline[4:])
        else:
            if currentline[:3] == "acc":
                acc += int(currentline[4:])
            # Always increment line after acc or nop
            line += 1
        if line in seen:
            return (False,acc)
        if line >= len(lines):
            return (True,acc)

assert parse2(lines_loop) == (False,5)
assert parse2(lines_end) == (True,8)

Now let's try mutating all the lines on the sample

def mutate_lines(lines):
    for i,line in enumerate(lines):
        newlist = lines.copy()
        if line[:3] == "nop":
            newlist[i] = line.replace("nop", "jmp")
        elif line[:3] == "jmp":
            newlist[i] = line.replace("jmp", "nop")
        ret,acc = parse2(newlist)
        if ret:
            return (True,acc)
    return (False,acc)

assert mutate_lines(lines_loop) == (True,8)

Great, that works just fine. Let's try it on the real data

print(mutate_lines(lines))

(True, 1375)

Next

2020-12-9

Previous

2020-12-7