94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
from solutions import BaseSolution
|
|
|
|
|
|
class Program:
|
|
def __init__(self, data=None):
|
|
if data:
|
|
name, weight, disc = self._parse(data)
|
|
self.name = name
|
|
self.weight = weight
|
|
self.disc = disc
|
|
|
|
def __repr__(self):
|
|
return str(self.name)
|
|
|
|
def _parse(self, data):
|
|
disc = []
|
|
try:
|
|
name, weight = data.split()
|
|
except ValueError:
|
|
name, weight, _, *disc = data.split()
|
|
weight = int(weight[1:len(weight) - 1])
|
|
disc = tuple(p.replace(',', '') for p in disc)
|
|
return name, weight, disc
|
|
|
|
def has_disc(self):
|
|
return len(self.disc) > 0
|
|
|
|
def unseen_discs(self, seen_discs):
|
|
return [t for t in self.disc if t not in seen_discs]
|
|
|
|
|
|
class Solution(BaseSolution):
|
|
input_file = '07.txt'
|
|
|
|
def __str__(self):
|
|
return 'Day 7: Recursive Circus'
|
|
|
|
def _get_programs(self, puzzle_input):
|
|
return [Program(data) for data in puzzle_input.splitlines()]
|
|
|
|
def _get_discs(self, disc, programs):
|
|
subdisc = [{'own_weight': p.weight, 'obj': p} for p in programs if p.name in disc]
|
|
for t in subdisc:
|
|
t['weight'] = t['own_weight']
|
|
if t['obj'].has_disc():
|
|
t['disc'] = self._get_discs(t['obj'].disc, programs)
|
|
t['weight'] += sum([st['weight'] for st in t['disc']])
|
|
del (t['obj'])
|
|
return subdisc
|
|
|
|
def _find_unbalanced_disc(self, disc):
|
|
disc = sorted(disc, key=lambda t: t['weight'])
|
|
if not disc[0]['weight'] < disc[1]['weight']:
|
|
disc = sorted(disc, key=lambda t: t['weight'], reverse=True)
|
|
return disc[0], disc[1]['weight'] - disc[0]['weight']
|
|
|
|
def solve(self, puzzle_input):
|
|
programs = self._get_programs(puzzle_input)
|
|
programs_with_discs = []
|
|
seen = []
|
|
for p in programs:
|
|
if p.has_disc():
|
|
programs_with_discs.append(p)
|
|
else:
|
|
seen.append(p.name)
|
|
for p in programs_with_discs:
|
|
unseen = p.unseen_discs(seen)
|
|
if len(unseen) > 0:
|
|
seen += unseen
|
|
bottom_program = list(filter(lambda p: p.name not in seen, programs_with_discs))[0]
|
|
return bottom_program
|
|
|
|
def solve_again(self, puzzle_input):
|
|
programs = self._get_programs(puzzle_input)
|
|
bottom_program = self.solve(puzzle_input)
|
|
disc_tree = {
|
|
'own_weight': bottom_program.weight,
|
|
'disc': self._get_discs(bottom_program.disc, programs)
|
|
}
|
|
diff = -1
|
|
unbalanced = True
|
|
while unbalanced:
|
|
disc, new_diff = self._find_unbalanced_disc(disc_tree['disc'])
|
|
if new_diff == 0:
|
|
unbalanced = False
|
|
else:
|
|
disc_tree = disc
|
|
diff = new_diff
|
|
return disc_tree['own_weight'] + diff
|
|
|
|
|
|
if __name__ == '__main__':
|
|
solution = Solution()
|
|
solution.show_results()
|