advent-of-code/2018-python/solutions/day_07.py
2021-11-01 16:45:03 +01:00

84 lines
3.8 KiB
Python

import itertools
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = '07.in'
def __str__(self):
return 'Day 07: The Sum of Its Parts'
def solve(self, puzzle_input):
steps = [tuple(filter(lambda x: len(x) == 1, l.split())) for l in puzzle_input.splitlines()]
relations = self._get_relations(steps)
completed_steps = self._get_completed_steps(relations)
return completed_steps
def solve_again(self, puzzle_input):
return self.solve_with_workers(puzzle_input)
def solve_with_workers(self, puzzle_input, workers=5, completion_reducer=0):
steps = [tuple(filter(lambda x: len(x) == 1, l.split())) for l in puzzle_input.splitlines()]
relations = self._get_relations(steps)
return self.get_seconds(relations, workers, completion_reducer)
def _get_completed_steps(self, relations):
stop_at = len(relations.keys())
completed_steps = ''
while len(completed_steps) < stop_at:
available = sorted([child for child, parents in relations.items()
if (all(prnt in completed_steps for prnt in parents) or len(parents) == 0) and child not in completed_steps])
completed = available[0]
completed_steps += completed
return completed_steps
def _get_relations(self, steps):
deps = dict([(p, set()) for p in itertools.chain(dict(steps).keys(), dict(steps).values())])
for parent, child in steps:
deps[child].add(parent)
return deps
def get_seconds(self, relations, workers_count=5, completion_reduce=0):
workers = [dict() for _ in range(workers_count)]
stop_at = len(relations)
seconds = 0
completed = 0
completed_steps = ''
in_progress = set()
diff = ord('A')
while completed < stop_at:
available_steps = sorted([child for child, parents in relations.items()
if (all(prnt in completed_steps for prnt in parents) or len(parents) == 0) and
child not in completed_steps])
available_steps = [s for s in available_steps if s not in in_progress and s not in completed_steps]
available_workers = [i for i, w in enumerate(workers) if not w]
while available_steps and available_workers:
avs = available_steps.pop(0)
avw = available_workers.pop(0)
in_progress.add(avs)
workers[avw][avs] = 0
busy_workers = [i for i, w in enumerate(workers) if w]
for ip, bw in itertools.product(in_progress, busy_workers):
if ip in workers[bw]:
# Apply completion_reduce (60) here since A takes 61 seconds to complete,
# B 62 times to complete, etc in THE REAL WORLD, but in the example it was "simplified".
#
# > To simplify things for the example, however, suppose you only have help from one Elf (a total
# > of two workers) and that each step takes **60 fewer seconds** (so that step A takes 1 second and
# > step Z takes 26 seconds).
#
# Die in a fire, would you kindly.
if workers[bw][ip] == 60 - completion_reduce + ord(ip) - diff:
completed_steps += ip
in_progress.remove(ip)
del workers[bw][ip]
completed += 1
else:
workers[bw][ip] += 1
seconds += 1
return seconds
if __name__ == '__main__':
solution = Solution()
solution.show_results()