Day 10

Here we are given a puzzle about 'tying knots.' The description tells us that we'll have a loop of numbers from 0 to 255 and a set of instructions that are single numbers. We start by selecting a subset of the numbers equal to the first instruction from the start of the list. Then we reverse the order of that set. Then we move forward from the end of the sublist by the 'skip value' which starts at zero and increments by one each time we resolve an instruction.

I get how this looks a bit like tying a knot on the first go but after that, it seems like they're saying you snap the thing back into a circle and the order of the marks along the rope are changed.

This doesn't actually look that hard... but we'll see! Start by defining the list of marks and initial values:

In [1]:
instructions = [227, 169, 3, 166, 246, 201, 0, 47, 1, 255, 2, 254, 96, 3, 97, 144]
marks = list(range(256))
skip = 0
pos = 0

# functions to mod and get indices around end
def mod(x):
    return(x % 256)
def sub_indices(start, length):
    if start + length <= len(marks):
        return(list(range(start, (start + length))))
    # If we go beyond the length of marks, get the leftovers:
    else:
        return(list(range(start, len(marks))) + list(range(mod(start+length))))

Now we just set up a for loop? Is it really that easy?

In [2]:
for inst in instructions:
    indices = sub_indices(pos, inst)
    sublist = [marks[i] for i in indices]
    sublist.reverse()
    for i in range(len(sublist)):
        marks[indices[i]] = sublist[i]
    pos = mod(pos + inst + skip)
    skip += 1
marks[0]*marks[1]
Out[2]:
13760

Part 2

Well that was surprisingly easy... maybe I'm getting better?

I really don't have an overarching view of what these instructions are telling me to do so I'll just try to complete them in pieces. The first thing to do is to turn the instructions string into an ASCII representation and I believe this involves the commas as well...

In [3]:
# reset
instructions = [227, 169, 3, 166, 246, 201, 0, 47, 1, 255, 2, 254, 96, 3, 97, 144]
#instructions = [1, 2, 3]
#instructions = []
marks = list(range(256))
skip = 0
pos = 0

instructions_ascii = [str(x) for x in instructions]
instructions_ascii = ','.join(instructions_ascii)
instructions_ascii = [ord(x) for x in instructions_ascii]
instructions_ascii[:5]
Out[3]:
[50, 50, 55, 44, 49]

Now we're told to add these 'standard length suffix' values to the string. This doesn't make a lot of sense to me but whatever.

In [4]:
# delete this next line for testing:
#instructions_ascii

instructions_ascii += [17, 31, 73, 47, 23]

Next we'll create a so-called 'sparse hash' by repeating the previous process 64 times...

In [5]:
instructions_ascii64 = instructions_ascii * 64

for inst in instructions_ascii64:
    indices = sub_indices(pos, inst)
    sublist = [marks[i] for i in indices]
    sublist.reverse()
    for i in range(len(sublist)):
        marks[indices[i]] = sublist[i]
    pos = mod(pos + inst + skip)
    skip += 1

Okay... now we're asked to do something where we divide up the marks into 16 unit chunks and take a cumulative 'XOR' which is some kind of bitwise flip. It's basically like one, the other, but not both as a logical operator on each 'decimal' place of the binary expansion of the numbers. I'm not quite sure why we would do this but it doesn't seem especially tough. I think i'll try the trick of turning them into strings, concatenating ^ between them, and using eval()

In [6]:
chunks = [str(x) for x in marks]
chunks = [chunks[(16*i):(16*(i+1))] for i in range(int(256/16))]
chunks = [' ^ '.join(x) for x in chunks]
dense_hash = [eval(x) for x in chunks]
print(dense_hash)
[45, 169, 51, 149, 241, 166, 187, 52, 114, 32, 50, 82, 227, 177, 127, 229]

Man I'm loving these list comprehensions. So we do have a hex function that can get us a hexidecimal representation but it doesn't format it quite correctly for this purpose. Let's write this function:

In [7]:
def hex2(x):
    x = hex(x).split('x')[1]
    if len(x) == 1:
        x = '0' + x
    return(x)
print(hex2(3))
print(hex2(17))
print(hex2(255))
03
11
ff
In [8]:
result = ''.join([hex2(x) for x in dense_hash])
result
Out[8]:
'2da93395f1a6bb3472203252e3b17fe5'