removed unnecessary parser
This commit is contained in:
parent
f59089d5eb
commit
38efdd1d1c
11 changed files with 105 additions and 1043 deletions
|
|
@ -1,9 +1,7 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
|
||||
from typing import ClassVar, Iterator, Self
|
||||
|
||||
from advent.parser.parser import P
|
||||
from typing import Iterator, Self
|
||||
|
||||
day_num = 5
|
||||
|
||||
|
|
@ -26,18 +24,13 @@ class Move:
|
|||
frm: int
|
||||
to: int
|
||||
|
||||
amount_parser: ClassVar[P[int]] = P.second(P.string("move "), P.unsigned())
|
||||
from_parser: ClassVar[P[int]] = P.second(P.string(" from "), P.unsigned())
|
||||
to_parser: ClassVar[P[int]] = P.second(P.string(" to "), P.unsigned())
|
||||
move_parser: ClassVar[P[tuple[int, int, int]]] = P.seq(amount_parser, from_parser, to_parser)
|
||||
|
||||
@classmethod
|
||||
def parse(cls, line: str) -> Self | None:
|
||||
parsed = cls.move_parser.parse(line)
|
||||
if parsed.is_fail():
|
||||
return None
|
||||
amount, frm, to = parsed.get()
|
||||
return cls(amount, frm - 1, to - 1)
|
||||
def parse(cls, line: str) -> Self:
|
||||
match line.split():
|
||||
case ['move', amount, 'from', frm, 'to', to]:
|
||||
return cls(int(amount), int(frm) - 1, int(to) - 1)
|
||||
case _:
|
||||
raise Exception("Not a valid move")
|
||||
|
||||
def do_move(self, crates: list[str], as_9001: bool) -> list[str]:
|
||||
"""
|
||||
|
|
@ -58,21 +51,23 @@ class Crane:
|
|||
moves: list[Move]
|
||||
is_9001: bool
|
||||
|
||||
crate_parser: ClassVar[P[str | None]] = P.either(
|
||||
P.any_char().in_brackets(), P.string(" ").replace(None))
|
||||
crate_row_parser: ClassVar[P[list[str | None]]] = crate_parser.sep_by(P.char(' '))
|
||||
|
||||
@classmethod
|
||||
def parse_crate_row(cls, line: str) -> list[None | str] | None:
|
||||
return Crane.crate_row_parser.parse(line).get_or(None)
|
||||
def parse_crate_row(cls, line: str) -> list[None | str]:
|
||||
result: list[str | None] = []
|
||||
for c in line[1::4]:
|
||||
if c.isalnum():
|
||||
result.append(c)
|
||||
else:
|
||||
result.append(None)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def parse_stacks(cls, lines: Iterator[str]) -> list[str]:
|
||||
stacks: list[str] = []
|
||||
for line in lines:
|
||||
crate_row = Crane.parse_crate_row(line)
|
||||
if crate_row is None:
|
||||
if not line:
|
||||
return stacks
|
||||
crate_row = Crane.parse_crate_row(line)
|
||||
|
||||
if len(stacks) < len(crate_row):
|
||||
stacks += [""] * (len(crate_row) - len(stacks))
|
||||
|
|
@ -86,7 +81,7 @@ class Crane:
|
|||
@classmethod
|
||||
def parse(cls, lines: Iterator[str], is_9001: bool) -> Self:
|
||||
drawing = cls.parse_stacks(lines)
|
||||
moves = [p for p in (Move.parse(line) for line in lines) if p is not None]
|
||||
moves = [Move.parse(line) for line in lines]
|
||||
return cls(drawing, moves, is_9001)
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ def test_parse_move():
|
|||
def test_parse_all():
|
||||
data = input.read_lines(day_num, 'example01.txt')
|
||||
expected = Crane(
|
||||
["ZN", "MCD", "P"],
|
||||
["1ZN", "2MCD", "3P"],
|
||||
[Move(1, 1, 0), Move(3, 0, 2), Move(2, 1, 0), Move(1, 0, 1)], True)
|
||||
result = Crane.parse(data, True)
|
||||
assert result == expected
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ from __future__ import annotations
|
|||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass, field
|
||||
from math import prod
|
||||
import re
|
||||
|
||||
from typing import Callable, Iterator, Self
|
||||
from advent.parser.parser import P
|
||||
|
||||
day_num = 11
|
||||
|
||||
|
|
@ -21,44 +21,16 @@ def part2(lines: Iterator[str]) -> int:
|
|||
return horde.inspected_result()
|
||||
|
||||
|
||||
def worry_increaser(op: str, value: int | str) -> WorryIncreaser:
|
||||
match (op, value):
|
||||
case '*', int(v): return lambda old: old * v
|
||||
case '*', 'old': return lambda old: old ** 2
|
||||
case '+', int(v): return lambda old: old + v
|
||||
case '+', 'old': return lambda old: 2 * old
|
||||
case _: raise Exception(f"Illegal line: {op} {value}")
|
||||
|
||||
|
||||
class Parser:
|
||||
""" All the parsers needed for this solution """
|
||||
worry_inc: P[WorryIncreaser] = P.second(
|
||||
P.tstring("Operation: new = old"),
|
||||
P.map2(P.one_of('+*'), P.either(P.tstring('old'), P.tsigned()),
|
||||
worry_increaser)).tline()
|
||||
monkey_number: P[int] = P.unsigned().between(P.tstring('Monkey'), P.tchar(':')).tline()
|
||||
items: P[list[int]] = P.second(
|
||||
P.tstring('Starting items:'), P.unsigned().sep_by(sep=P.tchar(','))).tline()
|
||||
modulo: P[int] = P.second(
|
||||
P.tstring("Test: divisible by"), P.unsigned()).tline()
|
||||
throw_parser: P[int] = P.second(
|
||||
P.seq(
|
||||
P.tstring("If"),
|
||||
P.either(P.tstring("true"), P.tstring("false")),
|
||||
P.tstring(": throw to monkey")),
|
||||
P.unsigned()).tline()
|
||||
test: P[tuple[int, int, int]] = P.seq(
|
||||
modulo, throw_parser, throw_parser)
|
||||
monkey: P[Monkey] = P.map4(monkey_number, items,
|
||||
worry_inc, test,
|
||||
lambda number, items, worry_inc, test:
|
||||
Monkey(number, items, worry_inc, *test))
|
||||
monkey_list: P[list[Monkey]] = P.second(P.eol().optional(), monkey).many()
|
||||
|
||||
|
||||
WorryIncreaser = Callable[[int], int]
|
||||
|
||||
|
||||
def match_raise(pattern: str, string: str) -> re.Match[str]:
|
||||
result = re.match(pattern, string)
|
||||
if result is None:
|
||||
raise Exception("Pattern did not match")
|
||||
return result
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Monkey:
|
||||
number: int
|
||||
|
|
@ -69,6 +41,31 @@ class Monkey:
|
|||
catcher_if_not_divides: int
|
||||
inspected: int = field(default=0, compare=False)
|
||||
|
||||
@classmethod
|
||||
def parse(cls, lines: Iterator[str]) -> Monkey:
|
||||
re_number = match_raise(r"Monkey (?P<number>\d+):", next(lines))
|
||||
number = int(re_number.group('number'))
|
||||
starting = next(lines).split(":")
|
||||
items = list(int(item.strip()) for item in starting[1].split(","))
|
||||
s_operation = next(lines).split('=')
|
||||
match s_operation[1].split():
|
||||
case ['old', '*', 'old']:
|
||||
operation: WorryIncreaser = lambda old: old ** 2
|
||||
case ['old', '*', num]:
|
||||
number = int(num)
|
||||
operation: WorryIncreaser = lambda old: old * number
|
||||
case ['old', '+', num]:
|
||||
number = int(num)
|
||||
operation: WorryIncreaser = lambda old: old + number
|
||||
case _: raise Exception("Illegal operation")
|
||||
s_modulo = next(lines).split("by")
|
||||
modulo = int(s_modulo[1].strip())
|
||||
s_if_true = next(lines).split("monkey")
|
||||
if_true = int(s_if_true[1])
|
||||
s_if_false = next(lines).split("monkey")
|
||||
if_false = int(s_if_false[1])
|
||||
return Monkey(number, items, operation, modulo, if_true, if_false)
|
||||
|
||||
def inspect_items(self, worry_decrease: int | None) -> Iterator[tuple[int, int]]:
|
||||
for item in self.items:
|
||||
self.inspected += 1
|
||||
|
|
@ -93,6 +90,15 @@ class Monkey:
|
|||
class Troop(ABC):
|
||||
monkeys: list[Monkey]
|
||||
|
||||
@classmethod
|
||||
def parse_monkeys(cls, lines: Iterator[str]) -> Iterator[Monkey]:
|
||||
while True:
|
||||
try:
|
||||
yield Monkey.parse(lines)
|
||||
next(lines)
|
||||
except StopIteration:
|
||||
return
|
||||
|
||||
@abstractmethod
|
||||
def single_round(self):
|
||||
...
|
||||
|
|
@ -108,11 +114,9 @@ class Troop(ABC):
|
|||
|
||||
@dataclass(slots=True)
|
||||
class Troop_While_Worried(Troop):
|
||||
""" The """
|
||||
@classmethod
|
||||
def parse(cls, lines: Iterator[str]) -> Self:
|
||||
monkeys = Parser.monkey_list.parse(lines).get()
|
||||
return Troop_While_Worried(monkeys)
|
||||
return Troop_While_Worried(list(Troop.parse_monkeys(lines)))
|
||||
|
||||
def single_round(self):
|
||||
for currentMonkey in self.monkeys:
|
||||
|
|
@ -126,7 +130,7 @@ class Troop_While_Kinda_Relieved(Troop):
|
|||
|
||||
@classmethod
|
||||
def parse(cls, lines: Iterator[str]) -> Self:
|
||||
monkeys = Parser.monkey_list.parse(lines).get()
|
||||
monkeys = list(Troop.parse_monkeys(lines))
|
||||
return Troop_While_Kinda_Relieved(monkeys, prod(monkey.modulator for monkey in monkeys))
|
||||
|
||||
def single_round(self):
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ from abc import ABC, abstractmethod
|
|||
from dataclasses import dataclass, field
|
||||
from itertools import product
|
||||
from queue import PriorityQueue
|
||||
import re
|
||||
|
||||
from typing import Iterator, Literal, NamedTuple, Self
|
||||
from advent.parser.parser import P
|
||||
|
||||
day_num = 16
|
||||
|
||||
|
|
@ -20,30 +20,29 @@ def part2(lines: Iterator[str]) -> int:
|
|||
return system.under_pressure(26, 2)
|
||||
|
||||
|
||||
valve_parser = P.map3(
|
||||
P.second(P.string("Valve "), P.upper().word()),
|
||||
P.second(P.string(" has flow rate="), P.unsigned()),
|
||||
P.second(P.either(P.string("; tunnels lead to valves "), P.string("; tunnel leads to valve ")),
|
||||
P.upper().word().sep_by(P.string(", "))),
|
||||
lambda name, flow_rate, following: RawValve(name, flow_rate, following)
|
||||
)
|
||||
pattern = re.compile(r"Valve (?P<name>[a-zA-Z]+)[^=]+=(?P<flow_rate>\d+).+valves? (?P<exits>.*)")
|
||||
|
||||
|
||||
class RawValve(NamedTuple):
|
||||
name: str
|
||||
flow_rate: int
|
||||
following: list[str]
|
||||
exits: list[str]
|
||||
|
||||
@classmethod
|
||||
def parse(cls, line: str) -> Self:
|
||||
return valve_parser.parse(line).get()
|
||||
result = pattern.match(line)
|
||||
if not result:
|
||||
raise Exception("Not a valid valve")
|
||||
return RawValve(result.group('name'),
|
||||
int(result.group('flow_rate')),
|
||||
result.group('exits').split(', '))
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Valve:
|
||||
name: str
|
||||
flow_rate: int
|
||||
following: list[Valve]
|
||||
exits: list[Valve]
|
||||
paths: dict[str, int] = field(default_factory=dict, init=False)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
|
|
@ -58,7 +57,7 @@ class Valve:
|
|||
return self.name < other.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.name}:{self.flow_rate}->{','.join(v.name for v in self.following)}"
|
||||
return f"{self.name}:{self.flow_rate}->{','.join(v.name for v in self.exits)}"
|
||||
|
||||
def travel_time(self, to: str) -> int:
|
||||
return self.paths[to]
|
||||
|
|
@ -71,7 +70,7 @@ class Valve:
|
|||
to_check = to_check[1:]
|
||||
|
||||
paths[current.name] = steps, (current.flow_rate > 0)
|
||||
for next in current.following:
|
||||
for next in current.exits:
|
||||
known_path, _ = paths.get(next.name, (steps + 2, False))
|
||||
if known_path > steps + 1:
|
||||
to_check.append((next, steps + 1))
|
||||
|
|
@ -281,8 +280,8 @@ class Network:
|
|||
valves = {valve.name: Valve(valve.name, valve.flow_rate, []) for valve in raw_system}
|
||||
for raw in raw_system:
|
||||
current = valves[raw.name]
|
||||
for follow in raw.following:
|
||||
current.following.append(valves[follow])
|
||||
for follow in raw.exits:
|
||||
current.exits.append(valves[follow])
|
||||
|
||||
return Network(valves)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ from itertools import islice
|
|||
from math import prod
|
||||
from multiprocessing import Pool
|
||||
from queue import PriorityQueue
|
||||
import re
|
||||
from typing import Iterable, Iterator, Self
|
||||
|
||||
from advent.parser.parser import P
|
||||
|
||||
day_num = 19
|
||||
|
||||
|
|
@ -35,17 +35,6 @@ class Processor:
|
|||
return blueprint, blueprint.run(self.rounds)
|
||||
|
||||
|
||||
number_parser = P.second(P.string("Blueprint "), P.unsigned())
|
||||
ore_parser = P.second(P.string(": Each ore robot costs "), P.unsigned())
|
||||
clay_parser = P.second(P.string(" ore. Each clay robot costs "), P.unsigned())
|
||||
tuple_parser = P.seq(P.unsigned(), P.second(P.string(" ore and "), P.unsigned()))
|
||||
obsidian_parser = P.second(P.string(" ore. Each obsidian robot costs "), tuple_parser)
|
||||
geode_parser = P.second(P.string(" clay. Each geode robot costs "), tuple_parser)
|
||||
blueprint_parser = P.map5(number_parser, ore_parser, clay_parser, obsidian_parser, geode_parser,
|
||||
lambda number, ore, clay, obsidian, geode:
|
||||
Blueprint.create(number, ore, clay, obsidian, geode))
|
||||
|
||||
|
||||
class Element(IntEnum):
|
||||
Geode = 0
|
||||
Obsidian = 1
|
||||
|
|
@ -114,6 +103,15 @@ class State:
|
|||
return self.material > other.material
|
||||
|
||||
|
||||
r_number = r"Blueprint (?P<number>\d+):"
|
||||
r_ore = r".*(?P<ore_ore>\d+) ore."
|
||||
r_clay = r".*(?P<clay_ore>\d+) ore."
|
||||
r_obsidian = r".*(?P<obsidian_ore>\d+) ore and (?P<obsidian_clay>\d+) clay."
|
||||
r_geode = r".*(?P<geode_ore>\d+) ore and (?P<geode_obsidian>\d+) obsidian."
|
||||
|
||||
pattern = re.compile(r_number + r_ore + r_clay + r_obsidian + r_geode)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Blueprint:
|
||||
number: int
|
||||
|
|
@ -134,7 +132,16 @@ class Blueprint:
|
|||
|
||||
@classmethod
|
||||
def parse(cls, line: str) -> Self:
|
||||
return blueprint_parser.parse(line).get()
|
||||
result = pattern.match(line)
|
||||
if result is None:
|
||||
raise Exception("Not a valid Blueprint")
|
||||
return Blueprint.create(
|
||||
number=int(result.group('number')),
|
||||
ore=int(result.group('ore_ore')),
|
||||
clay=int(result.group('clay_ore')),
|
||||
obsidian=(int(result.group('obsidian_ore')), int(result.group('obsidian_clay'))),
|
||||
geode=(int(result.group('geode_ore')), int(result.group('geode_obsidian'))),
|
||||
)
|
||||
|
||||
def run(self, rounds: int) -> int:
|
||||
queue: PriorityQueue[State] = PriorityQueue()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue