diff --git a/README.md b/README.md index e5bd875..0772c61 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Compiled using clang 16 and LTO. | 12 | 347 ms | 554 ms | | 13 | 248 µs | 486 µs | | 14 | | 53.4 ms | +| 15 | | 1 ms | ## 🙏 Acknowledgments and Resources diff --git a/puzzle/day15.md b/puzzle/day15.md index cbb8737..9a3dec1 100644 --- a/puzzle/day15.md +++ b/puzzle/day15.md @@ -78,8 +78,6 @@ Run the HASH algorithm on each step in the initialization sequence. What is the Your puzzle answer was 515495. -The first half of this puzzle is complete! It provides one gold star: * - --- Part Two --- You convince the reindeer to bring you the page; the page confirms that your HASH algorithm is working. @@ -181,9 +179,13 @@ So, the above example ends up with a total focusing power of 145. With the help of an over-enthusiastic reindeer in a hard hat, follow the initialization sequence. What is the focusing power of the resulting lens configuration? -Answer: +Your puzzle answer was 229349. + +Both parts of this puzzle are complete! They provide two gold stars: ** + +At this point, you should return to your Advent calendar and try another puzzle. -Although it hasn't changed, you can still get your puzzle input. +If you still want to see it, you can get your puzzle input. You can also [Shareon Twitter Mastodon] this puzzle. diff --git a/src/day15/solve.c b/src/day15/solve.c index f8960eb..2c091d8 100644 --- a/src/day15/solve.c +++ b/src/day15/solve.c @@ -11,34 +11,111 @@ #include +#define MAX_LABEL_LEN 8 +#define MAX_LENSES_PER_BOX 512 +#define BOX_COUNT 256 + +typedef struct { + CharSlice99 label; + int focal; +} lens_t; + +typedef struct { + lens_t item[MAX_LENSES_PER_BOX]; + int item_count; +} box_t; + +typedef struct { + box_t box[BOX_COUNT]; +} hashmap_t; + static inline u32 hash(CharSlice99 s) { u32 value = 0; - for (size_t i = 0; i < s.len; i++) { - char c = *CharSlice99_get(s, i); - value += c; - value *= 17; - value %= 256; - } - log_debug("hash of %.*s is %u", s.len, s.ptr, value); + for (size_t i = 0; i < s.len; i++) { value = ((value + *CharSlice99_get(s, i)) * 17) % BOX_COUNT; } return value; } +static inline void hashmap_upsert(hashmap_t *self, CharSlice99 label, int focal) { + u32 box_id = hash(label); + box_t *box = &self->box[box_id]; + // check if we have to update an existing value + for (int i = 0; i < box->item_count; i++) { + if (CharSlice99_primitive_eq(box->item[i].label, label)) { + log_debug(">> updating box %d: label %.*s has new value %d", box_id, label.len, label.ptr, focal); + box->item[i].focal = focal; + return; + } + } + // insert new value + log_debug(">> appending to box %d: label %.*s with value %d", box_id, label.len, label.ptr, focal); + box->item_count++; + lens_t lens = {.label = label, .focal = focal}; + box->item[box->item_count - 1] = lens; +} + +static inline void hashmap_remove(hashmap_t *self, CharSlice99 label) { + u32 box_id = hash(label); + box_t *box = &self->box[box_id]; + if (box->item_count == 0) return; + // find index of label in box + int idx = 0; + while (idx < box->item_count) { + if (CharSlice99_primitive_eq(box->item[idx].label, label)) break; + idx++; + } + if (idx == box->item_count) { + log_debug(">> box %d: label %.*s not found in hashmap, nothing to do", box_id, label.len, label.ptr); + return; + } + log_debug(">> box %d: found label %.*s at position %idx, removing it", box_id, label.len, label.ptr); + // shift neighbors + for (int i = idx + 1; i < box->item_count; i++) { box->item[i - 1] = box->item[i]; } + box->item_count--; +} + +static inline void apply_instruction(hashmap_t *hm, CharSlice99 instruction) { + if (*CharSlice99_last(instruction) == '-') { + hashmap_remove(hm, CharSlice99_sub(instruction, 0, instruction.len - 1)); + } else { + int i = 0; + while (*CharSlice99_get(instruction, i) != '=') i++; + CharSlice99 label = CharSlice99_sub(instruction, 0, i); + size_t pos = i + 1; + i64 focal = aoc_parse_nonnegative(instruction.ptr, &pos); + hashmap_upsert(hm, label, focal); + } +} + +static inline u64 compute_focusing_power(hashmap_t *hm) { + u64 result = 0; + for (int box_number = 0; box_number < BOX_COUNT; box_number++) { + box_t *box = &hm->box[box_number]; + for (int j = 0; j < box->item_count; j++) { result += (1 + box_number) * (1 + j) * box->item[j].focal; } + } + return result; +} + void solve(char *buf, size_t buf_size, Solution *result) { u64 part1 = 0, part2 = 0; CharSlice99 input = CharSlice99_from_ptrdiff(buf, &buf[buf_size - 2]); // no '\0', no '\n' + hashmap_t hm = {0}; + size_t start = 0; size_t pos = 0; while (pos < input.len) { if (*CharSlice99_get(input, pos) == ',') { CharSlice99 s = CharSlice99_sub(input, start, pos); part1 += hash(s); + apply_instruction(&hm, s); start = pos + 1; } pos++; } CharSlice99 s = CharSlice99_sub(input, start, pos + 1); part1 += hash(s); + apply_instruction(&hm, s); + part2 = compute_focusing_power(&hm); snprintf(result->part1, sizeof(result->part1), "%lu", part1); snprintf(result->part2, sizeof(result->part2), "%lu", part2); diff --git a/src/day15/solve_test.c b/src/day15/solve_test.c index 72983e3..44c4bce 100644 --- a/src/day15/solve_test.c +++ b/src/day15/solve_test.c @@ -16,7 +16,7 @@ CTEST(day15, example) { Solution solution; solve(buf, strlen(buf), &solution); ASSERT_STR("1320", solution.part1); - // ASSERT_STR("0", solution.part2); + ASSERT_STR("145", solution.part2); } #ifdef HAVE_INPUTS @@ -24,7 +24,7 @@ CTEST(day15, real) { Solution solution; solve_input("input/" DAY ".txt", &solution); ASSERT_STR("515495", solution.part1); - // ASSERT_STR("0", solution.part2); + ASSERT_STR("229349", solution.part2); } #endif