I'm currently using the Advent of Code programming challenges as an opportunity to learn some python. The following code is my solution for the Day 8 challenge, where the task is to write a programm that reads a set of instructions which are then performed on registers. Registers start with a value of 0. Each instruction may increase or decrease the value of a register. The programm should return the maximum value in any register at the end of execution as well as the maximum that any register had during the whole process.
An instructions looks as follows: <register> <operation> <integer> if <register> <boolop> <integer>
.
Where register
is a name of a register, operation
is either inc
to increase the value of a register, or dec
to decrease its value. boolop
is a comparison operator. The instruction should only be executed if the condition is true, e.g. a inc 10 if b < 0
will increase the value of the register a
by 10 if the current value for register b
is less than 0.
I read the input from a file called in
. An example input can be found here. The expected output for this input would be (2971, 4254)
.
import re
def is_int(str):
if str == '':
return False
if str[0] in ('+', '-'):
return str[1:].isdecimal()
return str.isdecimal()
def value(registers, val):
if is_int(val):
return int(val)
if not val in registers:
registers[val] = 0
return registers[val]
def max_register(registers):
return max([x for _, x in registers.items()])
class Operation:
def __init__(self, line):
res = re.search('([^ ]*) ([^ ]*) ([^ ]*)', line)
self.left = res.group(1)
self.operator = res.group(2)
self.right = res.group(3)
def __repr__(self):
return self.left + ' ' + self.operator + ' ' + self.right
def perform(self, registers):
l, r = value(registers, self.left), value(registers, self.right)
if self.operator == 'inc':
registers[self.left] = l + r
elif self.operator == 'dec':
registers[self.left] = l - r
else:
print('Unsupported operation', self.operator)
class Condition:
def __init__(self, line):
res = re.search('if ([^ ]*) ([^ ]*) ([^ ]*)', line)
self.left = res.group(1)
self.operator = res.group(2)
self.right = res.group(3)
def __repr__(self):
return self.left + ' ' + self.operator + ' ' + self.right
def test(self, registers):
l, r = (value(registers, self.left), value(registers, self.right))
if self.operator == '==':
return l == r
elif self.operator == '!=':
return l != r
if self.operator == '>':
return l > r
elif self.operator == '>=':
return l >= r
elif self.operator == '<':
return l < r
elif self.operator == '<=':
return l <= r
else:
print('Unsupported condition operator', self.operator)
return False
class Instruction:
def __init__(self, line):
res = re.search('(.*) (if .*)', line)
self.operation = Operation(res.group(1))
self.condition = Condition(res.group(2))
def __repr__(self):
return str(self.operation) + ' ' + str(self.condition)
def perform(self, registers):
if self.condition.test((registers)):
self.operation.perform(registers)
class Programm:
def __init__(self, instr):
self.instructions = instr
self.position = 0
def __repr__(self):
return '@' + self.position + '\n' + str(self.instructions)
def run_next(self, registers):
if self.position >= len(self.instructions):
return False
self.instructions[self.position].perform(registers)
self.position = self.position + 1
return True
def run(self, registers):
for _ in range(len(self.instructions)):
self.run_next(registers)
def parse_programm(file):
lines = file.read().splitlines()
return Programm([Instruction(i) for i in lines])
def run(input):
with open(input) as infile:
programm = parse_programm(infile)
registers = {}
curmax = 0
while programm.run_next(registers):
curmax = max(curmax, max_register(registers))
return (max_register(registers), curmax)
if __name__ == '__main__':
print(run("in"))
I am especially interested in whether the code is pythonic and how it could be improved. E.g. the use of regexes to parse the instructions feels kinda hacky.
is_int
function is pointless (just cast toint
); and it doesn't make sense to usere.search
overre.match
; and probably a bunch of other stuff. \$\endgroup\$is_int
function was so simplify the usage ofvalue()
. I wanted a function where I can input astring
or andint
and getting back the value of the register matching thestring
or theint
value itself. \$\endgroup\$is_int(x)
would returnTrue
for values ofx
that were not ints, but rather, register names? That sounds like your current code is redundant but your future code is actively confusing! \$\endgroup\$value(int)
will return the value, butvalue(string)
will return the current value of the register with this name. Theis_int
function is just a helper to distinguish between those too cases invalue()
and will of course only returnTrue
, wenn thex
is anint
. \$\endgroup\$