Skip to content

Commit

Permalink
much better approach
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli committed Jan 5, 2025
1 parent 08501f7 commit c15fb2c
Showing 1 changed file with 48 additions and 158 deletions.
206 changes: 48 additions & 158 deletions examples/aoc2024/day23/part2.jou
Original file line number Diff line number Diff line change
Expand Up @@ -43,137 +43,38 @@ def sort_names(names: byte[3]*, len: int) -> None:
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 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 1000 successful merges.
# Helpful with actual input because it is really slow :)
if result_len % 1000 == 0:
printf(".")
fflush(stdout)

printf("\n")
fflush(stdout)

result = realloc(result, sizeof(result[0]) * (result_len + 1))
assert result != NULL
result[result_len] = NULL
return result
# recursive (depth-first search)
def add_more_members_to_group(
group: int*,
group_len: int,
biggest_group: int*,
biggest_group_len: int*,
ncomputers: int,
conn_matrix: bool**,
) -> None:
if group_len > *biggest_group_len:
*biggest_group_len = group_len
memcpy(biggest_group, group, sizeof(group[0]) * group_len)

# Always add members in order, speeds up search a lot
for i = group[group_len - 1] + 1; i < ncomputers; i++:
# Do not add same group member twice.
# Do not add members that don't connect with all existing members.
ok = True
for k = 0; k < group_len; k++:
if not conn_matrix[i][group[k]]:
ok = False
break

if ok:
# Temporarily add new member to the group and recurse with it
group[group_len] = i
add_more_members_to_group(
group,
group_len + 1,
biggest_group,
biggest_group_len,
ncomputers, conn_matrix)


def main() -> int:
Expand Down Expand Up @@ -204,40 +105,29 @@ def main() -> int:

fclose(f)

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

for i = 0; i < ncomputers; i++:
groups[i] = malloc(sizeof(groups[i][0]) * 2)
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
group[0] = i
add_more_members_to_group(
group,
1,
biggest_group,
&biggest_group_len,
ncomputers,
conn_matrix,
)

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

free(groups[0])
free(groups)

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

0 comments on commit c15fb2c

Please sign in to comment.