#advent of code 2018 #day 21 #part 1 and part 2 #this puzzle in my opinion was poorly worded, or I'm just overthinking #part 1 was suprisingly simple since it only required running the solution for day 19 #unfortunately, bruteforcing the answer for part 2 didn't go as well #reading through input its possible to identify instruction jumps and determine what causes them #I was stuck for like 2 days for part 2: I couldn't find a bug in my reverse-engineering #I even run someone else's solution (modified for my input) to verify it #only to get another wrong answer #I found a second solution and I compared it to mine #only to find out that the issue wasn't in logic, I simply entered wrong value in 1 line #at least this reverse-engineering puzzle was for me more enjoyable than the 2021 one #part 1 bruteforce #program halts if the value at register 0 matches the value at register X #(register X is between 1 and 5, depends on the input) #in one particular instruction near the end #instruction 28 in my case #calculating this value provides part 1 result #part 2 reverse engineering #you can simplify the input program by reverse-engineering #basically the first few lines are trash, then the program is initiated with starting values #if condition 1 is met, program enters a loop until condition 1 is no longer true #after this loop, program checks if the value at register 0 is equal to corresponding register X #if true, program halts #you can print out all values at register X, first printed message is part1, last one is part 2 ###opcode functions #bit modified version of day 19 def addr(before, op): bef = before.copy(); C = bef[op[1]] + bef[op[2]]; bef[op[3]] = C; return bef; def addi(before, op): bef = before.copy(); C = bef[op[1]] + op[2]; bef[op[3]] = C; return bef; def mulr(before, op): bef = before.copy(); C = bef[op[1]] * bef[op[2]]; bef[op[3]] = C; return bef; def muli(before, op): bef = before.copy(); C = bef[op[1]] * op[2]; bef[op[3]] = C; return bef; def banr(before, op): bef = before.copy(); C = int(bef[op[1]] & bef[op[2]]); bef[op[3]] = C; return bef; def bani(before, op): bef = before.copy(); C = int(bef[op[1]] & op[2]); bef[op[3]] = C; return bef; def borr(before, op): bef = before.copy(); C = int(bef[op[1]] | bef[op[2]]); bef[op[3]] = C; return bef; def bori(before, op): bef = before.copy(); C = int(bef[op[1]] | op[2]); bef[op[3]] = C; return bef; def setr(before, op): bef = before.copy(); C = bef[op[1]]; bef[op[3]] = C; return bef; def seti(before, op): bef = before.copy(); C = op[1]; bef[op[3]] = C; return bef; def gtir(before, op): bef = before.copy(); C = 0 + int( op[1] > bef[op[2]] ); bef[op[3]] = C; return bef; def gtri(before, op): bef = before.copy(); C = 0 + int( op[2] < bef[op[1]] ); bef[op[3]] = C; return bef; def gtrr(before, op): bef = before.copy(); C = 0 + int( bef[op[1]] > bef[op[2]] ); bef[op[3]] = C; return bef; def eqir(before, op): bef = before.copy(); C = 0 + int( op[1] == bef[op[2]] ); bef[op[3]] = C; return bef; def eqri(before, op): bef = before.copy(); C = 0 + int( op[2] == bef[op[1]] ); bef[op[3]] = C; return bef; def eqrr(before, op): bef = before.copy(); C = 0 + int( bef[op[1]] == bef[op[2]] ); bef[op[3]] = C; return bef; MatchFunctions = {}; MatchFunctions.update({"addr":addr}); MatchFunctions.update({"addi":addi}); MatchFunctions.update({"mulr":mulr}); MatchFunctions.update({"muli":muli}); MatchFunctions.update({"banr":banr}); MatchFunctions.update({"bani":bani}); MatchFunctions.update({"borr":borr}); MatchFunctions.update({"bori":bori}); MatchFunctions.update({"setr":setr}); MatchFunctions.update({"seti":seti}); MatchFunctions.update({"gtir":gtir}); MatchFunctions.update({"gtri":gtri}); MatchFunctions.update({"gtrr":gtrr}); MatchFunctions.update({"eqir":eqir}); MatchFunctions.update({"eqri":eqri}); MatchFunctions.update({"eqrr":eqrr}); #end of day19 copy-paste ############################################### f = open("input.txt",'r'); Testing = False; #Testing = True; if Testing: f.close(); f = open("testinput.txt",'r'); ip = int(f.readline()[4:]); instructions = []; for l in f: l = l.split(); i = []; i.append(l[0]); l_i = [int(x) for x in l[1:]]; i += l_i; instructions.append(i); part1 = None; instr_lim = len(instructions); while True: reg = [0,0,0,0,0,0]; reg[ip] = 0; part1 = None; while True: if( reg[ip] >= instr_lim): break; if( reg[ip] >= 28): #print(reg); part1 = int(reg[5]); break; instr = instructions[reg[ip]]; fun = MatchFunctions[instr[0]]; reg = fun(reg,instr); reg[ip] += 1; if part1 != None: break; print("part 1 = ", part1, "(bruteforce)"); #part2 store1 = set(); #var ehich determines if the loop continues store5 = set(); #var checked against the reg0 part1 = None; part2 = None; p2 = None; reg5 = 10678677; reg1 = 2**16; while True: reg4 = reg1%256; reg5 += reg4; reg5 = ((reg5%2**24)*65899)%(2**24); if reg1<256: if not store5: part1 = int(reg5); if reg5 not in store5: part2 = int(reg5); store5.add(reg5); #print("regs", reg1, reg5) reg1 = reg5 | 2**16; if reg1 in store1: break; store1.add(reg1); reg5 = 10678677; continue; reg1 = reg1//256; print("part 1 = ", part1); print("part 2 = ", part2); #my input ''' #ip 2 seti 123 0 5 bani 5 456 5 eqri 5 72 5 addr 5 2 2 seti 0 0 2 seti 0 4 5 bori 5 65536 1 seti 10678677 3 5 bani 1 255 4 addr 5 4 5 bani 5 16777215 5 muli 5 65899 5 bani 5 16777215 5 gtir 256 1 4 addr 4 2 2 addi 2 1 2 seti 27 5 2 seti 0 6 4 addi 4 1 3 muli 3 256 3 gtrr 3 1 3 addr 3 2 2 addi 2 1 2 seti 25 4 2 addi 4 1 4 seti 17 6 2 setr 4 6 1 seti 7 5 2 eqrr 5 0 4 addr 4 2 2 seti 5 4 2 '''