From 365f4bcc2d7d92888d521dd771c9c1dbc36ca28e Mon Sep 17 00:00:00 2001 From: Isaac Good Date: Sun, 15 Dec 2024 11:14:00 -0800 Subject: [PATCH] Use heuristic-selected parsers for 9 more puzzles --- 2019/d01.py | 1 - 2019/d08.py | 2 -- 2019/d09.py | 1 - 2020/d13.py | 1 - 2022/d25.py | 1 - 2023/d01.py | 1 - 2023/d06.py | 7 ++----- 2023/d12.py | 4 +--- 2023/d15.py | 1 - pylib/aoc.py | 24 ++++++++++++++---------- pylib/parsers.py | 4 +++- 11 files changed, 20 insertions(+), 27 deletions(-) diff --git a/2019/d01.py b/2019/d01.py index 8ca9edd..1811ff4 100755 --- a/2019/d01.py +++ b/2019/d01.py @@ -17,7 +17,6 @@ class Day01(aoc.Challenge): aoc.TestCase(inputs="1969", part=2, want=966), aoc.TestCase(inputs="100756", part=2, want=50346), ] - INPUT_PARSER = aoc.parse_one_int_per_line def fuel(self, mass): """Sum fuel needed for some mass and all its fuel.""" diff --git a/2019/d08.py b/2019/d08.py index 6db1a34..bc0fdc8 100755 --- a/2019/d08.py +++ b/2019/d08.py @@ -19,8 +19,6 @@ class Day08(aoc.Challenge): """Day 8.""" - INPUT_PARSER = aoc.parse_one_str - def part1(self, puzzle_input: str) -> int: """Find the min layer.""" layers = chunked(puzzle_input, WIDTH * HEIGHT) diff --git a/2019/d09.py b/2019/d09.py index 9ca4d8d..b13b16a 100755 --- a/2019/d09.py +++ b/2019/d09.py @@ -20,7 +20,6 @@ class Day09(aoc.Challenge): aoc.TestCase(inputs=SAMPLE[2], part=1, want="1125899906842624"), aoc.TestCase(inputs="", part=2, want=aoc.TEST_SKIP), ) - INPUT_PARSER = aoc.parse_one_str def part1(self, puzzle_input: str) -> int: computer = intcode.Computer(puzzle_input, debug=self.DEBUG) diff --git a/2020/d13.py b/2020/d13.py index f690b74..c4c6b47 100755 --- a/2020/d13.py +++ b/2020/d13.py @@ -18,7 +18,6 @@ class Day13(aoc.Challenge): aoc.TestCase(inputs=SAMPLE[0], part=1, want=295), aoc.TestCase(inputs=SAMPLE[0], part=2, want=1068781), ) - INPUT_PARSER = aoc.parse_one_str_per_line def part1(self, puzzle_input: List[str]) -> int: """Find which bus will first arrive once we are at the bus terminal.""" diff --git a/2022/d25.py b/2022/d25.py index 484cfee..8e832f9 100755 --- a/2022/d25.py +++ b/2022/d25.py @@ -48,7 +48,6 @@ class Day25(aoc.Challenge): aoc.TestCase(inputs=SAMPLE[0], part=1, want="2=-1=0"), aoc.TestCase(inputs=SAMPLE[0], part=2, want=aoc.TEST_SKIP), ] - INPUT_PARSER = aoc.parse_one_str_per_line def encode(self, dec: int) -> str: """Return the SNAFU encoding of a number.""" diff --git a/2023/d01.py b/2023/d01.py index 2ecd57e..ec43428 100755 --- a/2023/d01.py +++ b/2023/d01.py @@ -35,7 +35,6 @@ class Day01(aoc.Challenge): aoc.TestCase(inputs="eightwo", part=2, want=82), aoc.TestCase(inputs="hczrldvxffninemzbhsv2two5eightwozfh", part=2, want=92), ] - INPUT_PARSER = aoc.parse_one_str_per_line def solver(self, puzzle_input: list[str], part_one: bool) -> int: """Walk a string and extract numbers.""" diff --git a/2023/d06.py b/2023/d06.py index de2c36c..519c240 100755 --- a/2023/d06.py +++ b/2023/d06.py @@ -27,16 +27,13 @@ class Day06(aoc.Challenge): aoc.TestCase(inputs=SAMPLE[0], part=1, want=288), aoc.TestCase(inputs=SAMPLE[0], part=2, want=71503), ] - INPUT_PARSER = aoc.parse_one_str_per_line def solver(self, puzzle_input: InputType, part_one: bool) -> int: """Compute how long to charge the car in order to win the race.""" - lines = [line.split(":")[1].strip() for line in puzzle_input] - if part_one: - times, distances = ([int(i) for i in line.split()] for line in lines) + times, distances = ([int(i) for i in line[1:]] for line in puzzle_input) else: - times, distances = ([int(line.replace(" ", ""))] for line in lines) + times, distances = ([int("".join(line[1:]))] for line in puzzle_input) # See notes for explanation. result = 1 diff --git a/2023/d12.py b/2023/d12.py index fec4550..60ea4ec 100755 --- a/2023/d12.py +++ b/2023/d12.py @@ -16,7 +16,6 @@ class Day12(aoc.Challenge): """Day 12: Hot Springs.""" - INPUT_PARSER = aoc.parse_one_str_per_line TESTS = [ aoc.TestCase(inputs=SAMPLE, part=1, want=21), aoc.TestCase(inputs=SAMPLE, part=2, want=525152), @@ -62,8 +61,7 @@ def ways_to_fit(self, springs: tuple[str, ...], numbers: tuple[int, ...]) -> int def solver(self, puzzle_input: str, part_one: bool) -> int: """Return the total number of possible fits.""" count = 0 - for line in puzzle_input: - springs_str, numbers_str = line.split() + for springs_str, numbers_str in puzzle_input: if not part_one: springs_str = "?".join([springs_str] * 5) numbers_str = ",".join([numbers_str] * 5) diff --git a/2023/d15.py b/2023/d15.py index e99b8f8..18ff038 100755 --- a/2023/d15.py +++ b/2023/d15.py @@ -14,7 +14,6 @@ class Day15(aoc.Challenge): aoc.TestCase(inputs=SAMPLE, part=1, want=1320), aoc.TestCase(inputs=SAMPLE, part=2, want=145), ] - INPUT_PARSER = aoc.parse_one_str def hash(self, word: str) -> int: """Compute the HASH of a word.""" diff --git a/pylib/aoc.py b/pylib/aoc.py index 37ff8dc..05cc97b 100644 --- a/pylib/aoc.py +++ b/pylib/aoc.py @@ -626,28 +626,32 @@ def _parser(self) -> parsers.BaseParser: # Use heuristics to guess at an input parser. data = self.raw_data(None) lines = data.splitlines() - multi_lines = len(lines) > 1 + multi_line = len(lines) > 1 one_line = lines[0] if len(lines) > 5 and len(lines[0]) > 5 and len(lines) == len(lines[0]): return CoordinatesParser() - if ParseIntergers().matches(data): - return ParseIntergers() - - if len(RE_INT.findall(one_line)) > 1 and multi_lines: - lines = [re.sub(" +", " ", line) for line in lines[:4]] + pi = ParseIntergers(multi_line=multi_line) + if pi.matches(data): + return pi + + if len(RE_INT.findall(one_line)) > 1 and multi_line: + lines = lines[:4] + for char in "?()+*": + lines = [l.replace(char, "\\" + char) for l in lines] + lines = [re.sub(" +", " ", line) for line in lines] one_line = lines[0] template = re.compile(RE_INT.sub(lambda x: RE_INT.pattern, one_line)) - print(template) + # print(template) if all(template.fullmatch(line) for line in lines): return parse_ints_per_line - elif not multi_lines and all(RE_INT.fullmatch(i) for i in one_line.split()): + elif not multi_line and all(RE_INT.fullmatch(i) for i in one_line.split()): return parse_ints_one_line word_count = max(len(line.split()) for line in data.splitlines()) if word_count == 1: - return parse_one_str_per_line if multi_lines else parse_one_str - return parse_multi_str_per_line if multi_lines else parse_one_str + return parse_one_str_per_line if multi_line else parse_one_str + return parse_multi_str_per_line if multi_line else parse_one_str def input_parser(self, puzzle_input: str) -> Any: """Parse input data. Block of text -> output.""" diff --git a/pylib/parsers.py b/pylib/parsers.py index 5fc3725..718f856 100644 --- a/pylib/parsers.py +++ b/pylib/parsers.py @@ -60,9 +60,11 @@ def parse(self, puzzle_input: str) -> Any: raise NotImplementedError +@dataclasses.dataclass class ParseIntergers(BaseParser): """Get integers from input.""" + multi_line: bool | None = None NUMBER_LINE = re.compile(r"^[+-]?\d{1,1000}( +[+-]?\d{1,1000})*$") def matches(self, puzzle_input) -> bool: @@ -72,7 +74,7 @@ def matches(self, puzzle_input) -> bool: def parse(self, puzzle_input: str) -> int | list[int] | list[list[int]]: """Parse a puzzle input.""" lines = puzzle_input.splitlines() - multi_line = len(lines) > 1 + multi_line = len(lines) > 1 if self.multi_line is None else self.multi_line multi_word = any(len(line.split()) > 1 for line in lines) numbers = [[int(word) for word in line.split()] for line in lines] if multi_line: