1
0

Added AOC 2017 day 08 - 10

This commit is contained in:
Mike Schwörer 2020-12-14 14:03:27 +01:00
parent f3a29eac31
commit 1bc31acd5c
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
13 changed files with 1518 additions and 0 deletions

View File

@ -0,0 +1,27 @@
--- Day 8: I Heard You Like Registers ---
You receive a signal directly from the CPU. Because of your recent assistance with jump instructions, it would like you to compute the result of a series of unusual register instructions.
Each instruction consists of several parts: the register to modify, whether to increase or decrease that register's value, the amount by which to increase or decrease it, and a condition. If the condition fails, skip the instruction without modifying the register. The registers all start at 0. The instructions look like this:
b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10
These instructions would be processed as follows:
Because a starts at 0, it is not greater than 1, and so b is not modified.
a is increased by 1 (to 1) because b is less than 5 (it is 0).
c is decreased by -10 (to 10) because a is now greater than or equal to 1 (it is 1).
c is increased by -20 (to -10) because c is equal to 10.
After this process, the largest value in any register is 1.
You might also encounter <= (less than or equal to) or != (not equal to). However, the CPU doesn't have the bandwidth to tell you what all the registers are named, and leaves that to you to determine.
What is the largest value in any register after completing the instructions in your puzzle input?
--- Part Two ---
To be safe, the CPU also needs to know the highest value held in any register during this process so that it can decide how much memory to allocate to these operations. For example, in the above instructions, the highest value ever held was 10 (in register c after the third instruction was evaluated).

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
#!/usr/bin/env python3
import aoc
import re
def parse_line(line: str):
p = re.compile('(?P<r1>[a-z]+) (?P<op>inc|dec) (?P<param>-?[0-9]+) if (?P<r2>[a-z]+) (?P<cmp>[!=<>]+) (?P<arg>-?[0-9]+)')
m = p.match(line)
if m is None: raise "No match"
return m.group('r1'), m.group('op'), int(m.group('param')), m.group('r2'), m.group('cmp'), int(m.group('arg'))
def evaluate_cmp(mem: dict, reg: str, op: str, arg: str):
if not reg in mem:
mem[reg] = 0
if op == "==":
return (mem[reg] == arg)
if op == "!=":
return (mem[reg] != arg)
if op == "<":
return (mem[reg] < arg)
if op == ">":
return (mem[reg] > arg)
if op == "<=":
return (mem[reg] <= arg)
if op == ">=":
return (mem[reg] >= arg)
raise "invalid op: " + op
def exec_cmd(mem: dict, reg: str, op: str, arg: str):
if not reg in mem:
mem[reg] = 0
if op == "inc":
mem[reg] = mem[reg] + arg
elif op == "dec":
mem[reg] = mem[reg] - arg
else:
raise "invalid op: " + op
rawinput = aoc.read_input(8)
data = list(map(lambda x: parse_line(x), rawinput.splitlines()))
registers = dict()
for line in data:
if evaluate_cmp(registers, line[3], line[4], line[5]):
exec_cmd(registers, line[0], line[1], line[2])
max = None
for v in registers.values():
if max is None or v > max:
max = v
print(max)

View File

@ -0,0 +1,65 @@
#!/usr/bin/env python3
import aoc
import re
def parse_line(line: str):
p = re.compile('(?P<r1>[a-z]+) (?P<op>inc|dec) (?P<param>-?[0-9]+) if (?P<r2>[a-z]+) (?P<cmp>[!=<>]+) (?P<arg>-?[0-9]+)')
m = p.match(line)
if m is None: raise "No match"
return m.group('r1'), m.group('op'), int(m.group('param')), m.group('r2'), m.group('cmp'), int(m.group('arg'))
def evaluate_cmp(mem: dict, reg: str, op: str, arg: str):
if not reg in mem:
mem[reg] = 0
if op == "==":
return (mem[reg] == arg)
if op == "!=":
return (mem[reg] != arg)
if op == "<":
return (mem[reg] < arg)
if op == ">":
return (mem[reg] > arg)
if op == "<=":
return (mem[reg] <= arg)
if op == ">=":
return (mem[reg] >= arg)
raise "invalid op: " + op
def exec_cmd(mem: dict, reg: str, op: str, arg: str):
if not reg in mem:
mem[reg] = 0
if op == "inc":
mem[reg] = mem[reg] + arg
elif op == "dec":
mem[reg] = mem[reg] - arg
else:
raise "invalid op: " + op
rawinput = aoc.read_input(8)
data = list(map(lambda x: parse_line(x), rawinput.splitlines()))
registers = dict()
max = 0
for line in data:
if evaluate_cmp(registers, line[3], line[4], line[5]):
exec_cmd(registers, line[0], line[1], line[2])
if max < registers[line[0]]:
max = registers[line[0]]
if max < registers[line[3]]:
max = registers[line[3]]
print(max)

View File

