aboutsummaryrefslogtreecommitdiffstats
path: root/src/19
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2023-04-07 17:18:18 -0400
committerLouis Burda <quent.burda@gmail.com>2023-04-07 17:19:39 -0400
commit87ab487d59fa85dbe2afa55cc841b02805ae42ca (patch)
treecd90ab715e1b5b5803674045dbafd6d51d27ac90 /src/19
parent1bcc82c5bfbde87edd03c01ffdf9ee5934681592 (diff)
downloadaoc2018-python-87ab487d59fa85dbe2afa55cc841b02805ae42ca.tar.gz
aoc2018-python-87ab487d59fa85dbe2afa55cc841b02805ae42ca.zip
Reorder days into src
Diffstat (limited to 'src/19')
-rw-r--r--src/19/input37
-rw-r--r--src/19/part192
-rw-r--r--src/19/part258
-rw-r--r--src/19/solve.py109
-rw-r--r--src/19/test18
5 files changed, 304 insertions, 0 deletions
diff --git a/src/19/input b/src/19/input
new file mode 100644
index 0000000..0392b90
--- /dev/null
+++ b/src/19/input
@@ -0,0 +1,37 @@
+#ip 4
+addi 4 16 4
+seti 1 5 1
+seti 1 2 2
+mulr 1 2 3
+eqrr 3 5 3
+addr 3 4 4
+addi 4 1 4
+addr 1 0 0
+addi 2 1 2
+gtrr 2 5 3
+addr 4 3 4
+seti 2 7 4
+addi 1 1 1
+gtrr 1 5 3
+addr 3 4 4
+seti 1 9 4
+mulr 4 4 4
+addi 5 2 5
+mulr 5 5 5
+mulr 4 5 5
+muli 5 11 5
+addi 3 1 3
+mulr 3 4 3
+addi 3 18 3
+addr 5 3 5
+addr 4 0 4
+seti 0 3 4
+setr 4 2 3
+mulr 3 4 3
+addr 4 3 3
+mulr 4 3 3
+muli 3 14 3
+mulr 3 4 3
+addr 5 3 5
+seti 0 4 0
+seti 0 5 4
diff --git a/src/19/part1 b/src/19/part1
new file mode 100644
index 0000000..1a9c60b
--- /dev/null
+++ b/src/19/part1
@@ -0,0 +1,92 @@
+--- Day 19: Go With The Flow ---
+
+With the Elves well on their way constructing the North Pole base, you turn your attention back to
+understanding the inner workings of programming the device.
+
+You can't help but notice that the device's opcodes don't contain any flow control like jump
+instructions. The device's manual goes on to explain:
+
+"In programs where flow control is required, the instruction pointer can be bound to a register so
+that it can be manipulated directly. This way, setr/seti can function as absolute jumps, addr/addi
+can function as relative jumps, and other opcodes can cause truly fascinating effects."
+
+This mechanism is achieved through a declaration like #ip 1, which would modify register 1 so that
+accesses to it let the program indirectly access the instruction pointer itself. To compensate for
+this kind of binding, there are now six registers (numbered 0 through 5); the five not bound to the
+instruction pointer behave as normal. Otherwise, the same rules apply as the last time you worked
+with this device.
+
+When the instruction pointer is bound to a register, its value is written to that register just
+before each instruction is executed, and the value of that register is written back to the
+instruction pointer immediately after each instruction finishes execution. Afterward, move to the
+next instruction by adding one to the instruction pointer, even if the value in the instruction
+pointer was just updated by an instruction. (Because of this, instructions must effectively set the
+instruction pointer to the instruction before the one they want executed next.)
+
+The instruction pointer is 0 during the first instruction, 1 during the second, and so on. If the
+instruction pointer ever causes the device to attempt to load an instruction outside the
+instructions defined in the program, the program instead immediately halts. The instruction pointer
+starts at 0.
+
+It turns out that this new information is already proving useful: the CPU in the device is not very
+powerful, and a background process is occupying most of its time. You dump the background process'
+declarations and instructions to a file (your puzzle input), making sure to use the names of the
+opcodes rather than the numbers.
+
+For example, suppose you have the following program:
+
+#ip 0
+seti 5 0 1
+seti 6 0 2
+addi 0 1 0
+addr 1 2 3
+setr 1 0 0
+seti 8 0 4
+seti 9 0 5
+
+When executed, the following instructions are executed. Each line contains the value of the
+instruction pointer at the time the instruction started, the values of the six registers before
+executing the instructions (in square brackets), the instruction itself, and the values of the six
+registers after executing the instruction (also in square brackets).
+
+ip=0 [0, 0, 0, 0, 0, 0] seti 5 0 1 [0, 5, 0, 0, 0, 0]
+ip=1 [1, 5, 0, 0, 0, 0] seti 6 0 2 [1, 5, 6, 0, 0, 0]
+ip=2 [2, 5, 6, 0, 0, 0] addi 0 1 0 [3, 5, 6, 0, 0, 0]
+ip=4 [4, 5, 6, 0, 0, 0] setr 1 0 0 [5, 5, 6, 0, 0, 0]
+ip=6 [6, 5, 6, 0, 0, 0] seti 9 0 5 [6, 5, 6, 0, 0, 9]
+
+In detail, when running this program, the following events occur:
+
+
+ - The first line (#ip 0) indicates that the instruction pointer should be bound to register 0 in
+this program. This is not an instruction, and so the value of the instruction pointer does not
+change during the processing of this line.
+
+ - The instruction pointer contains 0, and so the first instruction is executed (seti 5 0 1). It
+updates register 0 to the current instruction pointer value (0), sets register 1 to 5, sets the
+instruction pointer to the value of register 0 (which has no effect, as the instruction did not
+modify register 0), and then adds one to the instruction pointer.
+
+ - The instruction pointer contains 1, and so the second instruction, seti 6 0 2, is executed. This
+is very similar to the instruction before it: 6 is stored in register 2, and the instruction pointer
+is left with the value 2.
+
+ - The instruction pointer is 2, which points at the instruction addi 0 1 0. This is like a
+relative jump: the value of the instruction pointer, 2, is loaded into register 0. Then, addi finds
+the result of adding the value in register 0 and the value 1, storing the result, 3, back in
+register 0. Register 0 is then copied back to the instruction pointer, which will cause it to end up
+1 larger than it would have otherwise and skip the next instruction (addr 1 2 3) entirely. Finally,
+1 is added to the instruction pointer.
+
+ - The instruction pointer is 4, so the instruction setr 1 0 0 is run. This is like an
+absolute jump: it copies the value contained in register 1, 5, into register 0, which causes it to
+end up in the instruction pointer. The instruction pointer is then incremented, leaving it at 6.
+
+ - The instruction pointer is 6, so the instruction seti 9 0 5 stores 9 into register 5. The
+instruction pointer is incremented, causing it to point outside the program, and so the program
+ends.
+
+
+What value is left in register 0 when the background process halts?
+
+
diff --git a/src/19/part2 b/src/19/part2
new file mode 100644
index 0000000..3bfe430
--- /dev/null
+++ b/src/19/part2
@@ -0,0 +1,58 @@
+--- Part Two ---
+
+You aren't sure how large Santa's ship is. You aren't even sure if you'll need to use this thing on
+Santa's ship, but it doesn't hurt to be prepared. You figure Santa's ship might fit in a
+100x100 square.
+
+The beam gets wider as it travels away from the emitter; you'll need to be a minimum distance away
+to fit a square of that size into the beam fully. (Don't rotate the square; it should be aligned to
+the same axes as the drone grid.)
+
+For example, suppose you have the following tractor beam readings:
+
+#.......................................
+.#......................................
+..##....................................
+...###..................................
+....###.................................
+.....####...............................
+......#####.............................
+......######............................
+.......#######..........................
+........########........................
+.........#########......................
+..........#########.....................
+...........##########...................
+...........############.................
+............############................
+.............#############..............
+..............##############............
+...............###############..........
+................###############.........
+................#################.......
+.................########OOOOOOOOOO.....
+..................#######OOOOOOOOOO#....
+...................######OOOOOOOOOO###..
+....................#####OOOOOOOOOO#####
+.....................####OOOOOOOOOO#####
+.....................####OOOOOOOOOO#####
+......................###OOOOOOOOOO#####
+.......................##OOOOOOOOOO#####
+........................#OOOOOOOOOO#####
+.........................OOOOOOOOOO#####
+..........................##############
+..........................##############
+...........................#############
+............................############
+.............................###########
+
+In this example, the 10x10 square closest to the emitter that fits entirely within the tractor beam
+has been marked O. Within it, the point closest to the emitter (the only highlighted O) is at X=25,
+Y=20.
+
+Find the 100x100 square closest to the emitter that fits entirely within the tractor beam; within
+that square, find the point closest to the emitter. What value do you get if you take that point's
+X coordinate, multiply it by 10000, then add the point's Y coordinate? (In the example above, this
+would be 250020.)
+
+
diff --git a/src/19/solve.py b/src/19/solve.py
new file mode 100644
index 0000000..c0c41eb
--- /dev/null
+++ b/src/19/solve.py
@@ -0,0 +1,109 @@
+import sys
+sys.path.append("../common")
+import aoc
+from math import sqrt
+
+def parse_command(l):
+ s = l.split(" ")
+ args = [s[0]]
+ args = args + [int(v) for v in s[1:]]
+ return args
+
+data = aoc.data.split("\n")
+
+inspaddr = int(data[0][4:])
+instructs = [parse_command(l) for l in data[1:] if len(l) != 0]
+
+register = [0 for x in range(inspaddr+1)]
+
+opmap = dict()
+opmap["addr"] = lambda a, b : register[a] + register[b]
+opmap["addi"] = lambda a, b : register[a] + b
+opmap["mulr"] = lambda a, b : register[a] * register[b]
+opmap["muli"] = lambda a, b : register[a] * b
+opmap["banr"] = lambda a, b : register[a] & register[b]
+opmap["bani"] = lambda a, b : register[a] & b
+opmap["borr"] = lambda a, b : register[a] | register[b]
+opmap["bori"] = lambda a, b : register[a] | b
+opmap["setr"] = lambda a, b : register[a]
+opmap["seti"] = lambda a, b : a
+opmap["gtir"] = lambda a, b : 1 * (a > register[b])
+opmap["gtri"] = lambda a, b : 1 * (register[a] > b)
+opmap["gtrr"] = lambda a, b : 1 * (register[a] > register[b])
+opmap["eqir"] = lambda a, b : 1 * (a == register[b])
+opmap["eqri"] = lambda a, b : 1 * (register[a] == b)
+opmap["eqrr"] = lambda a, b : 1 * (register[a] == register[b])
+
+
+def solve1(args):
+ global register, insptr
+ while register[inspaddr] < len(instructs):
+ insptr = register[inspaddr]
+ ins = instructs[insptr]
+ #aoc.debug(register)
+
+ # execute command
+ if len(register) <= ins[3]:
+ register += [0 for x in range(ins[3] - len(register) + 1)]
+ register[ins[3]] = opmap[ins[0]](ins[1], ins[2])
+
+ # increment instruction pointer
+ register[inspaddr] += 1
+
+ return register[0]
+
+
+alpha = [chr(c + ord('a')) for c in range(26)]
+
+dismap = dict()
+dismap["addr"] = lambda a, b : "%s + %s" % (alpha[a], alpha[b])
+dismap["addi"] = lambda a, b : "%s + %d" % (alpha[a], b)
+dismap["mulr"] = lambda a, b : "%s * %s" % (alpha[a], alpha[b])
+dismap["muli"] = lambda a, b : "%s * %d" % (alpha[a], b)
+dismap["banr"] = lambda a, b : str(alpha[a] & alpha[b])
+dismap["bani"] = lambda a, b : str(alpha[a] & b)
+dismap["borr"] = lambda a, b : str(alpha[a] | alpha[b])
+dismap["bori"] = lambda a, b : str(alpha[a] | b)
+dismap["setr"] = lambda a, b : str(alpha[a])
+dismap["seti"] = lambda a, b : str(a)
+dismap["gtir"] = lambda a, b : "(%d > %s)" % (a, alpha[b])
+dismap["gtri"] = lambda a, b : "(%s > %d)" % (alpha[a], b)
+dismap["gtrr"] = lambda a, b : "(%s > %s)" % (alpha[a], alpha[b])
+dismap["eqir"] = lambda a, b : "(%d == %s)" % (a, alpha[b])
+dismap["eqri"] = lambda a, b : "(%s == %d)" % (alpha[a], b)
+dismap["eqrr"] = lambda a, b : "(%s == %s)" % (alpha[a], alpha[b])
+
+def disassemble():
+ for i in range(len(instructs)):
+ ins = instructs[i]
+ aoc.debug(i ,":",alpha[ins[3]],"=", dismap[ins[0]](ins[1], ins[2]))
+ aoc.debug()
+
+""" divisor sum algo disassembled
+c = 1
+b = 1
+a = 0
+while b <= f:
+ c = 1
+ while c <= f:
+ if b * c == f:
+ a += b
+ c += 1
+ b += 1
+"""
+
+def solve2(args):
+ f = 10551276 # found by running
+
+ res = 0
+ sqrtf = sqrt(f)
+ for i in range(1, int(sqrtf)):
+ if f % i == 0:
+ res += i + f / i
+
+ if int(sqrtf) % f == 0:
+ res += sqrtf
+
+ return int(res)
+
+aoc.run(solve1, solve2, sols=[2072, 27578880])
diff --git a/src/19/test1 b/src/19/test1
new file mode 100644
index 0000000..d7d2a21
--- /dev/null
+++ b/src/19/test1
@@ -0,0 +1,8 @@
+#ip 0
+seti 5 0 1
+seti 6 0 2
+addi 0 1 0
+addr 1 2 3
+setr 1 0 0
+seti 8 0 4
+seti 9 0 5