Fun with mixins and linear time solution for day 04
This commit is contained in:
parent
6ef57e1e75
commit
19db86c4f2
5 changed files with 90 additions and 45 deletions
48
aoc.py
48
aoc.py
|
@ -1,6 +1,6 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Generic, TypeVar
|
from typing import Any, Generic, Protocol, TypeVar
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
@ -124,6 +124,52 @@ class Aoc(Generic[T1], Aoc2[T1, T1]):
|
||||||
def part2(self, inpt: T1) -> Any:
|
def part2(self, inpt: T1) -> Any:
|
||||||
return self.part1(inpt)
|
return self.part1(inpt)
|
||||||
|
|
||||||
|
class AocSameParser(Generic[T1]):
|
||||||
|
@abstractmethod
|
||||||
|
def parseinput(self, inpt: str) -> T1:
|
||||||
|
...
|
||||||
|
|
||||||
|
def parseinput1(self, inpt: str) -> T1:
|
||||||
|
return self.parseinput(inpt)
|
||||||
|
|
||||||
|
def parseinput2(self, inpt: str) -> T1:
|
||||||
|
return self.parseinput(inpt)
|
||||||
|
|
||||||
|
class AocSameImplementation(Generic[T1]):
|
||||||
|
@abstractmethod
|
||||||
|
def part(self, inpt: T1) -> Any:
|
||||||
|
...
|
||||||
|
|
||||||
|
def part1(self, inpt: T1) -> Any:
|
||||||
|
return self.part(inpt)
|
||||||
|
|
||||||
|
def part2(self, inpt: T1) -> Any:
|
||||||
|
return self.part(inpt)
|
||||||
|
|
||||||
|
|
||||||
|
class AocParseLines(Generic[T1, T2], Protocol):
|
||||||
|
def parseline1(self, inpt: str) -> T1:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def parseinput1(self, inpt: str) -> list[T1]:
|
||||||
|
return [self.parseline1(line) for line in inpt.splitlines()]
|
||||||
|
|
||||||
|
def parseline2(self, inpt: str) -> T2:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def parseinput2(self, inpt: str) -> list[T2]:
|
||||||
|
return [self.parseline2(line) for line in inpt.splitlines()]
|
||||||
|
|
||||||
|
class AocParseLinesSameParser(Generic[T1], AocParseLines[T1, T1]):
|
||||||
|
def parseline1(self, inpt: str) -> T1:
|
||||||
|
return self.parseline(inpt)
|
||||||
|
def parseline2(self, inpt: str) -> T1:
|
||||||
|
return self.parseline(inpt)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def parseline(self, inpt: str) -> T1:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
days = [
|
days = [
|
||||||
|
|
18
day01.py
18
day01.py
|
@ -1,9 +1,9 @@
|
||||||
import aoc
|
from aoc import Aoc2, AocSameImplementation, AocParseLines
|
||||||
|
|
||||||
|
|
||||||
class Day01(aoc.Aoc[list[str]]):
|
class Day01(AocParseLines[str, str], AocSameImplementation[list[str]], Aoc2[list[str], list[str]]):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(2023, 1)
|
Aoc2.__init__(self, 2023, 1)
|
||||||
|
|
||||||
def replace_digits(self, line: str) -> str:
|
def replace_digits(self, line: str) -> str:
|
||||||
spelled_digits = [
|
spelled_digits = [
|
||||||
|
@ -28,15 +28,15 @@ class Day01(aoc.Aoc[list[str]]):
|
||||||
rdigits = list(filter(lambda x: x in map(str, range(10)), reversed(line)))
|
rdigits = list(filter(lambda x: x in map(str, range(10)), reversed(line)))
|
||||||
return int(digits[0] + rdigits[0])
|
return int(digits[0] + rdigits[0])
|
||||||
|
|
||||||
def parseinput1(self, inpt: str) -> list[str]:
|
def parseline1(self, inpt: str) -> str:
|
||||||
return inpt.splitlines()
|
return inpt
|
||||||
|
|
||||||
def part1(self, inpt: list[str]) -> int:
|
def parseline2(self, inpt: str) -> str:
|
||||||
|
return self.replace_digits(inpt)
|
||||||
|
|
||||||
|
def part(self, inpt: list[str]) -> int:
|
||||||
return sum(map(self.filter_digits, inpt))
|
return sum(map(self.filter_digits, inpt))
|
||||||
|
|
||||||
def parseinput2(self, inpt: str) -> list[str]:
|
|
||||||
return [self.replace_digits(line) for line in inpt.splitlines()]
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
day1 = Day01()
|
day1 = Day01()
|
||||||
|
|
10
day02.py
10
day02.py
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import aoc
|
from aoc import Aoc2, AocParseLinesSameParser
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
@ -57,12 +57,12 @@ class Game:
|
||||||
return Game(int(gameid), [GameSet.from_line(s) for s in set_str_list])
|
return Game(int(gameid), [GameSet.from_line(s) for s in set_str_list])
|
||||||
|
|
||||||
|
|
||||||
class Day02(aoc.Aoc[list[Game]]):
|
class Day02(AocParseLinesSameParser[Game], Aoc2[list[Game], list[Game]]):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(2023, 2)
|
Aoc2.__init__(self, 2023, 2)
|
||||||
|
|
||||||
def parseinput(self, inpt: str) -> list[Game]:
|
def parseline(self, inpt: str) -> Game:
|
||||||
return [Game.from_line(line) for line in inpt.splitlines()]
|
return Game.from_line(inpt)
|
||||||
|
|
||||||
def part1(self, games: list[Game]) -> int:
|
def part1(self, games: list[Game]) -> int:
|
||||||
return sum(game.game_id for game in games if game.valid)
|
return sum(game.game_id for game in games if game.valid)
|
||||||
|
|
4
day03.py
4
day03.py
|
@ -2,7 +2,7 @@ from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
from aoc import Aoc
|
from aoc import Aoc2, AocSameParser
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -14,7 +14,7 @@ class Number:
|
||||||
linenr: int
|
linenr: int
|
||||||
|
|
||||||
|
|
||||||
class Day03(Aoc[list[Number]]):
|
class Day03(AocSameParser[list[Number]], Aoc2[list[Number], list[Number]]):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(2023, 3)
|
super().__init__(2023, 3)
|
||||||
|
|
||||||
|
|
53
day04.py
53
day04.py
|
@ -1,57 +1,56 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from collections import deque
|
import regex
|
||||||
|
|
||||||
|
from aoc import Aoc2, AocParseLinesSameParser
|
||||||
|
|
||||||
from aoc import Aoc
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Card:
|
class Card:
|
||||||
card_id: int
|
card_id: int
|
||||||
wins: int
|
wins: int
|
||||||
winning: list[int]
|
win: list[int]
|
||||||
have: list[int]
|
have: list[int]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_line(line: str) -> Card:
|
def from_line(line: str) -> Card:
|
||||||
id_section, number_section = line.split(": ")
|
matches = regex.match(
|
||||||
_, card_id = [s for s in id_section.split(" ") if s.strip()]
|
r"Card\s*(?P<card_id>\d+):(\s*(?P<win>\d+)\s*)*\|(\s*(?P<have>\d+)\s*)*",
|
||||||
winning_str, have_str = number_section.split(" | ")
|
line,
|
||||||
|
)
|
||||||
|
if matches is None:
|
||||||
|
raise RuntimeError(f'Could not parse "{line}"')
|
||||||
|
|
||||||
winning = [int(nr.strip()) for nr in winning_str.split(" ") if nr.strip()]
|
win = [int(d) for d in matches.captures("win")]
|
||||||
have = [int(nr.strip()) for nr in have_str.split(" ") if nr.strip()]
|
have = [int(d) for d in matches.captures("have")]
|
||||||
|
|
||||||
wins = len(set(winning).intersection(have))
|
wins = len(set(win).intersection(have))
|
||||||
return Card(int(card_id),wins, winning, have)
|
return Card(int(matches["card_id"]), wins, win, have)
|
||||||
|
|
||||||
class Day04(Aoc[list[Card]]):
|
|
||||||
|
class Day04(AocParseLinesSameParser[Card], Aoc2[list[Card], list[Card]]):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(2023, 4)
|
Aoc2.__init__(self, 2023, 4)
|
||||||
|
|
||||||
def parseinput(self, inpt: str) -> list[Card]:
|
|
||||||
lines = inpt.splitlines()
|
|
||||||
return [Card.from_line(line) for line in lines]
|
|
||||||
|
|
||||||
|
def parseline(self, inpt: str) -> Card:
|
||||||
|
return Card.from_line(inpt)
|
||||||
|
|
||||||
def part1(self, cards: list[Card]) -> int:
|
def part1(self, cards: list[Card]) -> int:
|
||||||
return sum(2 ** (card.wins - 1) for card in cards if card.wins)
|
return sum(2 ** (card.wins - 1) for card in cards if card.wins)
|
||||||
|
|
||||||
def part2(self, cards: list[Card]) -> int:
|
def part2(self, cards: list[Card]) -> int:
|
||||||
stack = deque(range(len(cards)))
|
d = [0] * len(cards)
|
||||||
complete = len(cards)
|
for card in reversed(cards):
|
||||||
while stack:
|
d[card.card_id - 1] = 1 + sum(
|
||||||
card_id = stack.popleft()
|
d[card.card_id + i] for i in range(card.wins)
|
||||||
for win in range(cards[card_id].wins):
|
)
|
||||||
stack.appendleft(win + card_id + 1)
|
return sum(d)
|
||||||
complete += 1
|
|
||||||
return complete
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
day4 = Day04()
|
day4 = Day04()
|
||||||
day4.run()
|
day4.run()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Add table
Reference in a new issue