Day 22 part 2: we're done!

This commit is contained in:
Dece 2019-12-29 02:33:09 +01:00
parent fcb6b51a3e
commit a6491289a7

View file

@ -1,168 +1,83 @@
EX1 = [ """
"deal with increment 7", Disclaimer: I could not solve part 2 on my own and had to look at some tips
"deal into new stack", abour modular arithmetic. Gotta gid gud at discrete maths... What I've
"deal into new stack", understood to get it to work in just a few milliseconds is commented below.
]
EX2 = [ Dumb part 1 is somewhere in Git history.
"cut 6", """
"deal with increment 7",
"deal into new stack",
]
EX3 = [
"deal with increment 7",
"deal with increment 9",
"cut -2",
]
EX4 = [
"deal into new stack",
"cut -2",
"deal with increment 7",
"cut 8",
"cut -4",
"deal with increment 7",
"cut 3",
"deal with increment 9",
"deal with increment 3",
"cut -1",
]
def main(): def main():
with open("day22.txt") as input_file: with open("day22.txt") as input_file:
lines = [l.rstrip() for l in input_file.readlines()] lines = [l.rstrip() for l in input_file.readlines()]
# Part 1 # Part 1
deck = list(range(10)) deck_len = 10007
# deck = list(range(10007)) o, i = shuffle(deck_len, lines)
deck = part1(deck, EX3) print(o, i)
print(deck) for n in range(deck_len):
# deck = part1(deck, lines) if (o + i * n) % deck_len == 2019:
# print(f"Position of card 2019: {deck.index(2019)}.") break
print(f"Position of card 2019: {n}.")
# Part 2 # Part 2
part2(10, 1, EX3, pos=0); input() deck_len = 119315717514047
part2(10, 1, EX3, pos=1); input() num_shuf = 101741582076661
part2(10, 1, EX3, pos=2); input() o, i = shuffle(deck_len, lines)
part2(10, 1, EX3, pos=3); input() # Each shuffle makes increment a multiple of its previous value % deck_len,
part2(10, 1, EX3, pos=4); input() # and new offset is the old offset incremented by this value.
part2(10, 1, EX3, pos=5); input() #
part2(10, 1, EX3, pos=6); input() # Using the offset and increment of the first shuffle (O1, I1), we can write
part2(10, 1, EX3, pos=7); input() # a function for n shuffles as they will use those same values:
part2(10, 1, EX3, pos=8); input() # On = On-1 + (In * O1) % deck_len
part2(10, 1, EX3, pos=9); input() # In = In-1 * I1 % deck_len
# part2(10007, 1, lines, pos=8191) #
# part2(119315717514047, 101741582076661, lines) # We observe that the increment for n shuffles is I1^n-1 % deck_len.
# print(f"2020th card: {deck[2020]}.") #
# We observe that the offset is dependent of each previous increment value:
# O0 = 0
# O1 = k
# O2 = k + I2*k (or: O1 + I2*O1)
# O3 = k + I2*O1 + I3*O2 (or: O2 + I3*O2)
# and this can be factorised as:
# On = k * (1 + I1 + I2 + ... + In-1) % deck_len
# This expression corresponds to a geometric series, which has a formula to
# calculate its n-point value, here for our offset function:
# On = O1 (1 - I1^n) (1 - I1)^n
inc_n = pow(i, num_shuf, deck_len)
ofs_n_op1 = 1 - pow(i, num_shuf, deck_len)
ofs_n_op2 = pow(1 - i, deck_len - 2, deck_len)
ofs_n = (o * ofs_n_op1 * ofs_n_op2) % deck_len
print(f"Card at pos 2020: {(ofs_n + inc_n * 2020) % deck_len}")
def part1(deck, commands): def shuffle(deck_len, raw_commands):
for command in commands: commands = parse_commands(raw_commands)
# Deal into new stack. offset, inc = 0, 1
if command.endswith("k"): for c, a in commands:
deck.reverse() if c == "dins":
inc = -inc
offset += inc
elif c == "cut":
offset += inc * a
elif c == "dwi":
inc *= pow(a, deck_len - 2, deck_len) # Fermat's lil theorem.
offset %= deck_len
inc %= deck_len
return offset, inc
def parse_commands(raw_commands):
commands = []
for rc in raw_commands:
if rc.endswith("k"):
commands.append(("dins", 0))
continue continue
arg = int(command[command.rfind(" "):]) arg = int(rc[rc.rfind(" "):])
# Deal with increment. if rc[0] == "d":
if command[0] == "d": commands.append(("dwi", arg))
new_deck = [None] * len(deck) elif rc[0] == "c":
for i in range(len(deck)): commands.append(("cut", arg))
new_deck[(i * arg) % len(new_deck)] = deck[i] return commands
deck = new_deck
# Cut.
elif command[0] == "c":
cut, deck = deck[:arg], deck[arg:]
deck += cut
return deck
def part2(deck_len, num_shuf, commands, pos=2020):
required = find_required(deck_len, commands, pos)
for c, r in zip(commands, required):
print(f"- Command '{c}' requires knowing value at {r[0]} for {r[1]}.")
print(f"Which gives us value at {pos}.")
deck = {i: i for r in required for i in r}
print(required)
print("Shuffling...")
shuffle(deck, required)
print(f"Card at {pos}: {deck[pos]}.")
def find_required(deck_len, commands, pos):
required = []
for command in reversed(commands):
dest = pos
if command.endswith("k"):
pos = req_dins(pos, deck_len)
required.append((pos, dest))
continue
arg = int(command[command.rfind(" "):])
if command[0] == "d":
print(f"What's req for {pos} in deal with increment {arg}?")
pos = req_dwi(pos, deck_len, arg)
print(f"- answer: {pos}.")
elif command[0] == "c":
pos = req_cut(pos, deck_len, arg)
required.append((pos, dest))
required.reverse()
return required
def req_dins(pos, deck_len):
return deck_len - 1 - pos
def req_cut(pos, deck_len, n):
return (pos + n) % deck_len
def req_dwi(pos, deck_len, inc):
# Straight is (i * arg) % deck_len = j
# (i * arg) = modinv(j, deck_len)
# i = modinv(j, deck_len) / arg
# damn idk
return (pos * inc - deck_len) % deck_len
def shuffle(deck, required):
for required_pair in required:
from_pos, to_pos = required_pair
deck[to_pos] = deck[from_pos]
def dins(parameter_list):
pass
def cut(parameter_list):
pass
def dwi(parameter_list):
pass
if __name__ == "__main__": if __name__ == "__main__":
# main() main()
assert req_cut(0, 10, 3) == 3
assert req_cut(1, 10, 3) == 4
assert req_cut(2, 10, 3) == 5
assert req_cut(5, 10, 3) == 8
assert req_cut(8, 10, 3) == 1
assert req_cut(0, 10, -4) == 6
assert req_cut(0, 10, -4) == 6
assert req_cut(1, 10, -4) == 7
assert req_cut(2, 10, -4) == 8
assert req_cut(3, 10, -4) == 9
assert req_cut(4, 10, -4) == 0
assert req_cut(5, 10, -4) == 1
assert req_cut(6, 10, -4) == 2
assert req_cut(7, 10, -4) == 3
assert req_cut(8, 10, -4) == 4
assert req_cut(9, 10, -4) == 5
assert req_dins(0, 10) == 9
assert req_dins(1, 10) == 8
assert req_dins(2, 10) == 7
assert req_dins(8, 10) == 1
assert req_dins(9, 10) == 0
assert req_dwi(0, 10, 3) == 0
assert req_dwi(1, 10, 3) == 7
assert req_dwi(2, 10, 3) == 4
assert req_dwi(3, 10, 3) == 1
assert req_dwi(4, 10, 3) == 8
assert req_dwi(5, 10, 3) == 5
assert req_dwi(6, 10, 3) == 2
assert req_dwi(7, 10, 3) == 9
assert req_dwi(8, 10, 3) == 6
assert req_dwi(9, 10, 3) == 3