[py] Port 2018_07

This commit is contained in:
Joscha 2022-12-06 15:30:53 +01:00
parent d01f1ccff3
commit 1e2919b1a1
4 changed files with 117 additions and 123 deletions

View file

@ -2,7 +2,7 @@ import sys
import argparse
from pathlib import Path
from .y2018 import d01, d02, d03, d04, d05, d06
from .y2018 import d01, d02, d03, d04, d05, d06, d07
from .y2020 import d10
from .y2021 import d14
from .y2022 import d01, d02, d03, d04, d05, d06
@ -14,6 +14,7 @@ DAYS = {
"2018_04": y2018.d04.solve,
"2018_05": y2018.d05.solve,
"2018_06": y2018.d06.solve,
"2018_07": y2018.d07.solve,
"2020_10": y2020.d10.solve,
"2021_14": y2021.d14.solve,
"2022_01": y2022.d01.solve,

115
py/aoc/y2018/d07.py Normal file
View file

@ -0,0 +1,115 @@
import re
# PART 1
STEP_RE = r"Step (\S+) must be finished before step (\S+) can begin."
def load_steps(inputstr):
steps = {}
for line in inputstr.splitlines():
match = re.fullmatch(STEP_RE, line)
step, before = match.groups()
steps[step] = steps.get(step, set()) | {before}
return steps
def reverse_steps(steps):
reverse = {}
for step, befores in steps.items():
# Make sure that step exists in reverse
reverse[step] = reverse.get(step, set())
for before in befores:
reverse[before] = reverse.get(before, set()) | {step}
return reverse
def duration_of(step):
return ord(step) - ord("A") + 61
class Tree:
def __init__(self, steps, workers=1):
self.workers = {i: None for i in range(workers)}
self.steps = reverse_steps(steps) # Warning: Steps are reversed in Trees.
self.result = []
self.duration = 0
def working(self):
return {
worker: work for worker, work in self.workers.items() if work is not None
}
def find_free(self):
return {step for step, afters in self.steps.items() if len(afters) == 0}
def find_working(self):
return {step for (step, _) in self.working().values()}
def find_available(self):
return self.find_free() - self.find_working()
def remove_step(self, step):
try:
del self.steps[step]
except KeyError:
pass
for s in self.steps.values():
try:
s.remove(step)
except KeyError:
pass
def update_workers(self):
min_duration = min(duration for (_, duration) in self.working().values())
self.duration += min_duration
finished_steps = set()
# Subtract min_duration from all workers
for w, s in self.workers.items():
if s is not None:
step, duration = s
duration -= min_duration
if duration <= 0:
finished_steps.add(step)
self.remove_step(step)
self.workers[w] = None
else:
self.workers[w] = (step, duration)
self.result += list(finished_steps)
def distribute_jobs(self):
available = list(reversed(sorted(self.find_available())))
for w, s in self.workers.items():
if not available:
break
if s is None:
step = available.pop()
duration = duration_of(step)
self.workers[w] = (step, duration)
def run(self):
while self.steps:
self.distribute_jobs()
self.update_workers()
def solve(inputstr):
steps = load_steps(inputstr)
tree = Tree(steps, workers=1)
tree.run()
sequence = "".join(tree.result)
print(f"Part 1: {sequence}")
tree = Tree(steps, workers=5)
tree.run()
duration = tree.duration
print(f"Part 2: {duration}")