diff --git a/day8/data.py b/day8/data.py index ed2f921..7b93fd0 100644 --- a/day8/data.py +++ b/day8/data.py @@ -39,8 +39,12 @@ class Node: def is_A_node(self): return self.value[2] == 'A' + def is_Z_node(self): return self.value[2] == 'Z' + + def __str__(self): + return f"{self.value} = ({self.left}, {self.right})" def parse(input_file:str) -> Tuple[str, List[Node]]: @@ -48,7 +52,7 @@ def parse(input_file:str) -> Tuple[str, List[Node]]: with open(input_file) as input: directions = input.readline().replace("\n","") _ = input.readline() - while match:= re.match("([A-Z]+) = \(([A-Z]+), ([A-Z]+)\)",input.readline()): + while match:= re.match("([A-Z]+) = \(([A-Z]+), ([A-Z]+)\)", input.readline()): node = Node(value=match.group(1), left=match.group(2), right=match.group(3)) the_list.append(node) return directions, the_list \ No newline at end of file diff --git a/day8/part2.py b/day8/part2.py index 0d590b5..f9fd599 100755 --- a/day8/part2.py +++ b/day8/part2.py @@ -1,37 +1,24 @@ #!/usr/bin/env python3.11 -from typing import List, Tuple -from concurrent.futures import ThreadPoolExecutor -from threading import current_thread +from typing import List +from datetime import datetime, timedelta from data import Node, parse -def count_steps_XXA_to_XXZ(node: Node, directions:str, node_list: List[Node]) -> int: - print(f"thread {current_thread().name} counting from {node}") - if not node.is_A_node(): - raise Exception("node must be a XXA node") +def count_steps_XXA_to_XXZ(directions:str, node_list: List[Node]) -> int: + current_nodes = list(filter(lambda node: node.is_A_node(), node_list)) count = 0 - current_node = node - while not current_node.is_Z_node(): - current_node = current_node.move_left(node_list) if directions[count % len(directions)] == 'L' else current_node.move_right(node_list) + start_time = datetime.now() + while not all(map(lambda n: n.is_Z_node(), current_nodes)): + if directions[count % len(directions)] == 'L': + current_nodes = list(map(lambda n: n.move_left(node_list), current_nodes)) + else: + current_nodes = list(map(lambda n: n.move_right(node_list), current_nodes)) + if start_time + timedelta(minutes=1) <= datetime.now(): + str_nodes = [node.__str__() for node in current_nodes] + print(f"count: {count} / {str_nodes}") + start_time = datetime.now() count += 1 - print(f"thread {current_thread().name} as counted {count} steps to {current_node}") return count - if __name__ == '__main__': directions, node_list = parse('input.txt') - print("solving all nodes") - solved_node_list = map(lambda node: node.resolve_left_right_nodes(node_list), node_list) - #print(node_list) - print("all_nodes_solved") - xxA_nodes = list(filter(lambda node: node.is_A_node(), solved_node_list)) - print(xxA_nodes) - results = [] - #Parallel version - #with ThreadPoolExecutor(max_workers=10) as executor: - # print("starting ThreadPool") - # for xxA_node in xxA_nodes: - # results.append(executor.submit(count_steps_XXA_to_XXZ, xxA_node, directions, solved_node_list)) - #results = map(lambda exec: exec.result(), results) - results = [count_steps_XXA_to_XXZ(xxA_node, directions, solved_node_list) for xxA_node in xxA_nodes] - print(results) - print(sum(results)) + print(count_steps_XXA_to_XXZ(directions, node_list)) diff --git a/day8/part2_cycles.py b/day8/part2_cycles.py new file mode 100755 index 0000000..f17e2d3 --- /dev/null +++ b/day8/part2_cycles.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3.11 +from typing import List +import math +from data import Node, parse + +def find_cycle(start_node, directions, node_list) -> List[Node]: + node_cycle = [] + idx = 0 + current_node = (start_node, idx) + while current_node not in node_cycle and not current_node[0].is_Z_node() : + node_cycle.append(current_node) + idx += 1 + if directions[(idx - 1) % len(directions)] == 'L': + current_node = (current_node[0].move_left(node_list), idx % len(directions)) + else: + current_node = (current_node[0].move_right(node_list), idx % len(directions)) + node_cycle.append(current_node) + return [node for node,_ in node_cycle] + +if __name__ == '__main__': + directions, node_list = parse('input.txt') + xxA_nodes = list(filter(lambda node: node.is_A_node(), node_list)) + cycles = list(map(len,map(lambda node: find_cycle(node, directions, node_list), xxA_nodes))) + print(cycles) + print(math.lcm(*cycles)) diff --git a/day8/part2_parallel.py b/day8/part2_parallel.py new file mode 100755 index 0000000..58db312 --- /dev/null +++ b/day8/part2_parallel.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3.11 +from typing import List +import concurrent.futures +from data import Node, parse + +#It is inefficient, more time in sys than in User... + +def movement(current_node: Node, direction: str, node_list: List[Node]) -> Node: + if direction == 'L': + return current_node.move_left(node_list) + else: + return current_node.move_right(node_list) + +def count_steps_XXA_to_XXZ(directions:str, node_list: List[Node]) -> int: + current_nodes = list(filter(lambda node: node.is_A_node(), node_list)) + count = 0 + while not all(map(lambda n: n.is_Z_node(), current_nodes)): + with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: + futures = [] + direction = directions[count % len(directions)] + futures = [executor.submit(movement, node, direction, node_list) for node in current_nodes] + current_nodes = [exec.result() for exec in concurrent.futures.as_completed(futures)] + count += 1 + return count +if __name__ == '__main__': + directions, node_list = parse('input.txt') + print(count_steps_XXA_to_XXZ(directions, node_list)) + diff --git a/day8/vis.py b/day8/vis.py new file mode 100644 index 0000000..cf58072 --- /dev/null +++ b/day8/vis.py @@ -0,0 +1,34 @@ +from typing import List, Optional + +import networkx as nx +import matplotlib.pyplot as plt + +from data import parse, Node + +destinations, node_list = parse("input.txt") + +starting_nodes = [] +for node in node_list: + if node.is_A_node(): + starting_nodes.append(node) + + +def add_node(node: Node, known_nodes: List[str], graph: nx.Graph, node_list:List[Node], predecessor: Optional[str] = None): + if node.value not in known_nodes: + known_nodes.append(node.value) + add_node(node.move_left(node_list), known_nodes, graph, node_list, predecessor=node.value) + graph.add_node(node.value) + if predecessor is not None: + graph.add_edge(predecessor, node.value) + add_node(node.move_right(node_list), known_nodes, graph, node_list, predecessor=node.value) + +subplot_num=121 +for node in starting_nodes: + current_node = node + known_nodes = [] + G = nx.DiGraph() + add_node(node, known_nodes, G, node_list) + plt.subplot(subplot_num) + nx.draw(G) + subplot_num += 1 +plt.show() \ No newline at end of file