Skip to content

Commit

Permalink
Aoc days 12, 13, 14 (#451)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli authored Dec 14, 2023
1 parent 866ed66 commit 118f1b1
Show file tree
Hide file tree
Showing 9 changed files with 732 additions and 0 deletions.
95 changes: 95 additions & 0 deletions examples/aoc2023/day12/part1.jou
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
166 changes: 166 additions & 0 deletions examples/aoc2023/day12/part2.jou
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
6 changes: 6 additions & 0 deletions examples/aoc2023/day12/sampleinput.txt
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
104 changes: 104 additions & 0 deletions examples/aoc2023/day13/part1.jou
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
Loading

0 comments on commit 118f1b1

Please sign in to comment.