# --- Day 7: Recursive Circus --- # Wandering further through the circuits of the computer, you come upon a tower # of programs that have gotten themselves into a bit of trouble. A recursive # algorithm has gotten out of hand, and now they're balanced precariously in a # large tower. # One program at the bottom supports the entire tower. It's holding a large # disc, and on the disc are balanced several more sub-towers. At the bottom of # these sub-towers, standing on the bottom disc, are other programs, each # holding their own disc, and so on. At the very tops of these # sub-sub-sub-...-towers, many programs stand simply keeping the disc below # them balanced but with no disc of their own. # You offer to help, but first you need to understand the structure of these # towers. You ask each program to yell out their name, their weight, and (if # they're holding a disc) the names of the programs immediately above them # balancing on that disc. You write this information down (your puzzle input). # Unfortunately, in their panic, they don't do this in an orderly fashion; by # the time you're done, you're not sure which program gave which information. # For example, if your list is the following: # pbga (66) # xhth (57) # ebii (61) # havc (66) # ktlj (57) # fwft (72) -> ktlj, cntj, xhth # qoyq (66) # padx (45) -> pbga, havc, qoyq # tknk (41) -> ugml, padx, fwft # jptl (61) # ugml (68) -> gyxo, ebii, jptl # gyxo (61) # cntj (57) # ...then you would be able to recreate the structure of the towers that looks # like this: # gyxo # / # ugml - ebii # / \ # | jptl # | # | pbga # / / # tknk --- padx - havc # \ \ # | qoyq # | # | ktlj # \ / # fwft - cntj # \ # xhth # In this example, tknk is at the bottom of the tower (the bottom program), # and is holding up ugml, padx, and fwft. Those programs are, in turn, holding # up other programs; in this example, none of those programs are holding up any # other programs, and are all the tops of their own towers. (The actual tower # balancing in front of you is much larger.) # Before you're ready to help them, you need to make sure your information is # correct. What is the name of the bottom program? with open("files/P7.txt") as f: towers = [line for line in f.read().strip().split("\n")] def part_1() -> None: parents, childs = [], [] for line in towers: if "->" in line: lhs, rhs = line.split(" -> ") parent, _ = lhs.split() parents.append(parent) _childs = rhs.split(", ") for child in _childs: childs.append(child) else: parent, _ = line.split() parents.append(parent) root_program = list(set(parents) - set(childs))[0] print(f"The name of the bottom program is {root_program}") # --- Part Two --- # The programs explain the situation: they can't get down. Rather, they could # get down, if they weren't expending all of their energy trying to keep the # tower balanced. Apparently, one program has the wrong weight, and until it's # fixed, they're stuck here. # For any program holding a disc, each program standing on that disc forms a # sub-tower. Each of those sub-towers are supposed to be the same weight, or # the disc itself isn't balanced. The weight of a tower is the sum of the # weights of the programs in that tower. # In the example above, this means that for ugml's disc to be balanced, gyxo, # ebii, and jptl must all have the same weight, and they do: 61. # However, for tknk to be balanced, each of the programs standing on its disc # and all programs above it must each match. This means that the following sums # must all be the same: # ugml + (gyxo + ebii + jptl) = 68 + (61 + 61 + 61) = 251 # padx + (pbga + havc + qoyq) = 45 + (66 + 66 + 66) = 243 # fwft + (ktlj + cntj + xhth) = 72 + (57 + 57 + 57) = 243 # As you can see, tknk's disc is unbalanced: ugml's stack is heavier than the # other two. Even though the nodes above ugml are balanced, ugml itself is too # heavy: it needs to be 8 units lighter for its stack to weigh 243 and keep the # towers balanced. If this change were made, its weight would be 60. # Given that exactly one program is the wrong weight, what would its weight # need to be to balance the entire tower? def child_values(node, node_map, node_values): weights, unbalanced = [], [] children = node_map[node] for child in children: if child in node_map.keys(): child_weight, child_balance = child_values( child, node_map, node_values ) value = sum(child_weight) + node_values[child] unbalanced.append(child_balance) else: value = node_values[child] weights.append(value) if len(set(weights)) != 1: unbalanced.append((node_map[node], weights)) return weights, unbalanced def part_2() -> None: node_map, node_values = {}, {} for line in towers: if "->" in line: lhs, rhs = line.split(" -> ") parent, value = lhs.split() node_values[parent] = int(value[1:-1]) _childs = rhs.split(", ") node_map[parent] = [child for child in _childs] else: child, value = line.split() node_values[child] = int(value[1:-1]) _, unbalance = child_values("rqwgj", node_map, node_values) heavier_child_idx = unbalance[0][0][5][1].index(max(unbalance[0][0][5][1])) heavier_child = unbalance[0][0][5][0][heavier_child_idx] unbalanced_child = node_values[heavier_child] print(f"To balance the tower, the weight must be {unbalanced_child - 8}") if __name__ == "__main__": part_1() part_2()