Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aoc days 12, 13, 14 #451

Merged
merged 5 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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