diff --git a/src/day20/solve.c b/src/day20/solve.c index ce70620..1caa50b 100644 --- a/src/day20/solve.c +++ b/src/day20/solve.c @@ -49,7 +49,7 @@ typedef struct { typedef struct { CharSlice99 source, destination; - pulse_e input; + pulse_e pulse; } state_t; #define P @@ -59,6 +59,106 @@ typedef struct { static size_t module_t_hash(module_t *item) { return XXH3_64bits(item->name.ptr, item->name.len); } static int module_t_equal(module_t *lhs, module_t *rhs) { return CharSlice99_primitive_eq(lhs->name, rhs->name); } +typedef struct { + i32 low_count; + i32 high_count; + pulse_e pulse; // pulse value of "from -> to" +} push_result_t; + +static push_result_t push_button(ust_module_t *modules, CharSlice99 from, CharSlice99 to) { + push_result_t result = {.low_count = 0, .high_count = 0}; + + _cleanup_(que_state_t_free) que_state_t queue = que_state_t_init(); + que_state_t_push(&queue, (state_t){.source = CharSlice99_from_str("button"), + .destination = CharSlice99_from_str("broadcaster"), + .pulse = PULSE_LOW}); + while (!que_state_t_empty(&queue)) { + state_t *current = que_state_t_front(&queue); + /* + log_debug("%.*s -%c-> %.*s", current->source.len, current->source.ptr, current->input == PULSE_HIGH ? 'H' : 'L', + current->destination.len, current->destination.ptr); + */ + if (current->pulse == PULSE_HIGH) { + result.high_count++; + } else { + result.low_count++; + } + if (CharSlice99_primitive_eq(from, current->source) && CharSlice99_primitive_eq(to, current->destination)) { + result.pulse = current->pulse; + } + + ust_module_t_node *node = ust_module_t_find(modules, (module_t){.name = current->destination}); + if (node) { + module_t *m = &node->key; + switch (m->kind) { + case BROADCAST: + for (int i = 0; i < m->output_count; i++) { + state_t item = { + .source = current->destination, + .destination = m->output[i], + .pulse = current->pulse, + }; + que_state_t_push(&queue, item); + } + break; + case FLIP_FLOP: + if (current->pulse == PULSE_LOW) { + m->state.on = !m->state.on; + for (int i = 0; i < m->output_count; i++) { + state_t item = { + .source = current->destination, + .destination = m->output[i], + .pulse = m->state.on ? PULSE_HIGH : PULSE_LOW, + }; + que_state_t_push(&queue, item); + } + } + break; + case CONJUNCTION: + // the conjunction module first updates its memory for that input + for (int i = 0; i < m->state.list.len; i++) { + conjunction_item_t *item = &m->state.list.items[i]; + if (CharSlice99_primitive_eq(item->name, current->source)) { + item->pulse = current->pulse; + break; + } + } + // if high pulses for all inputs, it sends a low pulse; otherwise, it sends a high pulse + pulse_e pulse = PULSE_LOW; + for (int i = 0; i < m->state.list.len; i++) { + conjunction_item_t *item = &m->state.list.items[i]; + if (item->pulse == PULSE_LOW) { + pulse = PULSE_HIGH; + break; + } + } + for (int i = 0; i < m->output_count; i++) { + state_t item = { + .source = current->destination, + .destination = m->output[i], + .pulse = pulse, + }; + que_state_t_push(&queue, item); + } + break; + } + } + que_state_t_pop(&queue); + } + return result; +} + +static void reset_modules(ust_module_t *modules) { + foreach (ust_module_t, modules, it) { + module_t *m = &it.node->key; + if (m->kind == FLIP_FLOP) { + m->state.on = false; + } else if (m->kind == CONJUNCTION) { + for (int i = 0; i < m->state.list.len; i++) { m->state.list.items[i].pulse = PULSE_LOW; } + } + } +} + void solve(char *buf, size_t buf_size, Solution *result) { i64 part1 = 0, part2 = 0; @@ -103,11 +203,14 @@ void solve(char *buf, size_t buf_size, Solution *result) { } // initialize conjunction items with their incoming signals to low + CharSlice99 lg = {.len = 0}; foreach (ust_module_t, &modules, it) { module_t *m = &it.node->key; CharSlice99 source = m->name; for (int i = 0; i < m->output_count; i++) { CharSlice99 destination = m->output[i]; + if (CharSlice99_primitive_eq(destination, CharSlice99_from_str("rx"))) { lg = source; } + ust_module_t_node *node = ust_module_t_find(&modules, (module_t){.name = destination}); if (node == NULL) continue; module_t *dest_module = &node->key; @@ -130,83 +233,48 @@ void solve(char *buf, size_t buf_size, Solution *result) { } } - _cleanup_(que_state_t_free) que_state_t queue = que_state_t_init(); - i64 high_count = 0, low_count = 0; + int low_count = 0, high_count = 0; + CharSlice99 empty = CharSlice99_from_str(""); for (int round = 1; round <= 1000; round++) { - que_state_t_push(&queue, (state_t){.source = CharSlice99_from_str("button"), - .destination = CharSlice99_from_str("broadcaster"), - .input = PULSE_LOW}); - while (!que_state_t_empty(&queue)) { - state_t *current = que_state_t_front(&queue); - log_debug("%.*s -%c-> %.*s", current->source.len, current->source.ptr, - current->input == PULSE_HIGH ? 'H' : 'L', current->destination.len, current->destination.ptr); - if (current->input == PULSE_HIGH) { - high_count++; - } else { - low_count++; - } + push_result_t hl = push_button(&modules, empty, empty); + low_count += hl.low_count, high_count += hl.high_count; + } + part1 = high_count * low_count; - ust_module_t_node *node = ust_module_t_find(&modules, (module_t){.name = current->destination}); - if (node) { - module_t *m = &node->key; - switch (m->kind) { - case BROADCAST: - for (int i = 0; i < m->output_count; i++) { - state_t item = { - .source = current->destination, - .destination = m->output[i], - .input = current->input, - }; - que_state_t_push(&queue, item); - } - break; - case FLIP_FLOP: - if (current->input == PULSE_LOW) { - m->state.on = !m->state.on; - for (int i = 0; i < m->output_count; i++) { - state_t item = { - .source = current->destination, - .destination = m->output[i], - .input = m->state.on ? PULSE_HIGH : PULSE_LOW, - }; - que_state_t_push(&queue, item); - } - } - break; - case CONJUNCTION: - // the conjunction module first updates its memory for that input - for (int i = 0; i < m->state.list.len; i++) { - conjunction_item_t *item = &m->state.list.items[i]; - if (CharSlice99_primitive_eq(item->name, current->source)) { - item->pulse = current->input; - break; - } - } - // if high pulses for all inputs, it sends a low pulse; otherwise, it sends a high pulse - pulse_e pulse = PULSE_LOW; - for (int i = 0; i < m->state.list.len; i++) { - conjunction_item_t *item = &m->state.list.items[i]; - if (item->pulse == PULSE_LOW) { - pulse = PULSE_HIGH; - break; - } - } - for (int i = 0; i < m->output_count; i++) { - state_t item = { - .source = current->destination, - .destination = m->output[i], - .input = pulse, - }; - que_state_t_push(&queue, item); - } + /* + * 1. &lg -> rx has to send a low pulse + * 2. So everything (4 modules) going into lq must send a high pulse. + * 3. For each of the 4 modules, check how many button presses it takes to send a high pulse to lq. + * 4. The answer is the lcm of these button presses. + */ + if (lg.len > 0) { + log_debug("rx source: %.*s", lg.len, lg.ptr); + ust_module_t_node *node = ust_module_t_find(&modules, (module_t){.name = lg}); + module_t *lg_module = &node->key; + assert(lg_module->kind == CONJUNCTION); + + CharSlice99 lg_sources[4]; + for (int i = 0; i < lg_module->state.list.len; i++) { + lg_sources[i] = lg_module->state.list.items[i].name; + log_debug("source: %.*s", lg_sources[i].len, lg_sources[i].ptr); + } + + i64 button_presses[4]; + for (int i = 0; i < 4; i++) { + reset_modules(&modules); + i32 count = 0; + while (count > 0) { + push_result_t result = push_button(&modules, lg_sources[i], lg); + count++; + if (result.pulse == PULSE_HIGH) { // FIXME: this never happens + printf("took %d button presses", count); + button_presses[i] = count; break; } } - que_state_t_pop(&queue); + assert(count > 0); } - log_debug("round %d: low_count: %ld, high_count: %ld", round, low_count, high_count); } - part1 += high_count * low_count; snprintf(result->part1, sizeof(result->part1), "%ld", part1); snprintf(result->part2, sizeof(result->part2), "%ld", part2);