day 20 finished
This commit is contained in:
parent
150bf3e15b
commit
122f3a3730
5 changed files with 5158 additions and 0 deletions
116
advent/days/day20/solution.py
Normal file
116
advent/days/day20/solution.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
|
||||
from typing import Iterable, Iterator, Self
|
||||
|
||||
day_num = 20
|
||||
|
||||
|
||||
def part1(lines: Iterator[str]) -> int:
|
||||
ring = Ring.create(int(line) for line in lines)
|
||||
ring.process(1)
|
||||
return sum(ring.get_ordered([1000, 2000, 3000]))
|
||||
|
||||
|
||||
def part2(lines: Iterator[str]) -> int:
|
||||
ring = Ring.create(int(line) * 811589153 for line in lines)
|
||||
ring.process(10)
|
||||
return sum(ring.get_ordered([1000, 2000, 3000]))
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Ring:
|
||||
""" Datastructure that stores Items in a Ring """
|
||||
items: list[RingItem]
|
||||
|
||||
@classmethod
|
||||
def create(cls, values: Iterable[int]):
|
||||
""" Crates ring from an Iterable """
|
||||
value_iterator = iter(values)
|
||||
|
||||
current = RingItem.create(next(value_iterator))
|
||||
items = [current]
|
||||
for value in value_iterator:
|
||||
current = current.append(value)
|
||||
items.append(current)
|
||||
|
||||
return Ring(items)
|
||||
|
||||
@property
|
||||
def zero(self) -> RingItem:
|
||||
""" Helper to find the first item with value zero. Raises Exception if there is none. """
|
||||
for item in self.items:
|
||||
if item.value == 0:
|
||||
return item
|
||||
raise Exception("No Zero Item found")
|
||||
|
||||
def process(self, rounds: int):
|
||||
""" Processes the given number of complete rounds. """
|
||||
for _ in range(rounds):
|
||||
for item in self.items:
|
||||
item.move(len(self.items))
|
||||
|
||||
def get_ordered(self, values: list[int]) -> Iterator[int]:
|
||||
""" Returns the values at the given ordered positions """
|
||||
for n, item in zip(range(max(values) + 1), self.zero):
|
||||
if n in values:
|
||||
yield item
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RingItem:
|
||||
""" A class to store one Item in a Ring"""
|
||||
value: int
|
||||
next: RingItem
|
||||
prev: RingItem
|
||||
|
||||
@classmethod
|
||||
def create(cls, value: int) -> Self:
|
||||
""" Creates a single Ring Element that points to itself"""
|
||||
root = RingItem(value, None, None) # type: ignore
|
||||
root.next = root
|
||||
root.prev = root
|
||||
return root
|
||||
|
||||
def append(self, value: int) -> RingItem:
|
||||
""" Appends the given value to the current element """
|
||||
next = RingItem(value, self.next, self)
|
||||
self.next.prev = next
|
||||
self.next = next
|
||||
return next
|
||||
|
||||
def move(self, item_count: int):
|
||||
""" Moves the current element according to its value """
|
||||
steps = self.value % (item_count - 1)
|
||||
|
||||
if steps == 0:
|
||||
return
|
||||
|
||||
self.next.prev = self.prev
|
||||
self.prev.next = self.next
|
||||
|
||||
new_pos = self
|
||||
for _ in range(steps):
|
||||
new_pos = new_pos.next
|
||||
|
||||
self.prev = new_pos
|
||||
self.next = new_pos.next
|
||||
|
||||
new_pos.next.prev = self
|
||||
new_pos.next = self
|
||||
|
||||
def __iter__(self) -> Iterator[int]:
|
||||
""" Never ending iterator through items"""
|
||||
current = self
|
||||
while True:
|
||||
yield current.value
|
||||
current = current.next
|
||||
|
||||
def stopping(self) -> Iterator[int]:
|
||||
""" Iterator that iterates exactly once through all items """
|
||||
current = self
|
||||
while True:
|
||||
yield current.value
|
||||
current = current.next
|
||||
if current == self:
|
||||
break
|
||||
Loading…
Add table
Add a link
Reference in a new issue