Skip to content

Commit

Permalink
day 19: constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeladler committed Dec 27, 2023
1 parent 301ea6b commit 69c29ba
Showing 1 changed file with 96 additions and 28 deletions.
124 changes: 96 additions & 28 deletions src/day19/solve.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ typedef struct {
} graph_t;

typedef struct {
int lower; // inclusive
int upper; // inclusive
int a; // inclusive
int b; // inclusive
} interval_t;

typedef struct {
Expand Down Expand Up @@ -97,42 +97,117 @@ static inline CharSlice99 *workflow_next(workflow_t *wf, data_t data) {

static inline void interval_apply_rule(interval_t *interval, rule_t *rule) {
if (rule->kind == LT) {
interval->upper = MIN(rule->value - 1, interval->upper); // interval.upper < rule->value
interval->b = MIN(rule->value - 1, interval->b); // interval.upper < rule->value
} else if (rule->kind == GT) {
interval->lower = MAX(rule->value + 1, interval->lower); // interval.lower > rule->value
interval->a = MAX(rule->value + 1, interval->a); // interval.lower > rule->value
}
}

static inline void interval_negate_rule(interval_t *interval, rule_t *rule) {
if (rule->kind == LT) {
interval->upper = MAX(rule->value, interval->upper); // interval.upper >= rule->value
interval->a = MAX(rule->value, interval->a); // interval.lower >= rule->value
} else if (rule->kind == GT) {
interval->lower = MIN(rule->value, interval->lower); // interval.lower <= rule->value
interval->b = MIN(rule->value, interval->b); // interval.upper <= rule->value
}
}

static inline constraint_t constraint_apply_rule(constraint_t constraint, rule_t *r) {
static inline char kind_to_char(rule_kind_e kind) {
switch (kind) {
case LT: return '<';
case GT: return '>';
case JUMP: return 'J';
}
}

static inline i64 interval_cardinality(interval_t interval) {
if (interval.b < interval.a) return 0;
return interval.b - interval.a + 1;
}

static inline i64 constraint_cardinality(constraint_t c) {
return interval_cardinality(c.x) * interval_cardinality(c.m) * interval_cardinality(c.a) *
interval_cardinality(c.s);
}

static inline constraint_t constraint_apply_rule(constraint_t constraint, rule_t *rule) {
if (rule->kind != JUMP) {
log_debug("applying rule: %c %c %d to jump to %.*s", rule->variable, kind_to_char(rule->kind), rule->value,
rule->destination.len, rule->destination.ptr);
}
constraint_t result = constraint;
switch (r->variable) {
case 'x': interval_apply_rule(&result.x, r); break;
case 'm': interval_apply_rule(&result.m, r); break;
case 'a': interval_apply_rule(&result.a, r); break;
case 's': interval_apply_rule(&result.s, r); break;
switch (rule->variable) {
case 'x': interval_apply_rule(&result.x, rule); break;
case 'm': interval_apply_rule(&result.m, rule); break;
case 'a': interval_apply_rule(&result.a, rule); break;
case 's': interval_apply_rule(&result.s, rule); break;
}
log_debug("constraints: %d<=x<=%d, %d<=m<=%d, %d<=a<=%d, %d<=s<=%d", result.x.a, result.x.b, result.m.a, result.m.b,
result.a.a, result.a.b, result.s.a, result.s.b);
return result;
}

static inline constraint_t constraint_negate_rule(constraint_t constraint, rule_t *r) {
static inline constraint_t constraint_negate_rule(constraint_t constraint, rule_t *rule) {
if (rule->kind != JUMP) {
log_debug("negating rule: %c %c %d to jump to %.*s", rule->variable, kind_to_char(rule->kind), rule->value,
rule->destination.len, rule->destination.ptr);
}
constraint_t result = constraint;
switch (r->variable) {
case 'x': interval_negate_rule(&result.x, r); break;
case 'm': interval_negate_rule(&result.m, r); break;
case 'a': interval_negate_rule(&result.a, r); break;
case 's': interval_negate_rule(&result.s, r); break;
switch (rule->variable) {
case 'x': interval_negate_rule(&result.x, rule); break;
case 'm': interval_negate_rule(&result.m, rule); break;
case 'a': interval_negate_rule(&result.a, rule); break;
case 's': interval_negate_rule(&result.s, rule); break;
}
log_debug("constraints: %d <= x <= %d, %d <= m <= %d, %d <= a <= %d, %d <= s <= %d", result.x.a, result.x.b,
result.m.a, result.m.b, result.a.a, result.a.b, result.s.a, result.s.b);
return result;
}

static inline void process_path(int path[], int path_len, ust_workflow_t *workflows, CharSlice99 *id_to_name,
constraint_t initial) {
log_debug(">> found new path to destination:");
CharSlice99 from = id_to_name[path[0]];
constraint_t constraints[128];
constraints[0] = initial;
int constraints_count = 1;
for (int i = 1; i < path_len; i++) {
workflow_t *wf = &ust_workflow_t_find(workflows, (workflow_t){.name = from})->key;
CharSlice99 to = id_to_name[path[i]];
log_debug("looking for rules %.*s -> %.*s", from.len, from.ptr, to.len, to.ptr);
int old_count = constraints_count;
for (int j = 0; j < old_count; j++) {
constraint_t c = constraints[j];
bool is_first_match = true;
for (int k = 0; k < wf->rule_count; k++) {
rule_t *rule = &wf->rule[k];
if (CharSlice99_primitive_eq(rule->destination, to)) {
constraint_t new_c = constraint_apply_rule(c, rule);
if (is_first_match) {
constraints[j] = new_c;
is_first_match = false;
} else { // append
log_debug("constraints_count:%d", constraints_count);
constraints[constraints_count++] = new_c;
}
} else {
c = constraint_negate_rule(c, rule);
}
}
}
from = to;
}

i64 total = 0;
log_debug(">> final result:");
for (int i = 0; i < constraints_count; i++) {
constraint_t c = constraints[i];
total += constraint_cardinality(c);
log_debug("- %d <= x <= %d, %d <= m <= %d, %d <= a <= %d, %d <= s <= %d", c.x.a, c.x.b, c.m.a, c.m.b, c.a.a,
c.a.b, c.s.a, c.s.b);
}
log_debug(">> total: %ld", total);
}

static void find_all_paths(graph_t *graph, int current, int destination, bool visited[], int path[], int *path_idx,
ust_workflow_t *workflows, CharSlice99 *id_to_name) {
// mark the current node and store it in path[]
Expand All @@ -141,12 +216,9 @@ static void find_all_paths(graph_t *graph, int current, int destination, bool vi
*path_idx = *path_idx + 1;

if (current == destination) {
log_debug(">> found new path to destination:");
for (int i = 0; i < *path_idx; i++) {
int id = path[i];
CharSlice99 name = id_to_name[id];
log_debug("%.*s", name.len, name.ptr);
}
interval_t initial = {.a = 0, .b = 4000};
constraint_t constraint = {.x = initial, .m = initial, .a = initial, .s = initial};
process_path(path, *path_idx, workflows, id_to_name, constraint);
} else {
adj_list_t *lst = &graph->adj[current];
for (int i = 0; i < lst->neighbor_count; i++) {
Expand Down Expand Up @@ -183,7 +255,6 @@ void solve(char *buf, size_t buf_size, Solution *result) {
wf.name = CharSlice99_new(&buf[start], pos - start);

ust_name_id_t_insert(&name_to_id, (name_id_t){.id = id_count, .name = wf.name});
log_debug("id_count: %d", id_count);
id_to_name[id_count++] = wf.name;

pos++;
Expand Down Expand Up @@ -248,12 +319,9 @@ void solve(char *buf, size_t buf_size, Solution *result) {
if (buf[pos] == '}') break;
pos++;
}
log_debug(">> input: x=%d, m=%d, a=%d, s=%d", data.x, data.m, data.a, data.s);

workflow_t *current = &start_node->key;
while (1) {
CharSlice99 next = *workflow_next(current, data);
log_debug("%.*s -> %.*s", current->name.len, current->name.ptr, next.len, next.ptr);
if (CharSlice99_primitive_eq(next, accepted)) {
part1 += data.x + data.a + data.m + data.s;
break;
Expand Down

0 comments on commit 69c29ba

Please sign in to comment.