Day 22 part 2: we're done!

master
Dece 4 years ago
parent fcb6b51a3e
commit a6491289a7

@ -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:
def part1(deck, commands): # O0 = 0
for command in commands: # O1 = k
# Deal into new stack. # O2 = k + I2*k (or: O1 + I2*O1)
if command.endswith("k"): # O3 = k + I2*O1 + I3*O2 (or: O2 + I3*O2)
deck.reverse() # and this can be factorised as:
continue # On = k * (1 + I1 + I2 + ... + In-1) % deck_len
arg = int(command[command.rfind(" "):]) # This expression corresponds to a geometric series, which has a formula to
# Deal with increment. # calculate its n-point value, here for our offset function:
if command[0] == "d": # On = O1 (1 - I1^n) (1 - I1)^n
new_deck = [None] * len(deck) inc_n = pow(i, num_shuf, deck_len)
for i in range(len(deck)): ofs_n_op1 = 1 - pow(i, num_shuf, deck_len)
new_deck[(i * arg) % len(new_deck)] = deck[i] ofs_n_op2 = pow(1 - i, deck_len - 2, deck_len)
deck = new_deck ofs_n = (o * ofs_n_op1 * ofs_n_op2) % deck_len
# Cut. print(f"Card at pos 2020: {(ofs_n + inc_n * 2020) % deck_len}")
elif command[0] == "c":
cut, deck = deck[:arg], deck[arg:] def shuffle(deck_len, raw_commands):
deck += cut commands = parse_commands(raw_commands)
return deck offset, inc = 0, 1
for c, a in commands:
def part2(deck_len, num_shuf, commands, pos=2020): if c == "dins":
required = find_required(deck_len, commands, pos) inc = -inc
for c, r in zip(commands, required): offset += inc
print(f"- Command '{c}' requires knowing value at {r[0]} for {r[1]}.") elif c == "cut":
print(f"Which gives us value at {pos}.") offset += inc * a
deck = {i: i for r in required for i in r} elif c == "dwi":
print(required) inc *= pow(a, deck_len - 2, deck_len) # Fermat's lil theorem.
print("Shuffling...") offset %= deck_len
shuffle(deck, required) inc %= deck_len
print(f"Card at {pos}: {deck[pos]}.") return offset, inc
def find_required(deck_len, commands, pos): def parse_commands(raw_commands):
required = [] commands = []
for command in reversed(commands): for rc in raw_commands:
dest = pos if rc.endswith("k"):
if command.endswith("k"): commands.append(("dins", 0))
pos = req_dins(pos, deck_len)
required.append((pos, dest))
continue continue
arg = int(command[command.rfind(" "):]) arg = int(rc[rc.rfind(" "):])
if command[0] == "d": if rc[0] == "d":
print(f"What's req for {pos} in deal with increment {arg}?") commands.append(("dwi", arg))
pos = req_dwi(pos, deck_len, arg) elif rc[0] == "c":
print(f"- answer: {pos}.") commands.append(("cut", arg))
elif command[0] == "c": return commands
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…
Cancel
Save