1
0
aoc-2020/23/main.py

103 lines
3.2 KiB
Python

def read_input(filename: str) -> list[int]:
with open(filename, "r") as f:
return list(int(c) for c in f.readline()[:-1])
def take_cups(cups: list[int], start: int, length: int) -> list[int]:
taken_cups = []
index = start % len(cups)
for _ in range(length):
taken_cups.append(cups.pop(index))
index = index % len(cups)
return taken_cups
def put_cups(cups: list[int], start: int, target: list[int]):
index = start
for value in target:
cups.insert(index, value)
index = index + 1
def simulate(cups: list[int], moves: int):
max_value = max(cups)
min_value = min(cups)
current_cup_label = cups[0]
for _ in range(moves):
# take 3 cups
taken_cups = take_cups(cups, cups.index(current_cup_label)+1, 3)
# determine destination cup
destination_label = current_cup_label - 1
while destination_label not in cups:
destination_label -= 1
if destination_label < min_value:
destination_label = max_value
destination = cups.index(destination_label)
# put back the 3 taken cups
put_cups(cups, destination + 1, taken_cups)
# move current cup
current_cup_label = cups[(cups.index(current_cup_label) + 1) % len(cups)]
def simulate_lookup(next_cup_lookup: list[int], current_cup: int, moves: int):
max_value = len(next_cup_lookup) - 1
min_value = 1
for _ in range(moves):
# take 3 cups
taken_cup1 = next_cup_lookup[current_cup]
taken_cup2 = next_cup_lookup[taken_cup1]
taken_cup3 = next_cup_lookup[taken_cup2]
# determine destination cup
destination = current_cup
while True:
destination -= 1
if destination < min_value:
destination = max_value
if destination != taken_cup1 and destination != taken_cup2 and destination != taken_cup3:
break
# put back the 3 taken cups
next_cup_lookup[current_cup] = next_cup_lookup[taken_cup3]
after_destination = next_cup_lookup[destination]
next_cup_lookup[taken_cup3] = after_destination
next_cup_lookup[destination] = taken_cup1
# move current cup
current_cup = next_cup_lookup[current_cup]
def part1(cups: list[int]) -> str:
simulate(cups, 100)
num1_index = cups.index(1)
return "".join(map(str, cups[num1_index+1:])) + "".join(map(str, cups[:num1_index]))
def part2(cups: list[int]) -> int:
# create lookup table for the next value
next_cup_lookup: list[int] = [0]*(len(cups)+1)
max_value = max(cups)
cups.append(max_value+1)
for i in range(len(cups)-1):
cup = cups[i]
next_cup = cups[i+1]
next_cup_lookup[cup] = next_cup
for next_cup in range(max_value+2, 1_000_000+1):
next_cup_lookup.append(next_cup)
next_cup_lookup.append(cups[0])
# perform the simulation
simulate_lookup(next_cup_lookup, cups[0], 10_000_000)
# calculate the final result
star1 = next_cup_lookup[1]
star2 = next_cup_lookup[star1]
return star1 * star2
if __name__ == "__main__":
cups = read_input("input.txt")
print("part1: ", part1(cups.copy()))
print("part2: ", part2(cups.copy()))