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 pathlib import Path
|
||||
from typing import Any, Generic, TypeVar
|
||||
from typing import Any, Generic, Protocol, TypeVar
|
||||
from urllib.request import Request, urlopen
|
||||
from html.parser import HTMLParser
|
||||
from importlib import import_module
|
||||
|
@ -124,6 +124,52 @@ class Aoc(Generic[T1], Aoc2[T1, T1]):
|
|||
def part2(self, inpt: T1) -> Any:
|
||||
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:
|
||||
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:
|
||||
super().__init__(2023, 1)
|
||||
Aoc2.__init__(self, 2023, 1)
|
||||
|
||||
def replace_digits(self, line: str) -> str:
|
||||
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)))
|
||||
return int(digits[0] + rdigits[0])
|
||||
|
||||
def parseinput1(self, inpt: str) -> list[str]:
|
||||
return inpt.splitlines()
|
||||
def parseline1(self, inpt: str) -> str:
|
||||
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))
|
||||
|
||||
def parseinput2(self, inpt: str) -> list[str]:
|
||||
return [self.replace_digits(line) for line in inpt.splitlines()]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
day1 = Day01()
|
||||
|
|
10
day02.py
10
day02.py
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
|
||||
import aoc
|
||||
from aoc import Aoc2, AocParseLinesSameParser
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
@ -57,12 +57,12 @@ class Game:
|
|||
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:
|
||||
super().__init__(2023, 2)
|
||||
Aoc2.__init__(self, 2023, 2)
|
||||
|
||||
def parseinput(self, inpt: str) -> list[Game]:
|
||||
return [Game.from_line(line) for line in inpt.splitlines()]
|
||||
def parseline(self, inpt: str) -> Game:
|
||||
return Game.from_line(inpt)
|
||||
|
||||
def part1(self, games: list[Game]) -> int:
|
||||
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 functools import reduce
|
||||
from typing import Any, Optional
|
||||
from aoc import Aoc
|
||||
from aoc import Aoc2, AocSameParser
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -14,7 +14,7 @@ class Number:
|
|||
linenr: int
|
||||
|
||||
|
||||
class Day03(Aoc[list[Number]]):
|
||||
class Day03(AocSameParser[list[Number]], Aoc2[list[Number], list[Number]]):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(2023, 3)
|
||||
|
||||
|
|
55
day04.py
55
day04.py
|
@ -1,57 +1,56 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from collections import deque
|
||||
import regex
|
||||
|
||||
from aoc import Aoc2, AocParseLinesSameParser
|
||||
|
||||
from aoc import Aoc
|
||||
|
||||
@dataclass
|
||||
class Card:
|
||||
card_id: int
|
||||
wins: int
|
||||
winning: list[int]
|
||||
win: list[int]
|
||||
have: list[int]
|
||||
|
||||
@staticmethod
|
||||
def from_line(line: str) -> Card:
|
||||
id_section, number_section = line.split(": ")
|
||||
_, card_id = [s for s in id_section.split(" ") if s.strip()]
|
||||
winning_str, have_str = number_section.split(" | ")
|
||||
matches = regex.match(
|
||||
r"Card\s*(?P<card_id>\d+):(\s*(?P<win>\d+)\s*)*\|(\s*(?P<have>\d+)\s*)*",
|
||||
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()]
|
||||
have = [int(nr.strip()) for nr in have_str.split(" ") if nr.strip()]
|
||||
win = [int(d) for d in matches.captures("win")]
|
||||
have = [int(d) for d in matches.captures("have")]
|
||||
|
||||
wins = len(set(winning).intersection(have))
|
||||
return Card(int(card_id),wins, winning, have)
|
||||
wins = len(set(win).intersection(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:
|
||||
super().__init__(2023, 4)
|
||||
|
||||
def parseinput(self, inpt: str) -> list[Card]:
|
||||
lines = inpt.splitlines()
|
||||
return [Card.from_line(line) for line in lines]
|
||||
Aoc2.__init__(self, 2023, 4)
|
||||
|
||||
def parseline(self, inpt: str) -> Card:
|
||||
return Card.from_line(inpt)
|
||||
|
||||
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:
|
||||
stack = deque(range(len(cards)))
|
||||
complete = len(cards)
|
||||
while stack:
|
||||
card_id = stack.popleft()
|
||||
for win in range(cards[card_id].wins):
|
||||
stack.appendleft(win + card_id + 1)
|
||||
complete += 1
|
||||
return complete
|
||||
|
||||
|
||||
|
||||
d = [0] * len(cards)
|
||||
for card in reversed(cards):
|
||||
d[card.card_id - 1] = 1 + sum(
|
||||
d[card.card_id + i] for i in range(card.wins)
|
||||
)
|
||||
return sum(d)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
day4 = Day04()
|
||||
day4.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
Loading…
Add table
Reference in a new issue