diff --git a/challenges/2024/05-printQueue/README.md b/challenges/2024/05-printQueue/README.md new file mode 100644 index 0000000..e2b215a --- /dev/null +++ b/challenges/2024/05-printQueue/README.md @@ -0,0 +1 @@ +# [Day 5: Print Queue](https://adventofcode.com/2024/day/5) diff --git a/challenges/2024/05-printQueue/main.py b/challenges/2024/05-printQueue/main.py new file mode 100644 index 0000000..dc31774 --- /dev/null +++ b/challenges/2024/05-printQueue/main.py @@ -0,0 +1,91 @@ +import sys +from collections import namedtuple, defaultdict +import math + + +OrderingRule = namedtuple("OrderingRule", ["page", "goes_before"]) + + +def parse(instr: str) -> tuple[list[OrderingRule], list[list[int]]]: + rules, sets = instr.split("\n\n") + + return ( + [OrderingRule(*map(int, line.split("|"))) for line in rules.splitlines()], + [list(map(int, line.split(","))) for line in sets.splitlines()], + ) + + +def generate_rule_map(rules: list[OrderingRule]) -> dict[int, list[int]]: + rule_map = defaultdict(lambda: []) + for rule in rules: + rule_map[rule.page].append(rule.goes_before) + return rule_map + + +def is_pageset_valid(rule_map: dict[int, list[int]], pageset: list[int]) -> bool: + for i, v in enumerate(pageset): + before = pageset[:i] + + for following_number in rule_map[v]: + if following_number in before: + return False + return True + + +def get_middle_number(x: list[int]) -> int: + assert len(x) % 2 == 1, f"{x} has no nice middle point" + return x[int((len(x) - 1) / 2)] + + +def one(instr: str): + rules, pagesets = parse(instr) + rule_map = generate_rule_map(rules) # for each item, these items should be after it + + acc = 0 + for pageset in pagesets: + if is_pageset_valid(rule_map, pageset): + acc += get_middle_number(pageset) + + return acc + + +def two(instr: str): + rules, pagesets = parse(instr) + rule_map = generate_rule_map(rules) + + inverse_rule_map = defaultdict( + lambda: [] + ) # for each item, these items should be before it + for rule in rules: + inverse_rule_map[rule.goes_before].append(rule.page) + + acc = 0 + for pageset in filter(lambda x: not is_pageset_valid(rule_map, x), pagesets): + while not is_pageset_valid(rule_map, pageset): + for i in range(len(pageset)): + for j in range(i + 1, len(pageset)): + iv = pageset[i] + jv = pageset[j] + + if jv in inverse_rule_map[iv] and i < j: + pageset[i], pageset[j] = pageset[j], pageset[i] + + acc += get_middle_number(pageset) + + return acc + + +def _debug(*args, **kwargs): + kwargs["file"] = sys.stderr + print(*args, **kwargs) + + +if __name__ == "__main__": + if len(sys.argv) < 2 or sys.argv[1] not in ["1", "2"]: + print("Missing day argument", file=sys.stderr) + sys.exit(1) + inp = sys.stdin.read().strip() + if sys.argv[1] == "1": + print(one(inp)) + else: + print(two(inp)) diff --git a/challenges/2024/05-printQueue/tests.json b/challenges/2024/05-printQueue/tests.json new file mode 100644 index 0000000..3041331 --- /dev/null +++ b/challenges/2024/05-printQueue/tests.json @@ -0,0 +1,14 @@ +{ + "1": [ + { + "is": "143", + "input": "47|53\n97|13\n97|61\n97|47\n75|29\n61|13\n75|53\n29|13\n97|29\n53|29\n61|53\n97|53\n61|29\n47|13\n75|47\n97|75\n47|61\n75|61\n47|29\n75|13\n53|13\n\n75,47,61,53,29\n97,61,53,29,13\n75,29,13\n75,97,47,61,53\n61,13,29\n97,13,75,29,47\n\n" + } + ], + "2": [ + { + "is": "123", + "input": "47|53\n97|13\n97|61\n97|47\n75|29\n61|13\n75|53\n29|13\n97|29\n53|29\n61|53\n97|53\n61|29\n47|13\n75|47\n97|75\n47|61\n75|61\n47|29\n75|13\n53|13\n\n75,47,61,53,29\n97,61,53,29,13\n75,29,13\n75,97,47,61,53\n61,13,29\n97,13,75,29,47\n\n" + } + ] +} \ No newline at end of file diff --git a/challenges/2024/README.md b/challenges/2024/README.md index d46556b..2e7562c 100644 --- a/challenges/2024/README.md +++ b/challenges/2024/README.md @@ -17,4 +17,5 @@ A day denoted with an asterisk means it has a visualisation. | 01 - Historian Hysteria | ★ ★ | Python | The reading comprehension was the hardest part of this. | | 02 - Red-Nosed Reindeer | ★ ★ | Python || | 03 - Mull It Over | ★ ★ | Python | The first instance of Advent of Parsing this year! | -| 04* - Ceres Search | ★ ★ | Python | When it says a cross, it does not mean a plus. | \ No newline at end of file +| 04* - Ceres Search | ★ ★ | Python | When it says a cross, it does not mean a plus. | +| 05 - Print Queue | ★ ★ | Python | Before you dismiss and idea as being "too simple", make sure you check that it actually wouldn't work! | \ No newline at end of file diff --git a/challenges/2024/benchmark-graph.png b/challenges/2024/benchmark-graph.png index a0f44ed..0c95cb0 100644 Binary files a/challenges/2024/benchmark-graph.png and b/challenges/2024/benchmark-graph.png differ diff --git a/challenges/2024/benchmarks.jsonl b/challenges/2024/benchmarks.jsonl index 4b1a70b..43d0c75 100644 --- a/challenges/2024/benchmarks.jsonl +++ b/challenges/2024/benchmarks.jsonl @@ -6,3 +6,5 @@ {"day": 3, "part": 2, "runner": "py", "min": 0.02115607261657715, "max": 0.03121805191040039, "avg": 0.022722084522247315, "n": 100} {"day": 4, "part": 1, "runner": "py", "min": 0.17342424392700195, "max": 0.3778045177459717, "avg": 0.18848238706588746, "n": 100} {"day": 4, "part": 2, "runner": "py", "min": 0.05280470848083496, "max": 0.06299543380737305, "avg": 0.05627016305923462, "n": 100} +{"day": 5, "part": 1, "runner": "py", "min": 0.02001357078552246, "max": 0.030559301376342773, "avg": 0.02152919292449951, "n": 100} +{"day": 5, "part": 2, "runner": "py", "min": 0.02507805824279785, "max": 0.03197765350341797, "avg": 0.027084295749664308, "n": 100}