<jupyter_start><jupyter_text>Part 1> How many paths through this cave system are there that visit small caves at most once?<jupyter_code>inp = [x.split("-") for x in open("input.txt").read().splitlines()]

def build_tree(n: Node = Node("start", parent=None)):   
    nodes = [n] 
    for a, b in inp:
        if a == n.name and (b.isupper() or b not in [x.name for x in n.path]):           
            nodes = nodes + ([Node(b, parent=n)] if b == "end" else build_tree(Node(b, parent=n)))
        if a != "start" and b == n.name and (a.isupper() or a not in [x.name for x in n.path]):           
            nodes = nodes + build_tree(Node(a, parent=n))
    return nodes

nodes = build_tree()
sum([1 if x.name == "end" else 0 for x in nodes])
# Render tree
#for pre, fill, node in RenderTree(nodes[0]):
#    print("%s%s" % (pre, node.name))<jupyter_output><empty_output><jupyter_text>Part 2> Given these new rules, how many paths through this cave system are there?<jupyter_code>inp = [x.split("-") for x in open("input.txt").read().splitlines()]

def build_tree(n: Node = Node("start", parent=None)):   
    nodes = [n] 
    double_in_use = any([v > 1 for k, v in Counter(x.name for x in n.path if x.name.islower()).items()])
    for a, b in inp:
        if a == n.name and b != "start" and (b.isupper() or not double_in_use or b not in [x.name for x in n.path]):
            nodes = nodes + ([Node(b, parent=n)] if b == "end" else build_tree(Node(b, parent=n)))
        if b == n.name and a != "start" and b != "end" and (a.isupper() or not double_in_use or a not in [x.name for x in n.path]):           
            nodes = nodes + build_tree(Node(a, parent=n))
    return nodes

nodes = build_tree()
print(sum([1 if x.name == "end" else 0 for x in nodes]))<jupyter_output>140718
{'fraction_non_alphanumeric': 0.15816040381379698, 'fraction_numerical': 0.007851934941110488, 'mean_word_length': 3.018018018018018, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '654363', 'license': 'MIT', 'n_tokens_mistral': 645, 'n_tokens_neox': 631, 'n_words': 221, 'path': 'day_12/task.ipynb', 'repo_name': 'codebude/aoc-2021', 'size': '1783'}