Skip to content

Commit

Permalink
working on part 2, slow but seems ok
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli committed Jan 4, 2025
1 parent e43e868 commit aa5073f
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 12 deletions.
12 changes: 0 additions & 12 deletions examples/aoc2024/day23/part1.jou
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import "stdlib/io.jou"

class Computer:
name: byte[3]
group_id: int # computers connected together have same group id
connections: void*[100] # TODO: should be Computer*[100], can't be due to compiler bug

def connect(self, other: Computer*) -> None:
Expand Down Expand Up @@ -34,23 +33,12 @@ def find_or_create_computer(computers: Computer*, ncomputers: int*, name: byte*)
c = &computers[*ncomputers]
assert strlen(name) == 2
strcpy(c->name, name)
c->group_id = *ncomputers # unique initially
memset(c->connections, 0, sizeof(c->connections))

++*ncomputers
return c


def merge_groups(computers: Computer*, ncomputers: int, id1: int, id2: int) -> None:
printf("Merge %d,%d\n", id1, id2)
if id1 == id2:
return

for i = 0; i < ncomputers; i++:
if computers[i].group_id == id1:
computers[i].group_id = id2


def main() -> int:
computers: Computer* = malloc(sizeof(computers[0]) * 5000)
assert computers != NULL
Expand Down
252 changes: 252 additions & 0 deletions examples/aoc2024/day23/part2.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import "stdlib/str.jou"
import "stdlib/mem.jou"
import "stdlib/io.jou"


def add_computer_if_not_exist(names: byte[3]*, ncomputers: int*, name: byte*) -> None:
for i = 0; i < *ncomputers; i++:
if strcmp(names[i], name) == 0:
return

assert strlen(name) == 2
strcpy(names[(*ncomputers)++], name)


def find_computer(names: byte[3]*, ncomputers: int, name: byte*) -> int:
for i = 0; i < ncomputers; i++:
if strcmp(names[i], name) == 0:
return i
assert False


# If conn_matrix[i][j] is True, then computers i and j are connected
def create_conn_matrix(ncomputers: int) -> bool**:
conn_matrix: bool** = malloc(sizeof(conn_matrix[0]) * ncomputers)
assert conn_matrix != NULL

for i = 0; i < ncomputers; i++:
conn_matrix[i] = calloc(sizeof(conn_matrix[i][0]), ncomputers)
assert conn_matrix[i] != NULL

return conn_matrix


def sort_names(names: byte[3]*, len: int) -> None:
# very dumb algorithm
while True:
did_something = False
for i = 1; i < len; i++:
if strcmp(names[i-1], names[i]) > 0:
memswap(&names[i-1], &names[i], sizeof(names[0]))
did_something = True
if not did_something:
break


def sort_group(group: int*) -> None:
# very dumb algorithm
while True:
did_something = False
for i = 1; group[i] != -1; i++:
if group[i-1] > group[i]:
memswap(&group[i-1], &group[i], sizeof(group[0]))
did_something = True
if not did_something:
break


def group_contains(group: int*, value: int) -> bool:
for i = 0; group[i] != -1; i++:
if group[i] == value:
return True
return False


def groups_are_equal(g1: int*, g2: int*) -> bool:
for i = 0; g1[i] != -1 or g2[i] != -1; i++:
if g1[i] != g2[i]:
return False
return True


# Merges two groups of size n if:
# - they have n-1 same elements in common
# - the two non-common elements (one from each group) are connected
#
# Assumes groups are sorted.
def merge_two_groups(group1: int*, group2: int*, conn_matrix: bool**) -> int*:
n = 0
while group1[n] != -1 and group2[n] != -1:
n++
assert group1[n] == -1
assert group2[n] == -1

for i = 1; i < n; i++:
assert group1[i-1] < group1[i]
assert group2[i-1] < group2[i]

# Find the elements that is only in group 1 or 2. If there are multiple, don't merge.
p1 = group1
p2 = group2
only1 = -1
only2 = -1

