From 9d50268948656ce1ec9d946c7ce39c904489503f Mon Sep 17 00:00:00 2001 From: alexo Date: Thu, 28 Dec 2023 01:35:59 +1100 Subject: [PATCH] day19: coverage --- .gitignore | 2 +- day19/day19.py | 16 +++++----- day19/lib/classes.py | 25 ++++++++------- day19/tests/test_classes.py | 61 +++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 day19/tests/test_classes.py diff --git a/.gitignore b/.gitignore index c62e1ec..ccd53cd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ input.txt /day20/vis/ /day10/big*.txt -.coverage \ No newline at end of file +.coverage* \ No newline at end of file diff --git a/day19/day19.py b/day19/day19.py index f8d227d..f6b3ffd 100644 --- a/day19/day19.py +++ b/day19/day19.py @@ -13,16 +13,18 @@ def get_input(path: str) -> tuple[list[Workflow], list[Part]]: workflows: list[Workflow] = [] parts: list[Part] = [] + parsing_parts: bool = False with open(path, encoding="utf8") as file: for line in file: if len(line.strip()) == 0: - break - workflow: Workflow = parse_workflow_string(line) - workflows.append(workflow) - - for line in file: - part: Part = parse_part_string(line) - parts.append(part) + parsing_parts = True + continue + if not parsing_parts: + workflow: Workflow = parse_workflow_string(line) + workflows.append(workflow) + else: + part: Part = parse_part_string(line) + parts.append(part) return (workflows, parts) diff --git a/day19/lib/classes.py b/day19/lib/classes.py index 3aabd1c..1386286 100644 --- a/day19/lib/classes.py +++ b/day19/lib/classes.py @@ -37,7 +37,7 @@ def get_value(self, component: "Component") -> int: return self.a if component == Component.S: return self.s - raise ValueError(f"Unsupported component{component}") + raise AssertionError(f"Unsupported component{component}") class Component(StrEnum): @@ -65,7 +65,7 @@ def split( ) -> tuple[Optional["PartRange"], Optional["PartRange"]]: """ Split a partrange in two, using a chosen component and splitvalue - in the case that our range falls on one wohle side, we return None. + in the case that our range falls on one whole side, we return None. E.g. range = 0-100; split == 200 -> return [(0-100), None] range = 100-200; split == 50 -> return [None, (100-200)] @@ -128,14 +128,14 @@ def process_part(self, part: Part) -> bool: elif self.component == Component.S: part_val = part.s else: - raise ValueError(f"Unsupported component: {self.component}") + raise AssertionError(f"Unsupported component: {self.component}") if self.sign == Comparator.GreaterThan: return part_val > self.value elif self.sign == Comparator.LessThan: return part_val < self.value else: - raise ValueError(f"Unsupported comparator: {self.sign}") + raise AssertionError(f"Unsupported comparator: {self.sign}") def process_part_range( self, part_range: PartRange @@ -146,7 +146,8 @@ def process_part_range( return (success, fail) if self.sign == Comparator.GreaterThan: fail, success = part_range.split(self.component, self.value + 1) - return (success, fail) + return (success, fail) + raise AssertionError(f"Unknown comparator: {self.sign}") @dataclass @@ -168,12 +169,13 @@ def process_part_range( ) -> tuple[Optional[PartRangeDest], Optional[PartRange]]: success: Optional[PartRange] fail: Optional[PartRange] - if self.condition is None: + if self.condition is None: # pass all success, fail = part_range, None - else: + else: # split up range success, fail = self.condition.process_part_range(part_range) if success is not None: return (PartRangeDest(success, self.destination), fail) + return None, fail @@ -188,7 +190,7 @@ def process_part(self, part: Part) -> str: destination = rule.process_part(part) if destination is not None: return destination - raise ValueError("uh oh, hit the end of worfkflow!") + raise AssertionError("uh oh, hit the end of workflow!") def process_part_range(self, part_range: PartRange) -> list[PartRangeDest]: """ @@ -198,11 +200,12 @@ def process_part_range(self, part_range: PartRange) -> list[PartRangeDest]: results: list[PartRangeDest] = [] remainder: Optional[PartRange] = part_range - for rule in self.rules: - if remainder is None: - break + index = 0 + while remainder is not None: + rule = self.rules[index] success, remainder = rule.process_part_range(remainder) if success is not None: results.append(success) + index += 1 return results diff --git a/day19/tests/test_classes.py b/day19/tests/test_classes.py new file mode 100644 index 0000000..3ba802a --- /dev/null +++ b/day19/tests/test_classes.py @@ -0,0 +1,61 @@ +from day19.lib.classes import ( + Comparator, + Component, + Condition, + Part, + PartRange, + PartRangeDest, + Rule, + Workflow, +) + + +def get_part_range() -> tuple[Part, Part]: + return Part(100, 100, 100, 100), Part(150, 200, 150, 150) + + +def test_part_range() -> None: + low: Part = Part(100, 100, 100, 100) + high: Part = Part(150, 200, 150, 150) + part_range: PartRange = PartRange(low, high) + low_split, high_split = part_range.split(Component.M, 300) + assert low_split == part_range + assert high_split is None + + low_split, high_split = part_range.split(Component.M, 25) + assert low_split is None + assert high_split == part_range + + assert str(part_range) == "100<=x<=149, 100<=m<=199, 100<=a<=149, 100<=s<=149" + + +def test_part_range_dest() -> None: + part_range: PartRange = PartRange(*get_part_range()) + part_range_dest = PartRangeDest(part_range, "test") + assert ( + str(part_range_dest) + == "test:100<=x<=149, 100<=m<=199, 100<=a<=149, 100<=s<=149" + ) + + +def test_rule() -> None: + rule = Rule("test", Condition(Component.M, Comparator.LessThan, 50)) + + low: Part = Part(100, 100, 100, 100) + high: Part = Part(150, 200, 150, 150) + part_range = PartRange(low, high) + dest, rest = rule.process_part_range(part_range) + assert dest is None + assert rest == part_range + + +def test_workflow() -> None: + rule1: Rule = Rule("test", Condition(Component.M, Comparator.LessThan, 50)) + rule2: Rule = Rule("rest") + workflow: Workflow = Workflow("workflow1", [rule1, rule2]) + + low: Part = Part(100, 100, 100, 100) + high: Part = Part(150, 200, 150, 150) + part_range = PartRange(low, high) + results = workflow.process_part_range(part_range) + assert results[0].destination == "rest"