2023 day 20.

This commit is contained in:
Mikael CAPELLE
2023-12-20 14:18:17 +01:00
parent 2959387bcd
commit eefb3ceb44
5 changed files with 244 additions and 21 deletions

View File

@@ -28,12 +28,12 @@ def accept(workflows: dict[str, Workflow], part: Part) -> bool:
while decision is None:
for check, target in workflows[workflow]:
ok = check is None
passed = check is None
if check is not None:
category, sense, value = check
ok = OPERATORS[sense](part[category], value)
passed = OPERATORS[sense](part[category], value)
if ok:
if passed:
if target in workflows:
workflow = target
else:
@@ -44,56 +44,63 @@ def accept(workflows: dict[str, Workflow], part: Part) -> bool:
def propagate(workflows: dict[str, Workflow], start: PartWithBounds) -> int:
def _fmt(meta: PartWithBounds) -> str:
return "{" + ", ".join(f"{k}={v}" for k, v in meta.items()) + "}"
def transfer_or_accept(
target: str, meta: PartWithBounds, queue: list[tuple[PartWithBounds, str]]
) -> int:
count = 0
if target in workflows:
logging.info(f" transfer to {target}")
logging.info(f" transfer to {target}")
queue.append((meta, target))
return 0
elif target == "A":
logging.info(" accepted")
return prod((high - low + 1) for low, high in meta.values())
count = prod((high - low + 1) for low, high in meta.values())
logging.info(f" accepted ({count})")
else:
logging.info(" rejected")
return 0
logging.info(" rejected")
return count
accepted = 0
queue: list[tuple[PartWithBounds, str]] = [(start, "in")]
n_iterations = 0
while queue:
n_iterations += 1
meta, workflow = queue.pop()
logging.info(f"{workflow}: {meta}")
logging.info(f"{workflow}: {_fmt(meta)}")
for check, target in workflows[workflow]:
if check is None:
logging.info(" end-of-workflow")
accepted += transfer_or_accept(target, meta, queue)
continue
category, sense, value = check
bounds, op = meta[category], OPERATORS[sense]
logging.info(f" splitting {meta} into {category} {sense} {value}")
logging.info(f" checking {_fmt(meta)} against {category} {sense} {value}")
if not op(bounds[0], value) and not op(bounds[1], value):
logging.info(" reject, always false")
logging.info(" reject, always false")
continue
if op(meta[category][0], value) and op(meta[category][1], value):
logging.info(" accept, always true")
logging.info(" accept, always true")
accepted += transfer_or_accept(target, meta, queue)
break
meta2 = meta.copy()
low, high = meta[category]
if sense == "<":
meta2[category] = (meta[category][0], value - 1)
meta[category] = (value, meta[category][1])
meta[category], meta2[category] = (value, high), (low, value - 1)
else:
meta2[category] = (value + 1, meta[category][1])
meta[category] = (meta[category][0], value)
logging.info(f" split {meta2} ({target}), {meta}")
meta[category], meta2[category] = (low, value), (value + 1, high)
logging.info(f" split {_fmt(meta2)} ({target}), {_fmt(meta)}")
accepted += transfer_or_accept(target, meta2, queue)
logging.info(f"run took {n_iterations} iterations")
return accepted