removed unnecessary parser

This commit is contained in:
Ruediger Ludwig 2023-01-20 07:10:24 +01:00
parent f59089d5eb
commit 38efdd1d1c
11 changed files with 105 additions and 1043 deletions

View file

@ -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

View file

@ -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

View file

@ -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):

View file

@ -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)

View file

@ -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()