135 lines
3.7 KiB
Python
135 lines
3.7 KiB
Python
|
#!/usr/bin/python3
|
||
|
"""
|
||
|
Jour 19 du défi Advent Of Code pour l'année 2023
|
||
|
"""
|
||
|
import os
|
||
|
|
||
|
def read_sample():
|
||
|
"""récupère les entrées depuis le fichier texte correspondant"""
|
||
|
filename = os.path.join(os.path.dirname(__file__ ), "inputs", "day19.txt")
|
||
|
with open(filename, 'r') as f:
|
||
|
sample = f.read()
|
||
|
return sample
|
||
|
|
||
|
def parse_sample(sample):
|
||
|
def parse_wf(wf):
|
||
|
name = wf.split("{")[0]
|
||
|
content = wf.split("{")[1].split("}")[0].split(",")
|
||
|
return name, content
|
||
|
|
||
|
def parse_part(part):
|
||
|
content = part.split("{")[1].split("}")[0].split(",")
|
||
|
dico = {p.split("=")[0]: int(p.split("=")[1]) for p in content}
|
||
|
return dico
|
||
|
|
||
|
workflows, parts, = sample.split("\n\n")
|
||
|
workflows = [parse_wf(i) for i in workflows.split("\n") if i != '']
|
||
|
workflows = {i[0]: i[1] for i in workflows}
|
||
|
parts = [parse_part(i) for i in parts.split("\n") if i != '']
|
||
|
return workflows, parts
|
||
|
|
||
|
|
||
|
def process(part, workflows, start="in"):
|
||
|
def goto(instr):
|
||
|
if instr == "A":
|
||
|
return True
|
||
|
if instr == "R":
|
||
|
return False
|
||
|
return process(part, workflows, start=instr)
|
||
|
|
||
|
def ceval(expr):
|
||
|
if '>' in expr:
|
||
|
a, b, = expr.split('>')
|
||
|
return part[a] > int(b)
|
||
|
if '<' in expr:
|
||
|
a, b, = expr.split('<')
|
||
|
return part[a] < int(b)
|
||
|
|
||
|
for instr in workflows[start]:
|
||
|
if ':' in instr:
|
||
|
if ceval(instr.split(":")[0]):
|
||
|
return goto(instr.split(":")[1])
|
||
|
else:
|
||
|
return goto(instr)
|
||
|
return goto("R")
|
||
|
|
||
|
|
||
|
def process2(part, workflows, start="in"):
|
||
|
def sizeof(part):
|
||
|
prod = 1
|
||
|
for i in part.values():
|
||
|
prod *= i[1]-i[0]+1
|
||
|
return prod
|
||
|
|
||
|
def goto(instr, part):
|
||
|
if instr == "A":
|
||
|
return sizeof(part) # taille de l'instance
|
||
|
if instr == "R":
|
||
|
return 0
|
||
|
return process2(part, workflows, start=instr)
|
||
|
|
||
|
def apply_flows(part, flows):
|
||
|
if not flows:
|
||
|
raise NotImplementedError
|
||
|
instr, *next_flows = flows
|
||
|
|
||
|
if ":" not in instr:
|
||
|
return goto(instr, part)
|
||
|
|
||
|
expr = instr.split(":")[0]
|
||
|
action = instr.split(":")[1]
|
||
|
if '>' in expr:
|
||
|
a, b, = expr.split('>')
|
||
|
if part[a][0] > int(b):
|
||
|
return goto(action, part)
|
||
|
if part[a][1] <= int(b):
|
||
|
return apply_flows(part, next_flows)
|
||
|
|
||
|
left = part.copy()
|
||
|
right = part.copy()
|
||
|
left[a] = (part[a][0], int(b))
|
||
|
right[a] = (int(b)+1, part[a][1])
|
||
|
return goto(action, right)+apply_flows(left, next_flows)
|
||
|
|
||
|
if '<' in expr:
|
||
|
a, b, = expr.split('<')
|
||
|
if part[a][1] < int(b):
|
||
|
return goto(action, part)
|
||
|
if part[a][0] >= int(b):
|
||
|
return apply_flows(part, next_flows)
|
||
|
|
||
|
left = part.copy()
|
||
|
right = part.copy()
|
||
|
left[a] = (part[a][0], int(b)-1)
|
||
|
right[a] = (int(b), part[a][1])
|
||
|
return goto(action, left)+apply_flows(right, next_flows)
|
||
|
|
||
|
return apply_flows(part, workflows[start])
|
||
|
|
||
|
|
||
|
|
||
|
def part1(sample):
|
||
|
"""Partie 1 du défi"""
|
||
|
workflows, parts = parse_sample(sample)
|
||
|
somme = 0
|
||
|
for part in parts:
|
||
|
if process(part, workflows):
|
||
|
somme += part["x"]+part["m"]+part["a"]+part["s"]
|
||
|
return somme
|
||
|
|
||
|
|
||
|
def part2(sample):
|
||
|
"""Partie 2 du défi"""
|
||
|
workflows, _ = parse_sample(sample)
|
||
|
|
||
|
return process2({"x":(1, 4000), "m":(1, 4000), "a":(1, 4000), "s":(1, 4000)}, workflows)
|
||
|
|
||
|
|
||
|
def main():
|
||
|
"""Fonction principale"""
|
||
|
sample = read_sample()
|
||
|
print(f"part1: {part1(sample)}")
|
||
|
print(f"part2: {part2(sample)}")
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|