From 866ed660b90513578a09421b835a51d35ba424d6 Mon Sep 17 00:00:00 2001 From: Akuli Date: Wed, 13 Dec 2023 18:01:54 +0200 Subject: [PATCH 1/3] move run_exe() to separate file (#449) --- src/output.c | 24 ------------------------ src/run.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 src/run.c diff --git a/src/output.c b/src/output.c index 375d9ebc..a7e51e0a 100644 --- a/src/output.c +++ b/src/output.c @@ -184,27 +184,3 @@ char *compile_to_object_file(LLVMModuleRef module) assert(!error); return path; } - -int run_exe(const char *exepath, bool valgrind) -{ - char *command = malloc(strlen(exepath) + 1000); -#ifdef _WIN32 - sprintf(command, "\"%s\"", exepath); - char *p; - while ((p = strchr(command, '/'))) - *p = '\\'; -#else - if (valgrind) - sprintf(command, "valgrind -q --leak-check=full --show-leak-kinds=all --error-exitcode=1 '%s'", exepath); - else - sprintf(command, "'%s'", exepath); -#endif - - // Make sure that everything else shows up before the user's prints. - fflush(stdout); - fflush(stderr); - - int ret = system(command); - free(command); - return !!ret; -} diff --git a/src/run.c b/src/run.c new file mode 100644 index 00000000..b92dfe3c --- /dev/null +++ b/src/run.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include "jou_compiler.h" + + +int run_exe(const char *exepath, bool valgrind) +{ + char *command = malloc(strlen(exepath) + 1000); +#ifdef _WIN32 + sprintf(command, "\"%s\"", exepath); + char *p; + while ((p = strchr(command, '/'))) + *p = '\\'; +#else + if (valgrind) + sprintf(command, "valgrind -q --leak-check=full --show-leak-kinds=all --error-exitcode=1 '%s'", exepath); + else + sprintf(command, "'%s'", exepath); +#endif + + // Make sure that everything else shows up before the user's prints. + fflush(stdout); + fflush(stderr); + + int ret = system(command); + free(command); + return !!ret; +} From 118f1b1b1408345eb3994c136bfe8c03c6f7c70d Mon Sep 17 00:00:00 2001 From: Akuli Date: Thu, 14 Dec 2023 15:56:43 +0200 Subject: [PATCH 2/3] Aoc days 12, 13, 14 (#451) --- examples/aoc2023/day12/part1.jou | 95 ++++++++++++++ examples/aoc2023/day12/part2.jou | 166 +++++++++++++++++++++++++ examples/aoc2023/day12/sampleinput.txt | 6 + examples/aoc2023/day13/part1.jou | 104 ++++++++++++++++ examples/aoc2023/day13/part2.jou | 108 ++++++++++++++++ examples/aoc2023/day13/sampleinput.txt | 15 +++ examples/aoc2023/day14/part1.jou | 81 ++++++++++++ examples/aoc2023/day14/part2.jou | 147 ++++++++++++++++++++++ examples/aoc2023/day14/sampleinput.txt | 10 ++ 9 files changed, 732 insertions(+) create mode 100644 examples/aoc2023/day12/part1.jou create mode 100644 examples/aoc2023/day12/part2.jou create mode 100644 examples/aoc2023/day12/sampleinput.txt create mode 100644 examples/aoc2023/day13/part1.jou create mode 100644 examples/aoc2023/day13/part2.jou create mode 100644 examples/aoc2023/day13/sampleinput.txt create mode 100644 examples/aoc2023/day14/part1.jou create mode 100644 examples/aoc2023/day14/part2.jou create mode 100644 examples/aoc2023/day14/sampleinput.txt diff --git a/examples/aoc2023/day12/part1.jou b/examples/aoc2023/day12/part1.jou new file mode 100644 index 00000000..e3c326c1 --- /dev/null +++ b/examples/aoc2023/day12/part1.jou @@ -0,0 +1,95 @@ +import "stdlib/str.jou" +import "stdlib/io.jou" +import "stdlib/mem.jou" + + +# arrays are empty string terminated +def append_all(dest: byte[100]*, src: byte[100]*) -> byte[100]*: + while (*src)[0] != '\0': + *dest++ = *src++ + *dest = "" + return dest + + +def count(arr: byte[100]*) -> int: + n = 0 + while arr[n][0] != '\0': + n++ + return n + + +# returns NULL terminated array +def substitute_questionmarks_in_all_ways(questional_string: byte*) -> byte[100]*: + assert strlen(questional_string) == strspn(questional_string, "?.#") + assert 0 < strlen(questional_string) and strlen(questional_string) < 100 + + for i = 0; questional_string[i] != '\0'; i++: + if questional_string[i] == '?': + temp: byte[100] + strcpy(temp, questional_string) + + temp[i] = '#' + with_hashtag = substitute_questionmarks_in_all_ways(temp) + temp[i] = '.' + without_hashtag = substitute_questionmarks_in_all_ways(temp) + + # Concatenate lists + result: byte[100]* = malloc(sizeof(result[0]) * (count(with_hashtag) + count(without_hashtag) + 1)) + assert result != NULL + append_all(append_all(result, with_hashtag), without_hashtag) + free(with_hashtag) + free(without_hashtag) + return result + + result = malloc(sizeof(result[0]) * 2) + strcpy(result[0], questional_string) + result[1] = "" + return result + + +# "##..#..###" --> "2,1,3" +def generate_numbers(non_questional_string: byte*) -> byte[100]: + assert strlen(non_questional_string) < 50 + result: byte[100] = "" + + prev = '.' + for p = non_questional_string; *p != '\0'; p++: + assert *p == '#' or *p == '.' + if prev == '.' and *p == '#': + n = 1 + elif prev == '#' and *p == '#': + n++ + elif prev == '#' and *p == '.': + sprintf(&result[strlen(result)], "%d,", n) + prev = *p + + if prev == '#': + sprintf(&result[strlen(result)], "%d,", n) + + if result[0] != '\0': + # remove last comma + result[strlen(result) - 1] = '\0' + return result + + +def main() -> int: + f = fopen("sampleinput.txt", "r") + assert f != NULL + + questional_string: byte[100] + non_questional_string: byte[100] + + result = 0 + + while fscanf(f, "%99s %99s\n", questional_string, non_questional_string) == 2: + #puts(questional_string) + possibilities = substitute_questionmarks_in_all_ways(questional_string) + for i = 0; possibilities[i][0] != '\0'; i++: + s = generate_numbers(possibilities[i]) + if strcmp(s, non_questional_string) == 0: + result++ + free(possibilities) + + fclose(f) + printf("%d\n", result) # Output: 21 + return 0 diff --git a/examples/aoc2023/day12/part2.jou b/examples/aoc2023/day12/part2.jou new file mode 100644 index 00000000..2c5b6b0a --- /dev/null +++ b/examples/aoc2023/day12/part2.jou @@ -0,0 +1,166 @@ +import "stdlib/str.jou" +import "stdlib/io.jou" +import "stdlib/mem.jou" + + +class State: + questional_string: byte[200] + numbers: int* # -1 terminated, not owned + repeat_count: long # how many equal states does this object represent, not enough mem without this + + def print(self) -> None: + printf("State{qs=\"%s\", nums=[", self->questional_string) + for i = 0; self->numbers[i] != -1; i++: + if i > 0: + printf(",") + printf("%d", self->numbers[i]) + printf("], repeat=%lld}\n", self->repeat_count) + fflush(stdout) + + def simplify(self) -> None: + s = &self->questional_string[0] + nums = self->numbers + + while s[0] == '.': + s++ + + while True: + n1 = strspn(s, "#") + n2 = strspn(s, "#?") + if n1 == n2 and n1 == nums[0]: + # Skip number and hashtag + nums++ + s = &s[n1] + while s[0] == '.': + s++ + else: + break + + memcpy(&self->questional_string, s, strlen(s) + 1) + self->numbers = nums + + def equals(self, other: State*) -> bool: + return ( + self->numbers == other->numbers + and strcmp(self->questional_string, other->questional_string) == 0 + ) + + # Returns whether a string can be filled according to consecutive_counts + # "##.#?.#", [3, 2, 1] --> True + # + # Does not detect some impossible cases until remaining question marks are substituted. + def is_possible(self) -> bool: + if self->questional_string[0] == '\0': + # ran out of string + # return false, if we still have numbers + return self->numbers[0] == -1 + + if self->numbers[0] == -1: + # ran out of numbers + # return false, if we still must put '#' in string + return strstr(self->questional_string, "#") == NULL + + # This state is impossible if: + # * it starts with a section of hashtags + # * there is wrong number of hashtags + # * there are no question marks after hashtags + # + # There are other cases we could say this state is impossible, but we don't have to. + n1 = strspn(self->questional_string, "#") + n2 = strspn(self->questional_string, "#?") + if n1 == n2 and n1 > 0 and self->numbers[0] != n1: + return False + return True + + # Substitutes first question mark with '#' and '.' + def substitute_both_ways(self) -> State[2]: + assert strchr(self->questional_string, '?') != NULL + result = [*self, *self] + *strchr(result[0].questional_string, '?') = '#' + *strchr(result[1].questional_string, '?') = '.' + return result + + +def split_ints_by_commas(s: byte*) -> int[200]: + assert strlen(s) < 200 + result: int[200] + p = &result[0] + + while True: + *p++ = atoi(s) + s = strchr(s, ',') + if s == NULL: + *p = -1 + return result + s++ + + +def repeat_5x(s: byte*, sep: byte) -> None: + n = strlen(s) + strcpy(&s[n+1], s) + strcpy(&s[2*(n+1)], s) + strcpy(&s[3*(n+1)], s) + strcpy(&s[4*(n+1)], s) + s[n] = sep + s[2*n+1] = sep + s[3*n+2] = sep + s[4*n+3] = sep + + +def main() -> int: + f = fopen("sampleinput.txt", "r") + assert f != NULL + + questional_string: byte[200] + number_string: byte[200] + + result = 0L + + while fscanf(f, "%39s %39s\n", questional_string, number_string) == 2: + repeat_5x(questional_string, '?') + repeat_5x(number_string, ',') + + numbers = split_ints_by_commas(number_string) + + n_question_marks = 0 + for i = 0; questional_string[i] != '\0'; i++: + if questional_string[i] == '?': + n_question_marks++ + + states: State* = malloc(sizeof(states[0])) + states[0] = State{questional_string=questional_string, numbers=numbers, repeat_count=1} + nstates = 1 + + while n_question_marks --> 0: + states = realloc(states, sizeof(states[0]) * (2*nstates)) + assert states != NULL + + for i = nstates-1; i >= 0; i--: + both_ways = states[i].substitute_both_ways() + states[2*i] = both_ways[0] + states[2*i+1] = both_ways[1] + nstates *= 2 + + # Simplify states to merge as much as possible + for i = 0; i < nstates; i++: + states[i].simplify() + + # Merge duplicate states + for i = 0; i < nstates; i++: + for k = nstates-1; k > i; k--: + if states[i].equals(&states[k]): + states[i].repeat_count += states[k].repeat_count + states[k] = states[--nstates] + + # Delete impossible states + for i = nstates-1; i >= 0; i--: + if not states[i].is_possible(): + states[i] = states[--nstates] + + for i = 0; i < nstates; i++: + result += states[i].repeat_count + free(states) + + fclose(f) + printf("%lld\n", result) # Output: 525152 + return 0 diff --git a/examples/aoc2023/day12/sampleinput.txt b/examples/aoc2023/day12/sampleinput.txt new file mode 100644 index 00000000..e9259357 --- /dev/null +++ b/examples/aoc2023/day12/sampleinput.txt @@ -0,0 +1,6 @@ +???.### 1,1,3 +.??..??...?##. 1,1,3 +?#?#?#?#?#?#?#? 1,3,1,6 +????.#...#... 4,1,1 +????.######..#####. 1,6,5 +?###???????? 3,2,1 diff --git a/examples/aoc2023/day13/part1.jou b/examples/aoc2023/day13/part1.jou new file mode 100644 index 00000000..1b495f2a --- /dev/null +++ b/examples/aoc2023/day13/part1.jou @@ -0,0 +1,104 @@ +import "stdlib/io.jou" +import "stdlib/str.jou" +import "stdlib/mem.jou" +import "stdlib/ascii.jou" + + +class Grid: + width: int + height: int + data: byte* + + def is_in_bounds(self, point: int[2]) -> bool: + x = point[0] + y = point[1] + return 0 <= x and x < self->width and 0 <= y and y < self->height + + def get(self, point: int[2]) -> byte: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + return self->data[(self->width + 1)*y + x] + + +def find_h_reflection(g: Grid*) -> int: + for reflect = 1; reflect < g->width; reflect++: + left = reflect-1 + right = reflect + matches = True + while left >= 0 and right < g->width and matches: + for y = 0; y < g->height; y++: + if g->get([left, y]) != g->get([right, y]): + matches = False + break + left-- + right++ + if matches: + return reflect + return -1 + + +def find_v_reflection(g: Grid*) -> int: + for reflect = 1; reflect < g->height; reflect++: + top = reflect-1 + bottom = reflect + matches = True + while top >= 0 and bottom < g->height and matches: + for x = 0; x < g->width; x++: + if g->get([x, top]) != g->get([x, bottom]): + matches = False + break + top-- + bottom++ + if matches: + return reflect + return -1 + + +def main() -> int: + f = fopen("sampleinput.txt", "r") + assert f != NULL + + line: byte[200] + max_size = 10000 + + grid = Grid{data = malloc(max_size)} + result = 0 + + while True: + grid.width = 0 + grid.height = 0 + grid.data[0] = '\0' + + while fgets(line, sizeof(line) as int, f) != NULL: + trim_ascii_whitespace(line) + if line[0] == '\0': + break + + if grid.height == 0: # set width on first round + grid.width = strlen(line) as int + assert grid.width == strlen(line) + grid.height++ + + assert grid.width * grid.height < max_size + strcat(grid.data, line) + strcat(grid.data, "\n") + + if grid.height == 0: + # end of input + break + + h = find_h_reflection(&grid) + v = find_v_reflection(&grid) + assert h == -1 or v == -1 # does not have both + if h != -1: + result += h + elif v != -1: + result += 100*v + else: + assert False + + free(grid.data) + fclose(f) + printf("%d\n", result) # Output: 405 + return 0 diff --git a/examples/aoc2023/day13/part2.jou b/examples/aoc2023/day13/part2.jou new file mode 100644 index 00000000..6ec1f8dc --- /dev/null +++ b/examples/aoc2023/day13/part2.jou @@ -0,0 +1,108 @@ +import "stdlib/io.jou" +import "stdlib/str.jou" +import "stdlib/mem.jou" +import "stdlib/ascii.jou" + + +class Grid: + width: int + height: int + data: byte* + + def is_in_bounds(self, point: int[2]) -> bool: + x = point[0] + y = point[1] + return 0 <= x and x < self->width and 0 <= y and y < self->height + + def get(self, point: int[2]) -> byte: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + return self->data[(self->width + 1)*y + x] + + +def find_h_reflection(g: Grid*, mismatch_count: int) -> int: + for reflect = 1; reflect < g->width; reflect++: + left = reflect-1 + right = reflect + n = 0 + + while left >= 0 and right < g->width: + for y = 0; y < g->height; y++: + if g->get([left, y]) != g->get([right, y]): + n++ + left-- + right++ + + if n == mismatch_count: + return reflect + + return -1 + + +def find_v_reflection(g: Grid*, mismatch_count: int) -> int: + for reflect = 1; reflect < g->height; reflect++: + top = reflect-1 + bottom = reflect + n = 0 + + while top >= 0 and bottom < g->height: + for x = 0; x < g->width; x++: + if g->get([x, top]) != g->get([x, bottom]): + n++ + top-- + bottom++ + + if n == mismatch_count: + return reflect + + return -1 + + +def main() -> int: + f = fopen("sampleinput.txt", "r") + assert f != NULL + + line: byte[200] + max_size = 10000 + + grid = Grid{data = malloc(max_size)} + result = 0 + + while True: + grid.width = 0 + grid.height = 0 + grid.data[0] = '\0' + + while fgets(line, sizeof(line) as int, f) != NULL: + trim_ascii_whitespace(line) + if line[0] == '\0': + break + + if grid.height == 0: # set width on first round + grid.width = strlen(line) as int + assert grid.width == strlen(line) + grid.height++ + + assert grid.width * grid.height < max_size + strcat(grid.data, line) + strcat(grid.data, "\n") + + if grid.height == 0: + # end of input + break + + h = find_h_reflection(&grid, 1) + v = find_v_reflection(&grid, 1) + assert h == -1 or v == -1 # does not have both + if h != -1: + result += h + elif v != -1: + result += 100*v + else: + assert False + + free(grid.data) + fclose(f) + printf("%d\n", result) # Output: 400 + return 0 diff --git a/examples/aoc2023/day13/sampleinput.txt b/examples/aoc2023/day13/sampleinput.txt new file mode 100644 index 00000000..3b6b5ccd --- /dev/null +++ b/examples/aoc2023/day13/sampleinput.txt @@ -0,0 +1,15 @@ +#.##..##. +..#.##.#. +##......# +##......# +..#.##.#. +..##..##. +#.#.##.#. + +#...##..# +#....#..# +..##..### +#####.##. +#####.##. +..##..### +#....#..# diff --git a/examples/aoc2023/day14/part1.jou b/examples/aoc2023/day14/part1.jou new file mode 100644 index 00000000..7373bcdd --- /dev/null +++ b/examples/aoc2023/day14/part1.jou @@ -0,0 +1,81 @@ +import "stdlib/io.jou" +import "stdlib/mem.jou" +import "stdlib/str.jou" +import "stdlib/ascii.jou" + + +class Grid: + width: int + height: int + data: byte* + + def is_in_bounds(self, point: int[2]) -> bool: + x = point[0] + y = point[1] + return 0 <= x and x < self->width and 0 <= y and y < self->height + + def get(self, point: int[2]) -> byte: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + return self->data[(self->width + 1)*y + x] + + def set(self, point: int[2], value: byte) -> None: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + self->data[(self->width + 1)*y + x] = value + + +def roll_north(grid: Grid*) -> None: + for ysrc = 1; ysrc < grid->height; ysrc++: + for x = 0; x < grid->width; x++: + if grid->get([x, ysrc]) != 'O' or grid->get([x, ysrc-1]) != '.': + continue + + ydest = ysrc - 1 + while ydest > 0 and grid->get([x, ydest-1]) == '.': + ydest-- + + grid->set([x, ysrc], '.') + grid->set([x, ydest], 'O') + + +def calculate_load(g: Grid*) -> int: + result = 0 + for y = 0; y < g->height; y++: + load_per_O = g->height - y + for x = 0; x < g->width; x++: + if g->get([x, y]) == 'O': + result += load_per_O + return result + + +def main() -> int: + f = fopen("sampleinput.txt", "r") + assert f != NULL + + line: byte[200] + max_size = 100000 + + grid = Grid{data = malloc(max_size)} + grid.data[0] = '\0' + + while fgets(line, sizeof(line) as int, f) != NULL: + trim_ascii_whitespace(line) + if grid.height == 0: # set width on first round + grid.width = strlen(line) as int + assert grid.width == strlen(line) + grid.height++ + + assert grid.width * grid.height < max_size + strcat(grid.data, line) + strcat(grid.data, "\n") + + fclose(f) + + roll_north(&grid) + printf("%d\n", calculate_load(&grid)) # Output: 136 + + free(grid.data) + return 0 diff --git a/examples/aoc2023/day14/part2.jou b/examples/aoc2023/day14/part2.jou new file mode 100644 index 00000000..70ef0a39 --- /dev/null +++ b/examples/aoc2023/day14/part2.jou @@ -0,0 +1,147 @@ +import "stdlib/io.jou" +import "stdlib/mem.jou" +import "stdlib/str.jou" +import "stdlib/ascii.jou" +import "stdlib/math.jou" + + +class Grid: + width: int + height: int + data: byte* + + def is_in_bounds(self, point: int[2]) -> bool: + x = point[0] + y = point[1] + return 0 <= x and x < self->width and 0 <= y and y < self->height + + def get(self, point: int[2]) -> byte: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + return self->data[(self->width + 1)*y + x] + + def set(self, point: int[2], value: byte) -> None: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + self->data[(self->width + 1)*y + x] = value + + def swap(self, a: int[2], b: int[2]) -> None: + old_a = self->get(a) + self->set(a, self->get(b)) + self->set(b, old_a) + + def copy(self) -> Grid: + return Grid{width = self->width, height = self->height, data = strdup(self->data)} + + +def can_roll(grid: Grid*, rock: int[2], dir: int[2], n: int) -> bool: + new_rock_pos = [rock[0] + n*dir[0], rock[1] + n*dir[1]] + return ( + grid->get(rock) == 'O' + and grid->is_in_bounds(new_rock_pos) + and grid->get(new_rock_pos) == '.' + ) + + +def roll_one_round_rock(grid: Grid*, rock: int[2], dir: int[2]) -> None: + if can_roll(grid, rock, dir, 1): + n = 1 + while can_roll(grid, rock, dir, n+1): + n++ + grid->swap(rock, [rock[0] + n*dir[0], rock[1] + n*dir[1]]) + + +def roll(grid: Grid*, dir: int[2]) -> None: + assert (dir[0] == 0 and abs(dir[1]) == 1) or (dir[1] == 0 and abs(dir[0]) == 1) + + # Traverse grid opposite to the given direction, so that rocks won't block each other's rolling. + x_backwards = (dir[0] == 1) + y_backwards = (dir[1] == 1) + + if x_backwards: + x0 = grid->width - 1 + dx = -1 + else: + x0 = 0 + dx = 1 + + if y_backwards: + y0 = grid->height - 1 + dy = -1 + else: + y0 = 0 + dy = 1 + + for y = y0; 0 <= y and y < grid->height; y += dy: + for x = x0; 0 <= x and x < grid->width; x += dx: + roll_one_round_rock(grid, [x, y], dir) + + +def calculate_load(g: Grid*) -> int: + result = 0 + for y = 0; y < g->height; y++: + load_per_O = g->height - y + for x = 0; x < g->width; x++: + if g->get([x, y]) == 'O': + result += load_per_O + return result + + +def main() -> int: + f = fopen("sampleinput.txt", "r") + assert f != NULL + + line: byte[200] + max_size = 100000 + + grid = Grid{data = malloc(max_size)} + grid.data[0] = '\0' + + while fgets(line, sizeof(line) as int, f) != NULL: + trim_ascii_whitespace(line) + if grid.height == 0: # set width on first round + grid.width = strlen(line) as int + assert grid.width == strlen(line) + grid.height++ + + assert grid.width * grid.height < max_size + strcat(grid.data, line) + strcat(grid.data, "\n") + + fclose(f) + + previous_states: Grid* = NULL + n = 0 + + # Eventually the states will repeat in a cyclic way. + cycle_start = -1 + cycle_length = -1 + while cycle_start == -1 and cycle_length == -1: + previous_states = realloc(previous_states, sizeof(previous_states[0]) * (n+1)) + previous_states[n++] = grid + + grid = grid.copy() + roll(&grid, [0, -1]) + roll(&grid, [-1, 0]) + roll(&grid, [0, 1]) + roll(&grid, [1, 0]) + + for i = 0; i < n; i++: + if strcmp(previous_states[i].data, grid.data) == 0: + # State i and state n are the same. + # We haven't added state n to the list, but we don't have to. + cycle_start = i + cycle_length = n-i + break + + i = cycle_start + ((1000000000 - cycle_start) % cycle_length) + printf("%d\n", calculate_load(&previous_states[i])) # Output: 64 + + free(grid.data) + for i = 0; i < n; i++: + free(previous_states[i].data) + free(previous_states) + + return 0 diff --git a/examples/aoc2023/day14/sampleinput.txt b/examples/aoc2023/day14/sampleinput.txt new file mode 100644 index 00000000..5a24dce3 --- /dev/null +++ b/examples/aoc2023/day14/sampleinput.txt @@ -0,0 +1,10 @@ +O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#.... From 0023feca92ad03125f8ace265e9214abba8e7b1d Mon Sep 17 00:00:00 2001 From: Akuli Date: Thu, 14 Dec 2023 16:30:15 +0200 Subject: [PATCH 3/3] Refactor grid-related AoC stuff to new file (#452) --- examples/aoc2023/day10/part1.jou | 52 ++++------------- examples/aoc2023/day10/part2.jou | 54 ++++------------- examples/aoc2023/day11/part1.jou | 80 +++++++------------------- examples/aoc2023/day11/part2.jou | 80 +++++++------------------- examples/aoc2023/day13/part1.jou | 76 +++--------------------- examples/aoc2023/day13/part2.jou | 78 +++---------------------- examples/aoc2023/day14/part1.jou | 46 +-------------- examples/aoc2023/day14/part2.jou | 52 +---------------- examples/aoc2023/grid.jou | 99 ++++++++++++++++++++++++++++++++ 9 files changed, 186 insertions(+), 431 deletions(-) create mode 100644 examples/aoc2023/grid.jou diff --git a/examples/aoc2023/day10/part1.jou b/examples/aoc2023/day10/part1.jou index 5cf3106c..ed0884fb 100644 --- a/examples/aoc2023/day10/part1.jou +++ b/examples/aoc2023/day10/part1.jou @@ -1,32 +1,6 @@ import "stdlib/io.jou" import "stdlib/mem.jou" -import "stdlib/str.jou" - - -class Input: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - -def find_S(input: Input*) -> int[2]: - for x = 0; x < input->width; x++: - for y = 0; y < input->height; y++: - if input->get([x, y]) == 'S': - return [x, y] - - assert False +import "../grid.jou" def get_dirs(c: byte) -> int[2][2]: @@ -50,14 +24,14 @@ def eq(a: int[2], b: int[2]) -> bool: return a[0] == b[0] and a[1] == b[1] -def find_initial_direction(input: Input*, S: int[2]) -> int[2]: +def find_initial_direction(grid: Grid*, S: int[2]) -> int[2]: directions = [[0,1], [0,-1], [1,0], [-1,0]] for d = &directions[0]; d < &directions[4]; d++: S_to_neighbor = *d neighbor = [S[0] + S_to_neighbor[0], S[1] + S_to_neighbor[1]] - if input->is_in_bounds(neighbor) and input->get(neighbor) != '.': - dirs = get_dirs(input->get(neighbor)) + if grid->is_in_bounds(neighbor) and grid->get(neighbor) != '.': + dirs = get_dirs(grid->get(neighbor)) if eq(dirs[0], S_to_neighbor) or eq(dirs[1], S_to_neighbor): return S_to_neighbor @@ -65,28 +39,22 @@ def find_initial_direction(input: Input*, S: int[2]) -> int[2]: def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - point = find_S(&input) + point = grid.find_first('S') # take first step away from S - dir = find_initial_direction(&input, point) + dir = find_initial_direction(&grid, point) point[0] += dir[0] point[1] += dir[1] loop_length = 1 - while input.get(point) != 'S': + while grid.get(point) != 'S': came_from = [-dir[0], -dir[1]] - dirs = get_dirs(input.get(point)) + dirs = get_dirs(grid.get(point)) assert eq(came_from, dirs[0]) or eq(came_from, dirs[1]) if eq(came_from, dirs[0]): dir = dirs[1] @@ -99,5 +67,5 @@ def main() -> int: assert loop_length % 2 == 0 printf("%d\n", loop_length / 2) # Output: 4 - free(input.data) + free(grid.data) return 0 diff --git a/examples/aoc2023/day10/part2.jou b/examples/aoc2023/day10/part2.jou index 1319c025..21d9d824 100644 --- a/examples/aoc2023/day10/part2.jou +++ b/examples/aoc2023/day10/part2.jou @@ -1,33 +1,7 @@ import "stdlib/io.jou" import "stdlib/mem.jou" -import "stdlib/str.jou" import "stdlib/math.jou" - - -class Input: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - -def find_S(input: Input*) -> int[2]: - for x = 0; x < input->width; x++: - for y = 0; y < input->height; y++: - if input->get([x, y]) == 'S': - return [x, y] - - assert False +import "../grid.jou" def get_dirs(c: byte) -> int[2][2]: @@ -51,14 +25,14 @@ def eq(a: int[2], b: int[2]) -> bool: return a[0] == b[0] and a[1] == b[1] -def find_initial_direction(input: Input*, S: int[2]) -> int[2]: +def find_initial_direction(grid: Grid*, S: int[2]) -> int[2]: directions = [[0,1], [0,-1], [1,0], [-1,0]] for d = &directions[0]; d < &directions[4]; d++: S_to_neighbor = *d neighbor = [S[0] + S_to_neighbor[0], S[1] + S_to_neighbor[1]] - if input->is_in_bounds(neighbor) and input->get(neighbor) != '.': - dirs = get_dirs(input->get(neighbor)) + if grid->is_in_bounds(neighbor) and grid->get(neighbor) != '.': + dirs = get_dirs(grid->get(neighbor)) if eq(dirs[0], S_to_neighbor) or eq(dirs[1], S_to_neighbor): return S_to_neighbor @@ -107,31 +81,25 @@ def polygon_area(corners: int[2]*, ncorners: int) -> int: def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput2.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - point = find_S(&input) + point = grid.find_first('S') - loop: int[2]* = malloc(input.width * input.height * sizeof(loop[0])) + loop: int[2]* = malloc(grid.width * grid.height * sizeof(loop[0])) loop[0] = point # take first step away from S - dir = find_initial_direction(&input, point) + dir = find_initial_direction(&grid, point) point[0] += dir[0] point[1] += dir[1] loop_length = 1 - while input.get(point) != 'S': + while grid.get(point) != 'S': came_from = [-dir[0], -dir[1]] - dirs = get_dirs(input.get(point)) + dirs = get_dirs(grid.get(point)) assert eq(came_from, dirs[0]) or eq(came_from, dirs[1]) if eq(came_from, dirs[0]): dir = dirs[1] @@ -161,6 +129,6 @@ def main() -> int: # towards the inside. printf("%d\n", area_along_middle_of_path + 1 - loop_length/2) # Output: 4 - free(input.data) + free(grid.data) free(loop) return 0 diff --git a/examples/aoc2023/day11/part1.jou b/examples/aoc2023/day11/part1.jou index ea39cd6f..6c42cb25 100644 --- a/examples/aoc2023/day11/part1.jou +++ b/examples/aoc2023/day11/part1.jou @@ -2,54 +2,24 @@ import "stdlib/io.jou" import "stdlib/mem.jou" import "stdlib/str.jou" import "stdlib/math.jou" +import "../grid.jou" -class Input: - width: int - height: int - data: byte* +def duplicate_blank_lines(grid: Grid*) -> None: + grid->data = realloc(grid->data, 2*strlen(grid->data) + 1) + assert grid->data != NULL - def duplicate_blank_lines(self) -> None: - self->data = realloc(self->data, 2*strlen(self->data) + 1) - assert self->data != NULL - - y = 0 - while y < self->height: - if strspn(&self->data[(self->width + 1)*y], ".") == self->width: - # duplicate row - blank_start = &self->data[(self->width + 1)*y] - next_start = &self->data[(self->width + 1)*(y+1)] - memmove(next_start, blank_start, strlen(blank_start) + 1) - y += 2 - self->height++ - else: - y++ - - def transpose(self) -> None: - old_content = strdup(self->data) - old_width = self->width - old_height = self->height - self->width = old_height - self->height = old_width - - memset(self->data, '\n', strlen(self->data)) - for x = 0; x < self->width; x++: - for y = 0; y < self->height; y++: - self->data[(self->width + 1)*y + x] = old_content[(old_width + 1)*x + y] - - free(old_content) - - # array is terminated by [-1, -1] - def get_hashtag_coords(self) -> int[2]*: - result: int[2]* = malloc(sizeof(result[0]) * (self->width * self->height + 1)) - result_len = 0 - for y = 0; y < self->height; y++: - for x = 0; x < self->width; x++: - if self->data[(self->width + 1)*y + x] == '#': - result[result_len++] = [x, y] - - result[result_len] = [-1, -1] - return result + y = 0 + while y < grid->height: + if strspn(&grid->data[(grid->width + 1)*y], ".") == grid->width: + # duplicate row + blank_start = &grid->data[(grid->width + 1)*y] + next_start = &grid->data[(grid->width + 1)*(y+1)] + memmove(next_start, blank_start, strlen(blank_start) + 1) + y += 2 + grid->height++ + else: + y++ def manhattan_distance(a: int[2], b: int[2]) -> int: @@ -57,23 +27,17 @@ def manhattan_distance(a: int[2], b: int[2]) -> int: def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - input.duplicate_blank_lines() - input.transpose() - input.duplicate_blank_lines() - input.transpose() + duplicate_blank_lines(&grid) + grid.transpose() + duplicate_blank_lines(&grid) + grid.transpose() - coords = input.get_hashtag_coords() + coords = grid.find_all('#') sum = 0 for i = 0; coords[i][0] != -1; i++: @@ -81,6 +45,6 @@ def main() -> int: sum += manhattan_distance(coords[i], coords[k]) printf("%d\n", sum) # Output: 374 - free(input.data) + free(grid.data) free(coords) return 0 diff --git a/examples/aoc2023/day11/part2.jou b/examples/aoc2023/day11/part2.jou index b05ace4b..f33373db 100644 --- a/examples/aoc2023/day11/part2.jou +++ b/examples/aoc2023/day11/part2.jou @@ -1,59 +1,29 @@ import "stdlib/io.jou" import "stdlib/mem.jou" import "stdlib/str.jou" +import "../grid.jou" -class Input: - width: int - height: int - data: byte* +# returned array is terminated by -1 +def find_blank_lines(grid: Grid*) -> int*: + result: int* = malloc(sizeof(result[0]) * (grid->height + 1)) + result_len = 0 - # returned array is terminated by -1 - def find_blank_lines(self) -> int*: - result: int* = malloc(sizeof(result[0]) * (self->height + 1)) - result_len = 0 - - for y = 0; y < self->height; y++: - if strspn(&self->data[(self->width + 1)*y], ".") == self->width: - result[result_len++] = y - result[result_len] = -1 - return result - - def transpose(self) -> None: - old_content = strdup(self->data) - old_width = self->width - old_height = self->height - self->width = old_height - self->height = old_width - - memset(self->data, '\n', strlen(self->data)) - for x = 0; x < self->width; x++: - for y = 0; y < self->height; y++: - self->data[(self->width + 1)*y + x] = old_content[(old_width + 1)*x + y] - - free(old_content) - - # returned array is terminated by -1 - def get_hashtag_y_coords(self) -> int*: - result: int* = malloc(sizeof(result[0]) * (self->width * self->height + 1)) - result_len = 0 - for y = 0; y < self->height; y++: - for x = 0; x < self->width; x++: - if self->data[(self->width + 1)*y + x] == '#': - result[result_len++] = y - - result[result_len] = -1 - return result + for y = 0; y < grid->height; y++: + if strspn(&grid->data[(grid->width + 1)*y], ".") == grid->width: + result[result_len++] = y + result[result_len] = -1 + return result -def sum_vertical_distances(input: Input*) -> long: - hashtag_y_coords = input->get_hashtag_y_coords() - blank_lines = input->find_blank_lines() +def sum_vertical_distances(grid: Grid*) -> long: + hashtags = grid->find_all('#') + blank_lines = find_blank_lines(grid) result = 0L - for end = hashtag_y_coords; *end != -1; end++: - for start = hashtag_y_coords; start < end; start++: - for y = *start; y < *end; y++: + for end = hashtags; (*end)[0] != -1 and (*end)[1] != -1; end++: + for start = hashtags; start < end; start++: + for y = (*start)[1]; y < (*end)[1]; y++: y_is_blank = False for p = blank_lines; *p != -1; p++: if *p == y: @@ -66,27 +36,21 @@ def sum_vertical_distances(input: Input*) -> long: else: result += 1 - free(hashtag_y_coords) + free(hashtags) free(blank_lines) return result def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - result = sum_vertical_distances(&input) - input.transpose() - result += sum_vertical_distances(&input) + result = sum_vertical_distances(&grid) + grid.transpose() + result += sum_vertical_distances(&grid) printf("%lld\n", result) # Output: 8410 - free(input.data) + free(grid.data) return 0 diff --git a/examples/aoc2023/day13/part1.jou b/examples/aoc2023/day13/part1.jou index 1b495f2a..86783354 100644 --- a/examples/aoc2023/day13/part1.jou +++ b/examples/aoc2023/day13/part1.jou @@ -1,24 +1,6 @@ import "stdlib/io.jou" -import "stdlib/str.jou" import "stdlib/mem.jou" -import "stdlib/ascii.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] +import "../grid.jou" def find_h_reflection(g: Grid*) -> int: @@ -38,58 +20,19 @@ def find_h_reflection(g: Grid*) -> int: return -1 -def find_v_reflection(g: Grid*) -> int: - for reflect = 1; reflect < g->height; reflect++: - top = reflect-1 - bottom = reflect - matches = True - while top >= 0 and bottom < g->height and matches: - for x = 0; x < g->width; x++: - if g->get([x, top]) != g->get([x, bottom]): - matches = False - break - top-- - bottom++ - if matches: - return reflect - return -1 - - def main() -> int: - f = fopen("sampleinput.txt", "r") - assert f != NULL - - line: byte[200] - max_size = 10000 - - grid = Grid{data = malloc(max_size)} result = 0 - while True: - grid.width = 0 - grid.height = 0 - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if line[0] == '\0': - break - - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - - if grid.height == 0: - # end of input - break + f = fopen("sampleinput.txt", "r") + assert f != NULL + while feof(f) == 0: + grid = read_grid_from_file(f) h = find_h_reflection(&grid) - v = find_v_reflection(&grid) + grid.transpose() + v = find_h_reflection(&grid) + free(grid.data) + assert h == -1 or v == -1 # does not have both if h != -1: result += h @@ -98,7 +41,6 @@ def main() -> int: else: assert False - free(grid.data) fclose(f) printf("%d\n", result) # Output: 405 return 0 diff --git a/examples/aoc2023/day13/part2.jou b/examples/aoc2023/day13/part2.jou index 6ec1f8dc..baa17ad6 100644 --- a/examples/aoc2023/day13/part2.jou +++ b/examples/aoc2023/day13/part2.jou @@ -1,24 +1,6 @@ import "stdlib/io.jou" -import "stdlib/str.jou" import "stdlib/mem.jou" -import "stdlib/ascii.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] +import "../grid.jou" def find_h_reflection(g: Grid*, mismatch_count: int) -> int: @@ -40,60 +22,19 @@ def find_h_reflection(g: Grid*, mismatch_count: int) -> int: return -1 -def find_v_reflection(g: Grid*, mismatch_count: int) -> int: - for reflect = 1; reflect < g->height; reflect++: - top = reflect-1 - bottom = reflect - n = 0 - - while top >= 0 and bottom < g->height: - for x = 0; x < g->width; x++: - if g->get([x, top]) != g->get([x, bottom]): - n++ - top-- - bottom++ - - if n == mismatch_count: - return reflect - - return -1 - - def main() -> int: - f = fopen("sampleinput.txt", "r") - assert f != NULL - - line: byte[200] - max_size = 10000 - - grid = Grid{data = malloc(max_size)} result = 0 - while True: - grid.width = 0 - grid.height = 0 - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if line[0] == '\0': - break - - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - - if grid.height == 0: - # end of input - break + f = fopen("sampleinput.txt", "r") + assert f != NULL + while feof(f) == 0: + grid = read_grid_from_file(f) h = find_h_reflection(&grid, 1) - v = find_v_reflection(&grid, 1) + grid.transpose() + v = find_h_reflection(&grid, 1) + free(grid.data) + assert h == -1 or v == -1 # does not have both if h != -1: result += h @@ -102,7 +43,6 @@ def main() -> int: else: assert False - free(grid.data) fclose(f) printf("%d\n", result) # Output: 400 return 0 diff --git a/examples/aoc2023/day14/part1.jou b/examples/aoc2023/day14/part1.jou index 7373bcdd..6b3c095c 100644 --- a/examples/aoc2023/day14/part1.jou +++ b/examples/aoc2023/day14/part1.jou @@ -1,30 +1,6 @@ import "stdlib/io.jou" import "stdlib/mem.jou" -import "stdlib/str.jou" -import "stdlib/ascii.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - def set(self, point: int[2], value: byte) -> None: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - self->data[(self->width + 1)*y + x] = value +import "../grid.jou" def roll_north(grid: Grid*) -> None: @@ -54,28 +30,10 @@ def calculate_load(g: Grid*) -> int: def main() -> int: f = fopen("sampleinput.txt", "r") assert f != NULL - - line: byte[200] - max_size = 100000 - - grid = Grid{data = malloc(max_size)} - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - + grid = read_grid_from_file(f) fclose(f) roll_north(&grid) printf("%d\n", calculate_load(&grid)) # Output: 136 - free(grid.data) return 0 diff --git a/examples/aoc2023/day14/part2.jou b/examples/aoc2023/day14/part2.jou index 70ef0a39..e229d672 100644 --- a/examples/aoc2023/day14/part2.jou +++ b/examples/aoc2023/day14/part2.jou @@ -1,39 +1,8 @@ import "stdlib/io.jou" import "stdlib/mem.jou" import "stdlib/str.jou" -import "stdlib/ascii.jou" import "stdlib/math.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - def set(self, point: int[2], value: byte) -> None: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - self->data[(self->width + 1)*y + x] = value - - def swap(self, a: int[2], b: int[2]) -> None: - old_a = self->get(a) - self->set(a, self->get(b)) - self->set(b, old_a) - - def copy(self) -> Grid: - return Grid{width = self->width, height = self->height, data = strdup(self->data)} +import "../grid.jou" def can_roll(grid: Grid*, rock: int[2], dir: int[2], n: int) -> bool: @@ -92,24 +61,7 @@ def calculate_load(g: Grid*) -> int: def main() -> int: f = fopen("sampleinput.txt", "r") assert f != NULL - - line: byte[200] - max_size = 100000 - - grid = Grid{data = malloc(max_size)} - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - + grid = read_grid_from_file(f) fclose(f) previous_states: Grid* = NULL diff --git a/examples/aoc2023/grid.jou b/examples/aoc2023/grid.jou new file mode 100644 index 00000000..c402c2b7 --- /dev/null +++ b/examples/aoc2023/grid.jou @@ -0,0 +1,99 @@ +# This file contains a utility class for AoC solutions. +# It is not in the standard library because it feels too AoC-specific to me. + +import "stdlib/ascii.jou" +import "stdlib/str.jou" +import "stdlib/mem.jou" +import "stdlib/io.jou" + + +class Grid: + width: int + height: int + data: byte* + + def is_in_bounds(self, point: int[2]) -> bool: + x = point[0] + y = point[1] + return 0 <= x and x < self->width and 0 <= y and y < self->height + + def get(self, point: int[2]) -> byte: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + return self->data[(self->width + 1)*y + x] + + def set(self, point: int[2], value: byte) -> None: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + self->data[(self->width + 1)*y + x] = value + + def swap(self, a: int[2], b: int[2]) -> None: + old_a = self->get(a) + self->set(a, self->get(b)) + self->set(b, old_a) + + def copy(self) -> Grid: + return Grid{width = self->width, height = self->height, data = strdup(self->data)} + + def transpose(self) -> None: + old = self->copy() + memset(self->data, '\n', strlen(self->data)) + self->width = old.height + self->height = old.width + + for x = 0; x < self->width; x++: + for y = 0; y < self->height; y++: + self->set([x, y], old.get([y, x])) + free(old.data) + + # returned array is terminated by [-1, -1] + def find_all(self, b: byte) -> int[2]*: + result: int[2]* = malloc(sizeof(result[0]) * (self->width * self->height + 1)) + result_len = 0 + + for y = 0; y < self->height; y++: + for x = 0; x < self->width; x++: + if self->get([x, y]) == b: + result[result_len++] = [x, y] + + result = realloc(result, sizeof(result[0]) * (result_len + 1)) + result[result_len] = [-1, -1] + return result + + def find_first(self, b: byte) -> int[2]: + for y = 0; y < self->height; y++: + for x = 0; x < self->width; x++: + if self->get([x, y]) == b: + return [x, y] + assert False + + +# Reading stops on end of file or newline, so you can call this repeatedly +# to read multiple blank-line separated grids. +def read_grid_from_file(f: FILE*) -> Grid: + line: byte[5000] + max_size = 10000000 # 10 MB + + result = Grid{data = malloc(max_size)} + result.data[0] = '\0' + + while fgets(line, sizeof(line) as int, f) != NULL: + trim_ascii_whitespace(line) + if line[0] == '\0': + break + + if result.height == 0: # set width on first round + result.width = strlen(line) as int + assert result.width == strlen(line) + result.height++ + + assert result.width * result.height < max_size + strcat(result.data, line) + strcat(result.data, "\n") + + assert result.width != 0 and result.height != 0 + assert strlen(result.data) == (result.width + 1)*result.height + result.data = realloc(result.data, strlen(result.data) + 1) + return result