-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
732 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
???.### 1,1,3 | ||
.??..??...?##. 1,1,3 | ||
?#?#?#?#?#?#?#? 1,3,1,6 | ||
????.#...#... 4,1,1 | ||
????.######..#####. 1,6,5 | ||
?###???????? 3,2,1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.