while *p1 != -1 or *p2 != -1:
if *p2 == -1 or (*p1 != -1 and *p2 != -1 and *p1 < *p2):
# Group 1 left behind, it has element which is not in group 2
if only1 != -1:
# Not the first one, refuse to merge
return NULL
only1 = *p1++
elif *p1 == -1 or (*p1 != -1 and *p2 != -1 and *p1 > *p2):
# Group 2 element missing from group 1
if only2 != -1:
# Not the first one, refuse to merge
return NULL
only2 = *p2++
else:
# Skip common element
assert *p1 != -1
assert *p2 != -1
assert *p1 == *p2
p1++
p2++

assert p1 == &group1[n]
assert p2 == &group2[n]

# They shouldn't have everything in common, then they would be the same group
assert only1 != -1
assert only2 != -1

if not conn_matrix[only1][only2]:
return NULL

result: int* = malloc(sizeof(result[0]) * (n+2))
assert result != NULL
memcpy(result, group1, sizeof(result[0]) * n)
result[n] = only2
result[n+1] = -1
return result


# verbose parameter is useful when you're bored and you run this on the real input.
# It is very slow... :)
def merge_all_mergeable_pairs(conn_matrix: bool**, old_groups: int**) -> int**:
n = 0
while old_groups[n] != NULL:
n++
printf("Merging %d groups together", n)
fflush(stdout)

result: int** = malloc(sizeof(result[0]))
assert result != NULL
result_len = 0
result_alloc = 1

for g1 = old_groups; *g1 != NULL; g1++:
for g2 = &g1[1]; *g2 != NULL; g2++:
m = merge_two_groups(*g1, *g2, conn_matrix)
if m == NULL:
continue

sort_group(m)

already_exists = False
for i = 0; i < result_len; i++:
if groups_are_equal(result[i], m):
already_exists = True
break

if already_exists:
free(m)
else:
if result_len == result_alloc:
result_alloc *= 2
result = realloc(result, sizeof(result[0]) * (result_alloc + 1))
assert result != NULL

result[result_len++] = m

# Print a dot every 100 successful merges.
# Helpful with actual input because it is really slow :)
if result_len % 100 == 0:
printf(".")
fflush(stdout)

printf("\n")
fflush(stdout)

result[result_len] = NULL
return result


def main() -> int:
names: byte[3]* = malloc(sizeof(names[0]) * 5000)
assert names != NULL
ncomputers = 0

f = fopen("input", "r")
assert f != NULL

name1: byte[3]
name2: byte[3]
while fscanf(f, "%2s-%2s\n", name1, name2) == 2:
assert ncomputers < 5000
add_computer_if_not_exist(names, &ncomputers, name1)
assert ncomputers < 5000
add_computer_if_not_exist(names, &ncomputers, name2)

sort_names(names, ncomputers)

rewind(f)
conn_matrix = create_conn_matrix(ncomputers)
while fscanf(f, "%2s-%2s\n", name1, name2) == 2:
i1 = find_computer(names, ncomputers, name1)
i2 = find_computer(names, ncomputers, name2)
conn_matrix[i1][i2] = True
conn_matrix[i2][i1] = True

fclose(f)

# Create one group for each computer.
groups: int** = malloc(sizeof(groups[0]) * (ncomputers + 1))
assert groups != NULL
for i = 0; i < ncomputers; i++:
groups[i] = malloc(sizeof(groups[i][0]) * (ncomputers + 1))
groups[i][0] = i
groups[i][1] = -1
groups[ncomputers] = NULL

# Merge until there are at least two groups.
while groups[0] != NULL and groups[1] != NULL:
# Output: Merging 16 groups together
# Output: Merging 32 groups together
# Output: Merging 12 groups together
groups2 = merge_all_mergeable_pairs(conn_matrix, groups)

for i = 0; groups[i] != NULL; i++:
free(groups[i])
free(groups)
groups = groups2

# There should be one group remaining
assert groups[0] != NULL

# Output: co-de-ka-ta
for i = 0; groups[0][i] != -1; i++:
if i != 0:
printf("-")
printf("%s", names[groups[0][i]])
printf("\n")

free(groups[0])
free(groups)

for i = 0; i < ncomputers; i++:
free(conn_matrix[i])
free(conn_matrix)

free(names)

return 0

0 comments on commit aa5073f

Please sign in to comment.