Day 22 part 2: we're done!
This commit is contained in:
parent
fcb6b51a3e
commit
a6491289a7
225
2019/day22.py
225
2019/day22.py
|
@ -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
|
|
||||||
|
|
Loading…
Reference in a new issue