@ -0,0 +1,61 @@
--- Day 9: Stream Processing ---
A large stream blocks your path. According to the locals, it's not safe to cross the stream at the moment because it's full of garbage. You look down at the stream; rather than water, you discover that it's a stream of characters.
You sit for a while and record part of the stream (your puzzle input). The characters represent groups - sequences that begin with { and end with }. Within a group, there are zero or more other things, separated by commas: either another group or garbage. Since groups can contain other groups, a } only closes the most-recently-opened unclosed group - that is, they are nestable. Your puzzle input represents a single, large group which itself contains many smaller ones.
Sometimes, instead of a group, you will find garbage. Garbage begins with < and ends with >. Between those angle brackets, almost any character can appear, including { and }. Within garbage, < has no special meaning.
In a futile attempt to clean up the garbage, some program has canceled some of the characters within it using !: inside garbage, any character that comes after ! should be ignored, including <, >, and even another !.
You don't see any characters that deviate from these rules. Outside garbage, you only find well-formed groups, and garbage always terminates according to the rules above.
Here are some self-contained pieces of garbage:
<>, empty garbage.
<random characters>, garbage containing random characters.
<<<<>, because the extra < are ignored.
<{!>}>, because the first > is canceled.
<!!>, because the second ! is canceled, allowing the > to terminate the garbage.
<!!!>>, because the second ! and the first > are canceled.
<{o"i!a,<{i<a>, which ends at the first >.
Here are some examples of whole streams and the number of groups they contain:
{}, 1 group.
{{{}}}, 3 groups.
{{},{}}, also 3 groups.
{{{},{},{{}}}}, 6 groups.
{<{},{},{{}}>}, 1 group (which itself contains garbage).
{<a>,<a>,<a>,<a>}, 1 group.
{{<a>},{<a>},{<a>},{<a>}}, 5 groups.
{{<!>},{<!>},{<!>},{<a>}}, 2 groups (since all but the last > are canceled).
Your goal is to find the total score for all groups in your input. Each group is assigned a score which is one more than the score of the group that immediately contains it. (The outermost group gets a score of 1.)
{}, score of 1.
{{{}}}, score of 1 + 2 + 3 = 6.
{{},{}}, score of 1 + 2 + 2 = 5.
{{{},{},{{}}}}, score of 1 + 2 + 3 + 3 + 3 + 4 = 16.
{<a>,<a>,<a>,<a>}, score of 1.
{{<ab>},{<ab>},{<ab>},{<ab>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
{{<!!>},{<!!>},{<!!>},{<!!>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
{{<a!>},{<a!>},{<a!>},{<ab>}}, score of 1 + 2 = 3.
What is the total score for all groups in your input?
--- Part Two ---
Now, you're ready to remove the garbage.
To prove you've removed it, you need to count all of the characters within the garbage. The leading and trailing < and > don't count, nor do any canceled characters or the ! doing the canceling.
<>, 0 characters.
<random characters>, 17 characters.
<<<<>, 3 characters.
<{!>}>, 2 characters.
<!!>, 0 characters.
<!!!>>, 0 characters.
<{o"i!a,<{i<a>, 10 characters.
How many non-canceled characters are within the garbage in your puzzle input?

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import aoc
import re
class Group:
def __init__(self):
self.groups = []
self.garbages = []
def fullscore(self, depth):
fs = depth
for grp in self.groups:
fs += grp.fullscore(depth+1)
return fs
def read_group(data: str):
assert data[0] == '{'
pos = 1
result = Group()
while True:
if data[pos] == '}':
return result, pos+1
elif data[pos] == '<':
grb, npos = read_garbage(data[pos:])
result.garbages.append(grb)
pos += npos
if data[pos] == ',':
pos += 1
elif data[pos] == '{':
grp, npos = read_group(data[pos:])
result.groups.append(grp)
pos += npos
if data[pos] == ',':
pos += 1
else:
assert False
def read_garbage(data: str):
assert data[0] == '<'
pos = 1
escaped = False
while True:
if escaped:
escaped = False
elif data[pos] == '>':
return data[1:pos-1], pos+1
elif data[pos] == '!':
escaped = True
pos+=1
rawinput = aoc.read_input(9)
data, _ = read_group(rawinput)
print(data.fullscore(1))

View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
import aoc
import re
class Group:
def __init__(self):
self.groups = []
self.garbages = []
def fullscore(self, depth):
fs = depth
for grp in self.groups:
fs += grp.fullscore(depth+1)
return fs
def garbagesum(self):
gs = 0
for grp in self.groups:
gs += grp.garbagesum()
for grb in self.garbages:
gs += len(grb)
return gs
def read_group(data: str):
assert data[0] == '{'
pos = 1
result = Group()
while True:
if data[pos] == '}':
return result, pos+1
elif data[pos] == '<':
grb, npos = read_garbage(data[pos:])
result.garbages.append(grb)
pos += npos
if data[pos] == ',':
pos += 1
elif data[pos] == '{':
grp, npos = read_group(data[pos:])
result.groups.append(grp)
pos += npos
if data[pos] == ',':
pos += 1
else:
assert False
def read_garbage(data: str):
assert data[0] == '<'
pos = 1
gstr = ''
escaped = False
while True:
if escaped:
escaped = False
elif data[pos] == '>':
return gstr, pos+1
elif data[pos] == '!':
escaped = True
else:
gstr += data[pos]
pos+=1
rawinput = aoc.read_input(9)
data, _ = read_group(rawinput)
print(data.garbagesum())

View File

@ -0,0 +1,71 @@
--- Day 10: Knot Hash ---
You come across some programs that are trying to implement a software emulation of a hash based on knot-tying. The hash these programs are implementing isn't very strong, but you decide to help them anyway. You make a mental note to remind the Elves later not to invent their own cryptographic functions.
This hash function simulates tying a knot in a circle of string with 256 marks on it. Based on the input to be hashed, the function repeatedly selects a span of string, brings the ends together, and gives the span a half-twist to reverse the order of the marks within it. After doing this many times, the order of the marks is used to build the resulting hash.
4--5 pinch 4 5 4 1
/ \ 5,0,1 / \/ \ twist / \ / \
3 0 --> 3 0 --> 3 X 0
\ / \ /\ / \ / \ /
2--1 2 1 2 5
To achieve this, begin with a list of numbers from 0 to 255, a current position which begins at 0 (the first element in the list), a skip size (which starts at 0), and a sequence of lengths (your puzzle input). Then, for each length:
Reverse the order of that length of elements in the list, starting with the element at the current position.
Move the current position forward by that length plus the skip size.
Increase the skip size by one.
The list is circular; if the current position and the length try to reverse elements beyond the end of the list, the operation reverses using as many extra elements as it needs from the front of the list. If the current position moves past the end of the list, it wraps around to the front. Lengths larger than the size of the list are invalid.
Here's an example using a smaller list:
Suppose we instead only had a circular list containing five elements, 0, 1, 2, 3, 4, and were given input lengths of 3, 4, 1, 5.
The list begins as [0] 1 2 3 4 (where square brackets indicate the current position).
The first length, 3, selects ([0] 1 2) 3 4 (where parentheses indicate the sublist to be reversed).
After reversing that section (0 1 2 into 2 1 0), we get ([2] 1 0) 3 4.
Then, the current position moves forward by the length, 3, plus the skip size, 0: 2 1 0 [3] 4. Finally, the skip size increases to 1.
The second length, 4, selects a section which wraps: 2 1) 0 ([3] 4.
The sublist 3 4 2 1 is reversed to form 1 2 4 3: 4 3) 0 ([1] 2.
The current position moves forward by the length plus the skip size, a total of 5, causing it not to move because it wraps around: 4 3 0 [1] 2. The skip size increases to 2.
The third length, 1, selects a sublist of a single element, and so reversing it has no effect.
The current position moves forward by the length (1) plus the skip size (2): 4 [3] 0 1 2. The skip size increases to 3.
The fourth length, 5, selects every element starting with the second: 4) ([3] 0 1 2. Reversing this sublist (3 0 1 2 4 into 4 2 1 0 3) produces: 3) ([4] 2 1 0.
Finally, the current position moves forward by 8: 3 4 2 1 [0]. The skip size increases to 4.
In this example, the first two numbers in the list end up being 3 and 4; to check the process, you can multiply them together to produce 12.
However, you should instead use the standard list size of 256 (with values 0 to 255) and the sequence of lengths in your puzzle input. Once this process is complete, what is the result of multiplying the first two numbers in the list?
--- Part Two ---
The logic you've constructed forms a single round of the Knot Hash algorithm; running the full thing requires many of these rounds. Some input and output processing is also required.
First, from now on, your input should be taken not as a list of numbers, but as a string of bytes instead. Unless otherwise specified, convert characters to bytes using their ASCII codes. This will allow you to handle arbitrary ASCII strings, and it also ensures that your input lengths are never larger than 255. For example, if you are given 1,2,3, you should convert it to the ASCII codes for each character: 49,44,50,44,51.
Once you have determined the sequence of lengths to use, add the following lengths to the end of the sequence: 17, 31, 73, 47, 23. For example, if you are given 1,2,3, your final sequence of lengths should be 49,44,50,44,51,17,31,73,47,23 (the ASCII codes from the input string combined with the standard length suffix values).
Second, instead of merely running one round like you did above, run a total of 64 rounds, using the same length sequence in each round. The current position and skip size should be preserved between rounds. For example, if the previous example was your first round, you would start your second round with the same length sequence (3, 4, 1, 5, 17, 31, 73, 47, 23, now assuming they came from ASCII codes and include the suffix), but start with the previous round's current position (4) and skip size (4).
Once the rounds are complete, you will be left with the numbers from 0 to 255 in some order, called the sparse hash. Your next task is to reduce these to a list of only 16 numbers called the dense hash. To do this, use numeric bitwise XOR to combine each consecutive block of 16 numbers in the sparse hash (there are 16 such blocks in a list of 256 numbers). So, the first element in the dense hash is the first sixteen elements of the sparse hash XOR'd together, the second element in the dense hash is the second sixteen elements of the sparse hash XOR'd together, etc.
For example, if the first sixteen elements of your sparse hash are as shown below, and the XOR operator is ^, you would calculate the first output number like this:
65 ^ 27 ^ 9 ^ 1 ^ 4 ^ 3 ^ 40 ^ 50 ^ 91 ^ 7 ^ 6 ^ 0 ^ 2 ^ 5 ^ 68 ^ 22 = 64
Perform this operation on each of the sixteen blocks of sixteen numbers in your sparse hash to determine the sixteen numbers in your dense hash.
Finally, the standard way to represent a Knot Hash is as a single hexadecimal string; the final output is the dense hash in hexadecimal notation. Because each number in your dense hash will be between 0 and 255 (inclusive), always represent each number as two hexadecimal digits (including a leading zero as necessary). So, if your first three numbers are 64, 7, 255, they correspond to the hexadecimal numbers 40, 07, ff, and so the first six characters of the hash would be 4007ff. Because every Knot Hash is sixteen such numbers, the hexadecimal representation is always 32 hexadecimal digits (0-f) long.
Here are some example hashes:
The empty string becomes a2582a3a0e66e6e86e3812dcb672a272.
AoC 2017 becomes 33efeb34ea91902bb2f59c9920caa6cd.
1,2,3 becomes 3efbe78a8d82f29979031a4aa0b16a9d.
1,2,4 becomes 63960835bcdc130f0b66d7ff4f6a5a8e.
Treating your puzzle input as a string of ASCII characters, what is the Knot Hash of your puzzle input? Ignore any leading or trailing whitespace you might encounter.

View File

@ -0,0 +1 @@
230,1,2,221,97,252,168,169,57,99,0,254,181,255,235,167

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
import aoc
def twist(nums: list, nstart: int, nlen: int):
sublist = list()
for i in range(0, nlen):
sublist.append(nums[(i+nstart) % len(nums)])
for i in range(0, nlen):
nums[(i+nstart) % len(nums)] = sublist[nlen-i-1]
rawinput = aoc.read_input(10)
inputlengths = list(map(lambda x: int(x), rawinput.split(',')))
numberslist = list(range(0, 256))
currpos = 0
skipsize = 0
for ilen in inputlengths:
twist(numberslist, currpos, ilen)
currpos = (currpos + ilen + skipsize) % len(numberslist)
skipsize += 1
#print(numberslist)
print(numberslist[0] * numberslist[1])

View File

@ -0,0 +1,45 @@
#!/usr/bin/env python3
import aoc
def twist(nums: list, nstart: int, nlen: int):
sublist = list()
for i in range(0, nlen):
sublist.append(nums[(i+nstart) % len(nums)])
for i in range(0, nlen):
nums[(i+nstart) % len(nums)] = sublist[nlen-i-1]
rawinput = aoc.read_input(10).strip()
inputlengths = list(map(lambda x: ord(x), rawinput))
inputlengths.append(17)
inputlengths.append(31)
inputlengths.append(73)
inputlengths.append(47)
inputlengths.append(23)
numberslist = list(range(0, 256))
currpos = 0
skipsize = 0
for iround in range(0,64):
for ilen in inputlengths:
twist(numberslist, currpos, ilen)
currpos = (currpos + ilen + skipsize) % len(numberslist)
skipsize += 1
densehash = list()
for i1 in range(0, 16):
xor = 0
for i2 in range(0, 16):
xor = xor ^ numberslist[i1*16 + i2]
densehash.append(xor)
hex = "".join(map(lambda n: "%0.2x" % n, densehash))
print(hex)

View File

@ -11,6 +11,9 @@ return
['day' => 5, 'parts' => 2, 'title' => 'A Maze of Twisty Trampolines, All Alike', 'language' => 'pyth', 'solutions' => ['388611', '27763113'] ],
['day' => 6, 'parts' => 2, 'title' => 'Memory Reallocation', 'language' => 'pyth', 'solutions' => ['11137', '1037'] ],
['day' => 7, 'parts' => 2, 'title' => 'Recursive Circus', 'language' => 'pyth', 'solutions' => ['hlqnsbe', '1993'] ],
['day' => 8, 'parts' => 2, 'title' => 'I Heard You Like Registers', 'language' => 'pyth', 'solutions' => ['6343', '7184'] ],
['day' => 9, 'parts' => 2, 'title' => 'Stream Processing', 'language' => 'pyth', 'solutions' => ['7640', '4368'] ],
['day' => 10, 'parts' => 2, 'title' => 'Knot Hash', 'language' => 'pyth', 'solutions' => ['2928', '0c2f794b2eb555f7830766bf8fb65a16'] ],
],
'2018' =>
[