From 498e71c57c93f1c194e1c3f03f55bcb68072ee1e Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 8 Dec 2023 14:03:43 +0200 Subject: [PATCH 1/4] working, but part2 is too slow to get answer --- examples/aoc2023/day08/part1.jou | 69 +++++++++++++++++ examples/aoc2023/day08/part2_too_slow.jou | 93 +++++++++++++++++++++++ examples/aoc2023/day08/sampleinput.txt | 9 +++ examples/aoc2023/day08/sampleinput2.txt | 5 ++ examples/aoc2023/day08/sampleinput3.txt | 10 +++ 5 files changed, 186 insertions(+) create mode 100644 examples/aoc2023/day08/part1.jou create mode 100644 examples/aoc2023/day08/part2_too_slow.jou create mode 100644 examples/aoc2023/day08/sampleinput.txt create mode 100644 examples/aoc2023/day08/sampleinput2.txt create mode 100644 examples/aoc2023/day08/sampleinput3.txt diff --git a/examples/aoc2023/day08/part1.jou b/examples/aoc2023/day08/part1.jou new file mode 100644 index 00000000..05fc847f --- /dev/null +++ b/examples/aoc2023/day08/part1.jou @@ -0,0 +1,69 @@ +import "stdlib/ascii.jou" +import "stdlib/mem.jou" +import "stdlib/io.jou" +import "stdlib/str.jou" + + +class Node: + name: byte[4] + left: byte[4] + right: byte[4] + + +class Input: + left_right_string: byte[1000] + nodes: Node[1000] + nnodes: int + + def add_node(self, node: Node) -> void: + assert self->nnodes < sizeof(self->nodes)/sizeof(self->nodes[0]) + self->nodes[self->nnodes++] = node + + def find_node(self, name: byte*) -> Node*: + for i = 0; i < self->nnodes; i++: + if strcmp(self->nodes[i].name, name) == 0: + return &self->nodes[i] + assert False + + +def parse_input() -> Input*: + input: Input* = calloc(1, sizeof(*input)) + + f = fopen("sampleinput2.txt", "r") + assert f != NULL + + assert fgets(input->left_right_string, sizeof(input->left_right_string) as int, f) != NULL + trim_ascii_whitespace(input->left_right_string) + + assert fgetc(f) == '\n' + + n: Node + while fscanf(f, "%3s = (%3s, %3s)\n", &n.name, &n.left, &n.right) == 3: + input->add_node(n) + + fclose(f) + return input + + +def main() -> int: + input = parse_input() + + aaa = input->find_node("AAA") + zzz = input->find_node("ZZZ") + + current = aaa + counter = 0 + + while current != zzz: + c = input->left_right_string[counter++ % strlen(input->left_right_string)] + if c == 'L': + current = input->find_node(current->left) + elif c == 'R': + current = input->find_node(current->right) + else: + assert False + + free(input) + printf("%d\n", counter) # Output: 6 + + return 0 diff --git a/examples/aoc2023/day08/part2_too_slow.jou b/examples/aoc2023/day08/part2_too_slow.jou new file mode 100644 index 00000000..90c492da --- /dev/null +++ b/examples/aoc2023/day08/part2_too_slow.jou @@ -0,0 +1,93 @@ +import "stdlib/ascii.jou" +import "stdlib/mem.jou" +import "stdlib/io.jou" +import "stdlib/str.jou" + + +class Node: + name: byte[4] + left: byte[4] + right: byte[4] + left_node: Node* + right_node: Node* + + +class Input: + left_right_string: byte[1000] + nodes: Node[1000] + nnodes: int + + def add_node(self, node: Node) -> void: + assert self->nnodes < sizeof(self->nodes)/sizeof(self->nodes[0]) + self->nodes[self->nnodes++] = node + + def find_node(self, name: byte*) -> Node*: + for i = 0; i < self->nnodes; i++: + if strcmp(self->nodes[i].name, name) == 0: + return &self->nodes[i] + assert False + + def find_node_pointers(self) -> void: + for i = 0; i < self->nnodes; i++: + self->nodes[i].left_node = self->find_node(self->nodes[i].left) + self->nodes[i].right_node = self->find_node(self->nodes[i].right) + + +def parse_input() -> Input*: + input: Input* = calloc(1, sizeof(*input)) + + f = fopen("input.txt", "r") + assert f != NULL + + assert fgets(input->left_right_string, sizeof(input->left_right_string) as int, f) != NULL + trim_ascii_whitespace(input->left_right_string) + + assert fgetc(f) == '\n' + + n: Node + while fscanf(f, "%3s = (%3s, %3s)\n", &n.name, &n.left, &n.right) == 3: + input->add_node(n) + + input->find_node_pointers() + + fclose(f) + return input + + +def all_end_with_z(nodes: Node**, n: int) -> bool: + for i = 0; i < n; i++: + if nodes[i]->name[2] != 'Z': + return False + return True + + +def main() -> int: + input = parse_input() + + steppers: Node*[1000] + nsteppers = 0 + for i = 0; i < input->nnodes; i++: + if input->nodes[i].name[2] == 'A': + assert nsteppers < sizeof(steppers)/sizeof(steppers[0]) + steppers[nsteppers++] = &input->nodes[i] + + counter = 0L + n = strlen(input->left_right_string) + while not all_end_with_z(steppers, nsteppers): + if counter % 1000000 == 0: + printf("%lld\n", counter) + fflush(stdout) + + c = input->left_right_string[counter++ % n] + for i = 0; i < nsteppers; i++: + if c == 'L': + steppers[i] = steppers[i]->left_node + elif c == 'R': + steppers[i] = steppers[i]->right_node + else: + assert False + + free(input) + printf("%d\n", counter) # Output: 6 + + return 0 diff --git a/examples/aoc2023/day08/sampleinput.txt b/examples/aoc2023/day08/sampleinput.txt new file mode 100644 index 00000000..9029a1b2 --- /dev/null +++ b/examples/aoc2023/day08/sampleinput.txt @@ -0,0 +1,9 @@ +RL + +AAA = (BBB, CCC) +BBB = (DDD, EEE) +CCC = (ZZZ, GGG) +DDD = (DDD, DDD) +EEE = (EEE, EEE) +GGG = (GGG, GGG) +ZZZ = (ZZZ, ZZZ) diff --git a/examples/aoc2023/day08/sampleinput2.txt b/examples/aoc2023/day08/sampleinput2.txt new file mode 100644 index 00000000..7d1b58d4 --- /dev/null +++ b/examples/aoc2023/day08/sampleinput2.txt @@ -0,0 +1,5 @@ +LLR + +AAA = (BBB, BBB) +BBB = (AAA, ZZZ) +ZZZ = (ZZZ, ZZZ) diff --git a/examples/aoc2023/day08/sampleinput3.txt b/examples/aoc2023/day08/sampleinput3.txt new file mode 100644 index 00000000..5b3fa585 --- /dev/null +++ b/examples/aoc2023/day08/sampleinput3.txt @@ -0,0 +1,10 @@ +LR + +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) From 24ed473f91a57a5bc2f66eedf6b07efc4c1d6622 Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 8 Dec 2023 19:21:18 +0200 Subject: [PATCH 2/4] it works --- examples/aoc2023/day08/part2.jou | 298 +++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 examples/aoc2023/day08/part2.jou diff --git a/examples/aoc2023/day08/part2.jou b/examples/aoc2023/day08/part2.jou new file mode 100644 index 00000000..ed8ebf51 --- /dev/null +++ b/examples/aoc2023/day08/part2.jou @@ -0,0 +1,298 @@ +import "stdlib/ascii.jou" +import "stdlib/mem.jou" +import "stdlib/io.jou" +import "stdlib/str.jou" + + +class Node: + name: byte[4] + left: byte[4] + right: byte[4] + left_node: Node* + right_node: Node* + + +class Input: + left_right_string: byte[1000] + nodes: Node[1000] + nnodes: int + + def add_node(self, node: Node) -> void: + assert self->nnodes < sizeof(self->nodes)/sizeof(self->nodes[0]) + self->nodes[self->nnodes++] = node + + def find_node(self, name: byte*) -> Node*: + for i = 0; i < self->nnodes; i++: + if strcmp(self->nodes[i].name, name) == 0: + return &self->nodes[i] + assert False + + def find_node_pointers(self) -> void: + for i = 0; i < self->nnodes; i++: + self->nodes[i].left_node = self->find_node(self->nodes[i].left) + self->nodes[i].right_node = self->find_node(self->nodes[i].right) + + +class List: + ptr: long* + len: int + alloc: int + + def end(self) -> long*: + return &self->ptr[self->len] + + def last(self) -> long: + assert self->len != 0 + return self->end()[-1] + + def append(self, n: long) -> void: + if self->alloc == self->len: + if self->alloc == 0: + self->alloc = 4 + else: + self->alloc *= 2 + + self->ptr = realloc(self->ptr, self->alloc * sizeof(self->ptr[0])) + assert self->ptr != NULL + + self->ptr[self->len++] = n + + def extend(self, other: List) -> void: + for v = other.ptr; v < other.end(); v++: + self->append(*v) + + +def min(a: long, b: long) -> long: + if a > b: + return a + return b + +def max(a: long, b: long) -> long: + if a > b: + return a + return b + +def swap(a: long*, b: long*) -> void: + temp = *a + *a = *b + *b = temp + +def gcd(a: long, b: long) -> long: + assert a > 0 and b > 0 + while True: + a %= b + if a == 0: + return b + swap(&a, &b) + +def lcm(a: long, b: long) -> long: + return (a/gcd(a,b)) * b + + +# We say that a number is a Z-count for a node, if doing that many steps +# with the given node as starting node gets you into a node ending with +# letter Z. +# +# Each node ending with 'A' seems to have infinitely many Z-counts. After +# a while they will start to repeat periodically. For example, it could +# be something like: +# +# all_z_counts = [1, 2, 3, 10, 12, 15, 20, 22, 25, 30, 32, 35, ...] +# ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ +# non-repeating 1st cycle 2nd cycle 3rd cycle +# part +# +# Repeating starts where we reach the same state twice. By same state, I +# mean same current node and same index into the left_right_string. +class ZCounts: + non_repeating: List + first_cycle: List + cycle_offset: long # 10 in the example, each cycle starts 10 bigger than previous + + def free(self) -> void: + free(self->non_repeating.ptr) + free(self->first_cycle.ptr) + + def print(self) -> void: + printf(" ") + for p = self->non_repeating.ptr; p < self->non_repeating.end(); p++: + printf("%d ", *p) + for k=0; k<2; k++: + printf("| ") + for p = self->first_cycle.ptr; p < self->first_cycle.end(); p++: + printf("%d ", *p + k*self->cycle_offset) + printf("| ...\n") + + # Returns the smallest z-count that is >n. + def get_next_value(self, n: long) -> long: + if self->non_repeating.len != 0 and self->non_repeating.last() > n: + for p = self->non_repeating.ptr; p < self->non_repeating.end(); p++: + if *p > n: + return *p + + # The number we're looking for is a part of a cycle. + # The last number of the cycle is >n. + # Also, the last number of the previous cycle cannot be >n, so it is <=n. + # This is enough info to find the cycle we want: + # + # self->first_cycle.last() + k*self->cycle_offset > n + # self->first_cycle.last() + (k-1)*self->cycle_offset <= n + # + # From here we can solve: + # + # x < k <= x+1, where x = (n - self->first_cycle.last())/self->cycle_offset + # + # i.e. k = floor(x) + 1. Jou's division automatically does the floor() for us. + k = (n - self->first_cycle.last())/self->cycle_offset + 1 + if k < 0: # happens if we have large gap between non_repeating and first_cycle + k = 0 + + for p = self->first_cycle.ptr; p < self->first_cycle.end(); p++: + if *p + k*self->cycle_offset > n: + return *p + k*self->cycle_offset + + assert False + + def contains(self, n: long) -> bool: + return self->get_next_value(n-1) == n + + # Finds numbers that appear to both ZCounts instances + def intersect(self, other: ZCounts*) -> ZCounts: + if self->non_repeating.len == 0 or other->non_repeating.len == 0: + last_non_cyclic = 1L + else: + last_non_cyclic = max(self->non_repeating.last(), other->non_repeating.last()) + + n = 0L + + # Handle non-repeating part + non_repeating = List{} + while True: + n = self->get_next_value(n) + while not other->contains(n): + n = self->get_next_value(n) + if n > last_non_cyclic: + break + non_repeating.append(n) + + offset = lcm(self->cycle_offset, other->cycle_offset) + + cycle = List{} + cycle.append(n) + while True: + n = self->get_next_value(n) + if n >= cycle.ptr[0] + offset: + break + cycle.append(n) + + return ZCounts{non_repeating = non_repeating, first_cycle = cycle, cycle_offset = offset} + + +def get_z_counts(input: Input*, start: Node*) -> ZCounts: + left_right_strlen = strlen(input->left_right_string) + + # Number of (index, node) combinations. Guaranteed to cycle after this. + max_num_states = left_right_strlen * input->nnodes + + states: Node** = malloc(max_num_states * sizeof(states[0])) + assert states != NULL + nstates = 0 + + node = start + + while True: + # Check if we got the same state before + for i = 0; i < nstates; i++: + if i % left_right_strlen == nstates % left_right_strlen and states[i] == node: + # from here it cycles + non_repeating = List{} + first_cycle = List{} + for k = 0; k < nstates; k++: + if states[k]->name[2] == 'Z': + if k < i: + non_repeating.append(k) + else: + first_cycle.append(k) + free(states) + + assert first_cycle.len != 0 + return ZCounts{non_repeating=non_repeating, first_cycle=first_cycle, cycle_offset=nstates-i} + + assert nstates < max_num_states + states[nstates] = node + c = input->left_right_string[nstates % left_right_strlen] + nstates++ + + if c == 'L': + node = node->left_node + elif c == 'R': + node = node->right_node + else: + assert False + + +def parse_input() -> Input*: + input: Input* = calloc(1, sizeof(*input)) + + f = fopen("input.txt", "r") + assert f != NULL + + assert fgets(input->left_right_string, sizeof(input->left_right_string) as int, f) != NULL + trim_ascii_whitespace(input->left_right_string) + + assert fgetc(f) == '\n' + + n: Node + while fscanf(f, "%3s = (%3s, %3s)\n", &n.name, &n.left, &n.right) == 3: + input->add_node(n) + + input->find_node_pointers() + + fclose(f) + return input + + +def all_end_with_z(nodes: Node**, n: int) -> bool: + for i = 0; i < n; i++: + if nodes[i]->name[2] != 'Z': + return False + return True + + +def main() -> int: + input = parse_input() + + zcounts: ZCounts[1000] + nzcounts = 0 + for i = 0; i < input->nnodes; i++: + if input->nodes[i].name[2] == 'A': + assert nzcounts < sizeof(zcounts)/sizeof(zcounts[0]) + printf("get z counts %s\n", input->nodes[i].name) + zcounts[nzcounts++] = get_z_counts(input, &input->nodes[i]) + free(input) + + for i = 0; i < nzcounts; i++: + zcounts[i].print() + + # For some reason, each z count is actually just multiples of one number: + # + # [n, 2n, 3n, ...] + # + # Input is probably chosen so that this happens, to make things nice. + for i = 0; i < nzcounts; i++: + assert zcounts[i].non_repeating.len == 0 + assert zcounts[i].first_cycle.len == 1 + assert zcounts[i].cycle_offset == zcounts[i].first_cycle.ptr[0] + + # Compute the Least Common Multiple of what above comment calls "n". + # We basically do lcm(a,b,c,d) = lcm(lcm(lcm(a,b),c),d). + result = zcounts[0].cycle_offset + for i = 1; i < nzcounts; i++: + printf("lcm(%lld, %lld) = ", result, zcounts[i].cycle_offset) + result = lcm(result, zcounts[i].cycle_offset) + printf("%lld\n", result) + printf("%lld\n", result) # Output: 6 + + for i = 0; i < nzcounts; i++: + zcounts[i].free() + return 0 From d985b2920d1b4af976e9a36f4f5dd62d9190db50 Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 8 Dec 2023 19:23:30 +0200 Subject: [PATCH 3/4] remove unnecessary code --- examples/aoc2023/day08/part2.jou | 76 -------------------------------- 1 file changed, 76 deletions(-) diff --git a/examples/aoc2023/day08/part2.jou b/examples/aoc2023/day08/part2.jou index ed8ebf51..e8f0f7f8 100644 --- a/examples/aoc2023/day08/part2.jou +++ b/examples/aoc2023/day08/part2.jou @@ -62,16 +62,6 @@ class List: self->append(*v) -def min(a: long, b: long) -> long: - if a > b: - return a - return b - -def max(a: long, b: long) -> long: - if a > b: - return a - return b - def swap(a: long*, b: long*) -> void: temp = *a *a = *b @@ -123,70 +113,6 @@ class ZCounts: printf("%d ", *p + k*self->cycle_offset) printf("| ...\n") - # Returns the smallest z-count that is >n. - def get_next_value(self, n: long) -> long: - if self->non_repeating.len != 0 and self->non_repeating.last() > n: - for p = self->non_repeating.ptr; p < self->non_repeating.end(); p++: - if *p > n: - return *p - - # The number we're looking for is a part of a cycle. - # The last number of the cycle is >n. - # Also, the last number of the previous cycle cannot be >n, so it is <=n. - # This is enough info to find the cycle we want: - # - # self->first_cycle.last() + k*self->cycle_offset > n - # self->first_cycle.last() + (k-1)*self->cycle_offset <= n - # - # From here we can solve: - # - # x < k <= x+1, where x = (n - self->first_cycle.last())/self->cycle_offset - # - # i.e. k = floor(x) + 1. Jou's division automatically does the floor() for us. - k = (n - self->first_cycle.last())/self->cycle_offset + 1 - if k < 0: # happens if we have large gap between non_repeating and first_cycle - k = 0 - - for p = self->first_cycle.ptr; p < self->first_cycle.end(); p++: - if *p + k*self->cycle_offset > n: - return *p + k*self->cycle_offset - - assert False - - def contains(self, n: long) -> bool: - return self->get_next_value(n-1) == n - - # Finds numbers that appear to both ZCounts instances - def intersect(self, other: ZCounts*) -> ZCounts: - if self->non_repeating.len == 0 or other->non_repeating.len == 0: - last_non_cyclic = 1L - else: - last_non_cyclic = max(self->non_repeating.last(), other->non_repeating.last()) - - n = 0L - - # Handle non-repeating part - non_repeating = List{} - while True: - n = self->get_next_value(n) - while not other->contains(n): - n = self->get_next_value(n) - if n > last_non_cyclic: - break - non_repeating.append(n) - - offset = lcm(self->cycle_offset, other->cycle_offset) - - cycle = List{} - cycle.append(n) - while True: - n = self->get_next_value(n) - if n >= cycle.ptr[0] + offset: - break - cycle.append(n) - - return ZCounts{non_repeating = non_repeating, first_cycle = cycle, cycle_offset = offset} - def get_z_counts(input: Input*, start: Node*) -> ZCounts: left_right_strlen = strlen(input->left_right_string) @@ -288,9 +214,7 @@ def main() -> int: # We basically do lcm(a,b,c,d) = lcm(lcm(lcm(a,b),c),d). result = zcounts[0].cycle_offset for i = 1; i < nzcounts; i++: - printf("lcm(%lld, %lld) = ", result, zcounts[i].cycle_offset) result = lcm(result, zcounts[i].cycle_offset) - printf("%lld\n", result) printf("%lld\n", result) # Output: 6 for i = 0; i < nzcounts; i++: From f44078fd8c3007b9921e18bb1afd58b3ddc7893a Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 8 Dec 2023 19:45:15 +0200 Subject: [PATCH 4/4] clean up and make sampleinput3 work --- examples/aoc2023/day08/part2.jou | 50 +++++++++--- examples/aoc2023/day08/part2_too_slow.jou | 93 ----------------------- 2 files changed, 40 insertions(+), 103 deletions(-) delete mode 100644 examples/aoc2023/day08/part2_too_slow.jou diff --git a/examples/aoc2023/day08/part2.jou b/examples/aoc2023/day08/part2.jou index e8f0f7f8..62c25b2d 100644 --- a/examples/aoc2023/day08/part2.jou +++ b/examples/aoc2023/day08/part2.jou @@ -108,11 +108,42 @@ class ZCounts: for p = self->non_repeating.ptr; p < self->non_repeating.end(); p++: printf("%d ", *p) for k=0; k<2; k++: + # Print "|" character between cycles to distinguish them. + # Numbers before the first "|" do not belong to any cycle. printf("| ") for p = self->first_cycle.ptr; p < self->first_cycle.end(); p++: printf("%d ", *p + k*self->cycle_offset) printf("| ...\n") + def simplify_cycles(self) -> void: + # If the first cycle repeats the same thing twice, reduce it to half of its length. + # If the first cycle repeats the same thing 3 times, reduce it to 1/3 length. + # And so on. + # + # Example: + # before: | 3 6 | 9 12 | ... + # after: | 3 | 6 | 9 | 12 | ... + # + # We try more parts first: "| 1 2 3 4 | 5 6 7 8 | ..." should be split into 4 + # parts, not into 2 parts. + for num_parts = self->first_cycle.len; num_parts >= 2; num_parts--: + if self->first_cycle.len % num_parts != 0 or self->cycle_offset % num_parts != 0: + continue + + small_cycle_len = self->first_cycle.len / num_parts + small_offset = self->cycle_offset / num_parts + + can_split = True + for i = 0; i < self->first_cycle.len - small_cycle_len; i++: + if self->first_cycle.ptr[i] + small_offset != self->first_cycle.ptr[i + small_cycle_len]: + can_split = False + break + + if can_split: + self->first_cycle.len = small_cycle_len + self->cycle_offset = small_offset + return + def get_z_counts(input: Input*, start: Node*) -> ZCounts: left_right_strlen = strlen(input->left_right_string) @@ -160,7 +191,7 @@ def get_z_counts(input: Input*, start: Node*) -> ZCounts: def parse_input() -> Input*: input: Input* = calloc(1, sizeof(*input)) - f = fopen("input.txt", "r") + f = fopen("sampleinput3.txt", "r") assert f != NULL assert fgets(input->left_right_string, sizeof(input->left_right_string) as int, f) != NULL @@ -178,27 +209,26 @@ def parse_input() -> Input*: return input -def all_end_with_z(nodes: Node**, n: int) -> bool: - for i = 0; i < n; i++: - if nodes[i]->name[2] != 'Z': - return False - return True - - def main() -> int: input = parse_input() zcounts: ZCounts[1000] nzcounts = 0 + for i = 0; i < input->nnodes; i++: if input->nodes[i].name[2] == 'A': assert nzcounts < sizeof(zcounts)/sizeof(zcounts[0]) - printf("get z counts %s\n", input->nodes[i].name) + if input->nnodes > 500: + # progress print for the real input + printf("get z counts: %s\n", input->nodes[i].name) zcounts[nzcounts++] = get_z_counts(input, &input->nodes[i]) + + # Rest of the solution only deals with zcounts free(input) for i = 0; i < nzcounts; i++: - zcounts[i].print() + zcounts[i].simplify_cycles() + #zcounts[i].print() # For some reason, each z count is actually just multiples of one number: # diff --git a/examples/aoc2023/day08/part2_too_slow.jou b/examples/aoc2023/day08/part2_too_slow.jou deleted file mode 100644 index 90c492da..00000000 --- a/examples/aoc2023/day08/part2_too_slow.jou +++ /dev/null @@ -1,93 +0,0 @@ -import "stdlib/ascii.jou" -import "stdlib/mem.jou" -import "stdlib/io.jou" -import "stdlib/str.jou" - - -class Node: - name: byte[4] - left: byte[4] - right: byte[4] - left_node: Node* - right_node: Node* - - -class Input: - left_right_string: byte[1000] - nodes: Node[1000] - nnodes: int - - def add_node(self, node: Node) -> void: - assert self->nnodes < sizeof(self->nodes)/sizeof(self->nodes[0]) - self->nodes[self->nnodes++] = node - - def find_node(self, name: byte*) -> Node*: - for i = 0; i < self->nnodes; i++: - if strcmp(self->nodes[i].name, name) == 0: - return &self->nodes[i] - assert False - - def find_node_pointers(self) -> void: - for i = 0; i < self->nnodes; i++: - self->nodes[i].left_node = self->find_node(self->nodes[i].left) - self->nodes[i].right_node = self->find_node(self->nodes[i].right) - - -def parse_input() -> Input*: - input: Input* = calloc(1, sizeof(*input)) - - f = fopen("input.txt", "r") - assert f != NULL - - assert fgets(input->left_right_string, sizeof(input->left_right_string) as int, f) != NULL - trim_ascii_whitespace(input->left_right_string) - - assert fgetc(f) == '\n' - - n: Node - while fscanf(f, "%3s = (%3s, %3s)\n", &n.name, &n.left, &n.right) == 3: - input->add_node(n) - - input->find_node_pointers() - - fclose(f) - return input - - -def all_end_with_z(nodes: Node**, n: int) -> bool: - for i = 0; i < n; i++: - if nodes[i]->name[2] != 'Z': - return False - return True - - -def main() -> int: - input = parse_input() - - steppers: Node*[1000] - nsteppers = 0 - for i = 0; i < input->nnodes; i++: - if input->nodes[i].name[2] == 'A': - assert nsteppers < sizeof(steppers)/sizeof(steppers[0]) - steppers[nsteppers++] = &input->nodes[i] - - counter = 0L - n = strlen(input->left_right_string) - while not all_end_with_z(steppers, nsteppers): - if counter % 1000000 == 0: - printf("%lld\n", counter) - fflush(stdout) - - c = input->left_right_string[counter++ % n] - for i = 0; i < nsteppers; i++: - if c == 'L': - steppers[i] = steppers[i]->left_node - elif c == 'R': - steppers[i] = steppers[i]->right_node - else: - assert False - - free(input) - printf("%d\n", counter) # Output: 6 - - return 0