#!/usr/bin/env python instructions = """set i 31 set a 1 mul p 17 jgz p p mul a 2 add i -1 jgz i -2 add a -1 set i 127 set p 680 mul p 8505 mod p a mul p 129749 add p 12345 mod p a set b p mod b 10000 snd b add i -1 jgz i -9 jgz a 3 rcv b jgz b -1 set f 0 set i 126 rcv a rcv b set p a mul p -1 add p b jgz p 4 snd a set a b jgz 1 3 snd b set f 1 add i -1 jgz i -11 snd a jgz f -16 jgz a -19 """.splitlines() instructions = [instruction.split() for instruction in instructions] instruction_counter = [0] recovered_frequency = [0] registers = {} def my_set(other_pid, pid, regs, rf, pc, queue, is_waiting, x,y): if y in regs: regs[x]=regs[y] else: regs[x]=int(y) def my_add(other_pid, pid, regs, rf, pc, queue, is_waiting, x,y): if x not in regs and not x.isdigit(): regs[x] = 0 if y in regs: regs[x] += regs[y] else: regs[x] += int(y) def my_sound(other_pid, pid, regs, rf, pc, queue, is_waiting, x): if x in regs: rf[0] = regs[x] else: rf[0] = int(x) def my_mul(other_pid, pid, regs, rf, pc, queue, is_waiting, x,y): if x not in regs and not x.isdigit(): regs[x] = 0 if y in regs: regs[x] *= regs[y] else: regs[x] *= int(y) def my_mod(other_pid, pid, regs, rf, pc, queue, is_waiting, x,y): if x not in regs and not x.isdigit(): regs[x] = 0 if y in regs: regs[x] %= regs[y] else: regs[x] %= int(y) def my_recover(other_pid, pid, regs, rf, pc, queue, is_waiting, x): if ((x in regs and regs[x]!=0) or (x.isdigit() and int(x)!=0)): return rf else: return None def my_jgz(other_pid, pid, regs, rf, pc, queue, is_waiting, x,y): if x not in regs and not x.isdigit(): regs[x] = 0 if ((x in regs and regs[x]>0) or (x.isdigit() and int(x)>0)): if y in regs: pc[pid[0]]+=regs[y]-1 # pc[0]+=1 will fix one-off else: pc[pid[0]]+=int(y)-1 # pc[0]+=1 will fix one-off op_dict = {'set': my_set, 'add': my_add, 'snd': my_sound, 'mul': my_mul, 'mod': my_mod, 'rcv': my_recover, 'jgz': my_jgz} def parse(program,pc,regs,op_dict,rf): return_value = None program_length = len(program) op = None pid = [0] while(pc[0] < program_length): op = program[pc[0]][0] return_value = op_dict[op](None, pid, regs, rf, pc, None, None, *program[pc[0]][1:]) if op=='rcv': return "Recovered frequency = " + str(return_value) pc[0]+=1 print(parse(program=instructions,pc=instruction_counter,regs=registers,op_dict=op_dict,rf=recovered_frequency)) # Part 2 # REMOVE ALL INSTANCES OF "###" (without quotes) # FROM THIS FILE TO STEP THROUGH PART 2 INTERACTIVELY my_queue = [[],[]] is_waiting = [False, False] instruction_counter = [0, 0] registers = [{},{}] def check_if_deadlocked(queue,is_waiting,pc,program_length): if all([len(queue_item)==0 for queue_item in queue]) and all(is_waiting): ###print(">\tOops! All programs deadlocked! ([pc[pid=0], pc2[pid=1]]/program_length) is " + str(pc) + "/" + str(program_length)) terminate_all_programs(pc,program_length) def terminate_all_programs(pc,program_length): pc[0] = program_length pc[1] = program_length program_1_sends_count = 0 def my_send(to_pid, my_pid, regs, rf, pc, queue, is_waiting, x): global program_1_sends_count if x not in regs and not x.isdigit(): regs[x] = 0 if x in regs: queue[to_pid[0]].insert(0,regs[x]) else: queue[to_pid[0]].insert(0,int(x)) if my_pid[0] == 1: program_1_sends_count+=1 ###print(">\tQueue updated to " + str(queue)) def my_receive(from_pid, my_pid, regs, rf, pc, queue, is_waiting, x): if x not in regs and not x.isdigit(): regs[x] = 0 if len(queue[my_pid[0]])==0: is_waiting[my_pid[0]]=True else: is_waiting[my_pid[0]]=False regs[x] = int(queue[my_pid[0]].pop()) # reinterpreting op codes op_dict['snd'] = my_send op_dict['rcv'] = my_receive # creating new parse function for part 2 def parse2(pid,program,pc,regs_list,op_dict,queue,is_waiting): return_value = None program_length = len(program) op = None other_pid = [1] regs = regs_list[pid[0]] if pid[0]==1: other_pid[0] = 0 # setting p = pid for *both* programs op_dict['set'](other_pid, pid, regs_list[pid[0]], None, pc, queue, is_waiting, 'p', pid[0]) op_dict['set'](pid, other_pid, regs_list[other_pid[0]], None, pc, queue, is_waiting, 'p', other_pid[0]) while(pc[pid[0]] < program_length): op = program[pc[pid[0]]][0] ###print(program[pc[pid[0]]]) return_value = op_dict[op](other_pid, pid, regs, None, pc, queue, is_waiting, *program[pc[pid[0]]][1:]) ###print(regs) check_if_deadlocked(queue,is_waiting,pc,program_length) if not is_waiting[pid[0]]: pc[pid[0]]+=1 ###print("Incrementing pc to " + str(pc[pid[0]])) ###print("\t(Press enter to continue...)") ###raw_input() else: # since current program is waiting # switching context to other program pid[0] = 1 if pid[0]==0 else 0 other_pid[0] = 1 if other_pid[0]==0 else 0 regs = regs_list[pid[0]] ###print(">\tContext switched to pid=" + str(pid[0])) ###print(" ") # creating wrapper for parse function def parse_wrapper(x): global instructions, instruction_counter, registers, op_dict, my_queue, is_waiting return parse2(pid=x, program=instructions, pc=instruction_counter, regs_list=registers, op_dict=op_dict, queue=my_queue, is_waiting=is_waiting) currently_running_pid = [0] parse_wrapper(currently_running_pid) print ("Program 1's sends' count: " + str(program_1_sends_count))