We've got a somewhat similar problem to the one about dancing. Here we have a set of sounds that are going to be assigned different frequency. We have a notion of a sound being 'played' which sort of serves as a marker in the program. The sounds can also be 'recovered' which reverts them back to their most recent played value. This again looks like I'm going to come up with a function or few that mimic the moves and then let it run. I then expect the second part to ask me about 1 trillion iterations or something and I'll have to come up with a trick.
Start by loading up the instructions and also creating some data structures to hold current and most recent data:
import string
instructions = open('/Users/Sven/py_files/aoc_2017/d18_input.txt').readlines()
instructions = [inst.replace('\n', '') for inst in instructions]
# dictionaries seem good for this kind of data:
letters = string.ascii_letters
zeroes = [0 for i in range(26)]
played = []
current = dict(zip(letters, zeroes))
Now we'll go ahead and make a function to handle the instructions... I guess it's probably better to just use a single one isntead of many
def read_inst(play, curr, i):
# get the type of move, target, and value
inst = instructions[i].split()
inst_t, inst_x = inst[0], inst[1]
if len(inst) == 3:
inst_y = inst[2]
# if the instructions reference one of the registers, get that value
# otherwise, turn to int:
if inst_y in letters:
inst_y = current[inst_y]
else:
inst_y = int(inst_y)
# note that we haven't determined if inst_x is int or str
# Use this to determine if we've recovered something or not:
rcv = 0
# So I guess we gotta just go through all the cases now... I looked up switches
# in Python but they don't seem that much better than a longass ifelse here
if inst_t == 'snd':
play = [curr[inst_x]]
elif inst_t == 'set':
curr[inst_x] = inst_y
elif inst_t == 'add':
curr[inst_x] += inst_y
elif inst_t == 'mul':
curr[inst_x] *= inst_y
elif inst_t == 'mod':
curr[inst_x] %= inst_y
elif inst_t == 'rcv':
if curr[inst_x] > 0:
print(play)
rcv = 1
elif inst_t == 'jgz':
if curr[inst_x] != 0:
i += inst_y
# now increment i if we need to:
if inst_t != 'jgz' or curr[inst_x] == 0:
i += 1
return(play, curr, i, rcv)
Now we keep iterating the function until we receive a nonzero value for rcv
i = 0
play = played
curr = current
rcv = 0
while rcv == 0:
play, curr, i, rcv = read_inst(play, curr, i)
Dang, that multiple return value is much simpler than in R. I'm not sure I really like how I used it as 4 return values seems like a bit much but we'll consider using that in the future.
We've been informed that this is not actually a question about sounds, it's about sending and receiving values between two simultaneous executions of the program. The snd
and rcv
keywords send and receive a value from one side to the other. The two programs have different starting registries. Each program runs the set of instructions in order however they will sit and wait to receive instructions if none are present in the queue for them. We wish to run the program until either both sides are waiting to receive or the program runs its course.
Alright, I'm going to try a new approach using class definitions which is a little uncomfortable for me but let's try. I'm much more comfortable with the functional programming paradigm but I should learn how to work with this perspective.
First a class definition for the computers:
class program:
# Now we make the initialization function which gives it the basic properties
def __init__(self, name, init_reg):
letters = string.ascii_letters
zeroes = [init_reg for i in range(26)]
# name
self.name = name
# Registry
self.reg = dict(zip(letters, zeroes))
# Incomming queue
self.queue = []
# location within the instructions
self.i = 0
# Is it stalled?
self.stalled = 0
# How many sent?
self.n_sent = 0
# Method to receive data, we'll ignore None as inputs so we can
# just have the method receive that when the other program is not sending
def add_to_queue(self, val):
if val is not None:
self.queue.append(val)
self.stalled = 0
# Method to read instrutions
def read_instructions(self):
# check if we're beyond the bounds of the instructions
if self.i >= len(instructions) or self.i < 0:
self.stalled = 1
return(None)
# get the type of move, target, and value
inst = instructions[self.i].split()
#if self.name == 'a':
# print(inst)
inst_t, inst_x = inst[0], inst[1]
if len(inst) == 3:
inst_y = inst[2]
# if the instructions reference one of the registers, get that value
# otherwise, turn to int:
if inst_y in string.ascii_letters:
inst_y = self.reg[inst_y]
else:
inst_y = int(inst_y)
# note that we haven't determined if inst_x is int or str
# If we need to return something:
ret = None
# So I guess we gotta just go through all the cases now... I looked up switches
# in Python but they don't seem that much better than a longass ifelse here
if inst_t == 'snd':
ret = self.reg[inst_x]
self.n_sent += 1
elif inst_t == 'set':
self.reg[inst_x] = inst_y
elif inst_t == 'add':
self.reg[inst_x] += inst_y
elif inst_t == 'mul':
self.reg[inst_x] *= inst_y
elif inst_t == 'mod':
self.reg[inst_x] %= inst_y
elif inst_t == 'rcv':
# if there's items in the queue, use them
if len(self.queue) > 0:
self.reg[inst_x] = self.queue.pop(0)
self.stalled = 0
# Otherwise, set the stall flag, don't increment i
else:
self.stalled = 1
return(None)
elif inst_t == 'jgz':
# Deal with cases of inst_x being an integer:
if inst_x in self.reg:
chk = self.reg[inst_x]
else:
chk = int(inst_x)
if chk > 0:
self.i += inst_y
# now increment i if we need to:
if inst_t != 'jgz' or chk <= 0:
self.i += 1
return(ret)
# Initialize our programs.
progA = program('a', 0)
progB = program('b', 1)
Now we'll let the programs go while at least one program is not stalled:
while True:
while progA.stalled == 0:
progB.add_to_queue(progA.read_instructions())
while progB.stalled == 0:
progA.add_to_queue(progB.read_instructions())
if progA.stalled != 0 and progB.stalled != 0:
break
progB.n_sent
Well I don't know if this was an extremely ugly or nice solution but I'm somewhat proud of it. It seems like the basic implementation of classes in python is not that difficult. I'm sure we can make it so in the future though!