AOC day 16

🧩 Syntax:
from dataclasses import dataclass
from copy import copy

INPUT_FILENAME = "Day 16/input_1.txt"
INF = int(1e9)


@dataclass
class Valve:
    name: str
    flow_rate: int
    children: list[str]


def parse_input():
    with open(INPUT_FILENAME, "r") as file:
        lines = [l.rstrip() for l in file.readlines()]

    valves = {}

    for line in lines:
        split = line.split(" ")
        valve_name = split[1]
        flow_rate = int(split[4][:-1].split("=")[1])
        children = [split[-1]] + [token[:-1] for token in split if token.endswith(",")]
        valves[valve_name] = Valve(valve_name, flow_rate, children)

    return valves


def floid_warshall(valves):
    dist = {v: {u: INF for u in valves} for v in valves}

    for v in valves:
        dist[v][v] = 0
        for u in valves[v].children:
            dist[v][u] = 1

    for k in valves:
        for i in valves:
            for j in valves:
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

    return dist


def main():
    valves = parse_input()
    distances = floid_warshall(valves)
    non_zero_valves = [v for v in valves if valves[v].flow_rate > 0]

    def generate_open_options(pos, open_valves, time_left):
        for next in non_zero_valves:
            if next not in open_valves and distances[pos][next] <= time_left:
                open_valves.append(next)
                yield from generate_open_options(
                    next, open_valves, time_left - distances[pos][next] - 1
                )
                open_valves.pop()

        yield copy(open_valves)

    def get_order_score(open_order, time_left):
        now, ans = "AA", 0
        for pos in open_order:
            time_left -= distances[now][pos] + 1
            ans += valves[pos].flow_rate * time_left
            now = pos
        return ans

    def solution_1():
        return max(get_order_score(o, 30) for o in generate_open_options("AA", [], 30))

    def solution_2():
        ways = list(generate_open_options("AA", [], 26))

        best_scores = {}

        for order in ways:
            tup = tuple(sorted(order))
            score = get_order_score(order, 26)
            best_scores[tup] = max(best_scores.get(tup, 0), score)

        best_scores = list(best_scores.items())
        
        print(len(best_scores))
        print(len(ways))

        ans = 0
        for human_idx in range(len(best_scores)):
            for elephant_idx in range(human_idx + 1, len(best_scores)):
                human_opens, human_score = best_scores[human_idx]
                elephant_opens, elephant_score = best_scores[elephant_idx]

                if len(set(human_opens).intersection(elephant_opens)) == 0:
                    ans = max(ans, human_score + elephant_score)

        return ans

    print("Answer 1:", solution_1())
    print("Answer 2:", solution_2())


if __name__ == "__main__":
    # INPUT_FILENAME = "Day 16/sample.txt"
    main()