solve Day19 in python

This commit is contained in:
2023-12-19 21:34:24 +01:00
parent 3c3a481824
commit 905246bbbe
5 changed files with 272 additions and 4 deletions

96
Day19/python/solution1.py Normal file
View File

@@ -0,0 +1,96 @@
import sys
def parse_input(file_path):
"""Parse the input file into workflows and parts."""
with open(file_path) as file:
rules, parts = file.read().strip().split("\n\n")
workflows = {}
for rule in rules.split("\n"):
name, rest = rule.split("{")
workflows[name] = rest[:-1]
return workflows, parts.split("\n")
def parse_conditions(rule):
"""Parse the conditions and results from a rule string."""
conditions = []
for cmd in rule.split(","):
if ":" in cmd:
condition, result = cmd.split(":")
conditions.append((condition, result))
else:
conditions.append((None, cmd))
return conditions
def accepted(workflows, part):
"""Determine if a part is accepted by the workflows."""
state = "in"
while True:
conditions = parse_conditions(workflows[state])
for condition, result in conditions:
if not condition or evaluate_condition(part, condition):
if result == "A":
return True
elif result == "R":
return False
state = result
break
def evaluate_condition(part, condition):
"""Evaluate a condition against a part's attributes."""
var, op, n = condition[0], condition[1], int(condition[2:])
if op == ">":
return part[var] > n
else:
return part[var] < n
def calculate_sum_of_accepted_parts(workflows, parts):
"""Calculate the sum of all accepted parts."""
total_sum = 0
for part_str in parts:
part = parse_part(part_str)
if accepted(workflows, part):
total_sum += sum(part.values())
return total_sum
def parse_part(part_str):
"""Parse a part string into a dictionary of attributes."""
part_str = part_str[1:-1]
return {x.split("=")[0]: int(x.split("=")[1]) for x in part_str.split(",")}
def test_algorithm():
"""Test the algorithm with the test input."""
workflows, parts = parse_input("../test.txt")
expected_result = 19114 # Expected result from the test input
result = calculate_sum_of_accepted_parts(workflows, parts)
assert (
result == expected_result
), f"Test failed: Expected {expected_result}, got {result}"
print("Test passed successfully.")
def main():
"""Main function to run the algorithm."""
try:
test_algorithm() # Run the test first
workflows, parts = parse_input("../input.txt")
result = calculate_sum_of_accepted_parts(workflows, parts)
print(f"Result for the puzzle input: {result}")
except AssertionError as e:
print(f"Error: {e}")
sys.exit(1)
except Exception as e:
print(f"Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()

91
Day19/python/solution2.py Normal file
View File

@@ -0,0 +1,91 @@
import math
import sys
from itertools import groupby
def parse_input(file_path):
"""Parse input file to extract workflows."""
with open(file_path) as f:
lines = f.read().splitlines()
workflows_raw, _ = [
tuple(group) for not_empty, group in groupby(lines, bool) if not_empty
]
workflows = {}
for workflow in workflows_raw:
name, rules_str = workflow.split("{")
rules = rules_str[:-1].split(",")
workflows[name] = ([], rules.pop())
for rule in rules:
condition, target = rule.split(":")
key, comparison, *value = tuple(condition)
workflows[name][0].append((key, comparison, int("".join(value)), target))
return workflows
def count_ranges(workflows, ranges, name="in"):
"""Count the number of valid ranges recursively."""
if name == "R":
return 0
if name == "A":
return math.prod(stop - start + 1 for start, stop in ranges.values())
rules, fallback = workflows[name]
total = 0
for key, comparison, value, target in rules:
start, stop = ranges[key]
if comparison == "<":
t_start, t_stop = start, value - 1
f_start, f_stop = value, stop
else:
t_start, t_stop = value + 1, stop
f_start, f_stop = start, value
if t_start <= t_stop:
total += count_ranges(workflows, {**ranges, key: (t_start, t_stop)}, target)
if f_start <= f_stop:
ranges[key] = (f_start, f_stop)
else:
break
else:
total += count_ranges(workflows, ranges, fallback)
return total
def test_algorithm():
"""Run the algorithm on a test file and compare the result."""
test_file = "../test.txt"
expected_result = 167409079868000
workflows = parse_input(test_file)
result = count_ranges(workflows, {category: (1, 4000) for category in "xmas"})
assert (
result == expected_result
), f"Test failed: Expected {expected_result}, got {result}"
print("Test passed successfully.")
def main():
"""Main function to execute the algorithm."""
try: # Expected result for the test input
print("Running test...")
test_algorithm()
print("Test passed. Running on actual input...")
input_file = "../input.txt"
workflows = parse_input(input_file)
result = count_ranges(workflows, {category: (1, 4000) for category in "xmas"})
print(f"Result for the puzzle input: {result}")
except AssertionError as e:
print(f"Assertion Error: {e}")
sys.exit(1)
except Exception as e:
print(f"Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()