[py] Port 2018_07
This commit is contained in:
parent
d01f1ccff3
commit
1e2919b1a1
4 changed files with 117 additions and 123 deletions
|
|
@ -1,115 +0,0 @@
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# PART 1
|
|
||||||
|
|
||||||
STEP_RE = r"Step (\S+) must be finished before step (\S+) can begin.\n"
|
|
||||||
|
|
||||||
def load_steps(filename):
|
|
||||||
steps = {}
|
|
||||||
with open(filename, "r") as f:
|
|
||||||
for line in f:
|
|
||||||
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 main(filename):
|
|
||||||
print(f"Solutions for {filename}")
|
|
||||||
steps = load_steps(filename)
|
|
||||||
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}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
for filename in sys.argv[1:]:
|
|
||||||
main(filename)
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
Step C must be finished before step A can begin.
|
|
||||||
Step C must be finished before step F can begin.
|
|
||||||
Step A must be finished before step B can begin.
|
|
||||||
Step A must be finished before step D can begin.
|
|
||||||
Step B must be finished before step E can begin.
|
|
||||||
Step D must be finished before step E can begin.
|
|
||||||
Step F must be finished before step E can begin.
|
|
||||||
|
|
@ -2,7 +2,7 @@ import sys
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
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 .y2020 import d10
|
||||||
from .y2021 import d14
|
from .y2021 import d14
|
||||||
from .y2022 import d01, d02, d03, d04, d05, d06
|
from .y2022 import d01, d02, d03, d04, d05, d06
|
||||||
|
|
@ -14,6 +14,7 @@ DAYS = {
|
||||||
"2018_04": y2018.d04.solve,
|
"2018_04": y2018.d04.solve,
|
||||||
"2018_05": y2018.d05.solve,
|
"2018_05": y2018.d05.solve,
|
||||||
"2018_06": y2018.d06.solve,
|
"2018_06": y2018.d06.solve,
|
||||||
|
"2018_07": y2018.d07.solve,
|
||||||
"2020_10": y2020.d10.solve,
|
"2020_10": y2020.d10.solve,
|
||||||
"2021_14": y2021.d14.solve,
|
"2021_14": y2021.d14.solve,
|
||||||
"2022_01": y2022.d01.solve,
|
"2022_01": y2022.d01.solve,
|
||||||
|
|
|
||||||
115
py/aoc/y2018/d07.py
Normal file
115
py/aoc/y2018/d07.py
Normal 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}")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue