diff --git a/day21/c/day21 b/day21/c/day21 new file mode 100755 index 0000000..860112d Binary files /dev/null and b/day21/c/day21 differ diff --git a/day21/c/day21.c b/day21/c/day21.c new file mode 100644 index 0000000..c60559e --- /dev/null +++ b/day21/c/day21.c @@ -0,0 +1,421 @@ +#include +#include +#include +#include + +#define CODE_LEN 4 +#define CODES 5 +#define CODE_MAX_LEN 16*1024 +#define sgn(a) ((a) < 0 ? -1 : ((a) > 0 ? 1 : 0)) +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define abs(a) ((a) < 0 ? -(a) : (a)) + +typedef enum { + NUMERIC, + DIRECTIONAL +} panel_type; + +typedef struct node { + struct node *children[11]; + uint64_t value; + uint64_t value2; + char key; + uint8_t is_leaf; +} node_t; + + +typedef struct layer { + panel_type type; + struct layer *next_layer; + int current_position[2]; + int id; + node_t *trie; +} layer_t; + +const int numeric_panel[][2] = { + {3, 1}, // 0 + {2, 0}, // 1 + {2, 1}, // 2 + {2, 2}, // 3 + {1, 0}, // 4 + {1, 1}, // 5 + {1, 2}, // 6 + {0, 0}, // 7 + {0, 1}, // 8 + {0, 2}, // 9 + {3, 2}, // A +}; + +const int directional_panel[][2] = { + {0, 1}, // UP + {1, 0}, // LEFT + {1, 1}, // DOWN + {1, 2}, // RIGHT + {0, 2}, // A +}; + +#define UP 0 +#define LEFT 1 +#define DOWN 2 +#define RIGHT 3 + +#define d2c(a) ((a) == UP ? '^' : ((a) == LEFT ? '<' : ((a) == DOWN ? 'v' : ((a) == RIGHT ? '>' : 'A')))) + +layer_t* create_numeric_layer() { + layer_t *layer = calloc(1, sizeof(layer[0])); + layer->type = NUMERIC; + layer->next_layer = NULL; + layer->current_position[0] = numeric_panel[10][0]; + layer->current_position[1] = numeric_panel[10][1]; + layer->trie = calloc(1, sizeof(node_t)); + return layer; +} + +layer_t* create_directional_layer() { + layer_t *layer = calloc(1, sizeof(layer[0])); + layer->type = DIRECTIONAL; + layer->next_layer = NULL; + layer->current_position[0] = directional_panel[4][0]; + layer->current_position[1] = directional_panel[4][1]; + layer->trie = calloc(1, sizeof(node_t)); + return layer; +} + +int c2n(char code) { + if (code == 'A') { + return 10; + } + + return code - 48; +} + +node_t* create_node() { + node_t *node = calloc(1, sizeof(node[0])); + return node; +} + +node_t* search(node_t* root, int* word, int len) { + node_t* node = root; + for (int i = 0; i < len; i++) { + if (node->children[word[i]] == NULL) { + return NULL; + } + node = node->children[word[i]]; + } + return node; +} + +void insert(node_t* root, int* word, int len, uint64_t value, int value2) { + node_t* node = root; + for (int i = 0; i < len; i++) { + if (node->children[word[i]] == NULL) { + node->children[word[i]] = create_node(); + } + node = node->children[word[i]]; + } + node->value = value; + node->value2 = value2; + node->is_leaf = 1; +} + +void free_node(node_t* node) { + for (int i = 0; i < 11; i++) { + if (node->children[i] != NULL) { + free_node(node->children[i]); + } + } + + free(node); +} + +uint64_t process_layer(layer_t *layer, int *code, uint64_t code_len, int *new_code, uint64_t new_code_len); +uint64_t process_layer2(layer_t *layer, int* code, uint64_t code_len); +int coords_to_idx(layer_t *layer, int coords[2]); +uint64_t cost_of_activation(layer_t *layer, int button); + +int main() { + char c; + + char codes[CODES][CODE_LEN]; + int code_count = 0, character_count = 0; + + while ((c = getchar()) != EOF) { + codes[code_count][character_count++] = c; + if (c != '\n') { + continue; + } + character_count = 0; + code_count++; + } + + // Create Layers + layer_t **layers = calloc(26, sizeof(layer_t*)); + // First is the numeric panel + layers[0] = create_numeric_layer(); + layers[0]->id = 0; + // Create directional layers + for (int i = 1; i < 26; i++) { + layers[i] = create_directional_layer(); + layers[i]->id = i; + } + // Link layers + for (int i = 0; i < 25; i++) { + layers[i]->next_layer = layers[i+1]; + } + + uint64_t sum = 0; + for (int i = 0; i < code_count; i++) { + // Convert code chars to int + int code_encoded[4]; + for (int j = 0; j < 4; j++) { + code_encoded[j] = c2n(codes[i][j]); + } + + int *ccode = calloc(CODE_MAX_LEN, sizeof(ccode[0])); + //uint64_t a = process_layer(layers[0], code_encoded, 4, ccode, 0); + int code_encoded2[5]; + for (int j = 0; j < 4; j++) { + code_encoded2[j+1] = c2n(codes[i][j]); + } + code_encoded2[0] = 10; + uint64_t a = process_layer2(layers[0], code_encoded2, 5); + free(ccode); + int b; + sscanf(codes[i], "%d", &b); + sum += a * (uint64_t)b; + } + printf("%lu\n", sum); + + for (int i = 0; i < 26; i++) { + free_node(layers[i]->trie); + free(layers[i]); + } + free(layers); +} + +uint64_t process_layer(layer_t *layer, int *code, uint64_t code_len, int *new_code, uint64_t new_code_len) { + if (code_len == 0) { + if (layer->next_layer == NULL) { + return new_code_len; + } + int *ccode = calloc(CODE_MAX_LEN, sizeof(ccode[0])); + uint64_t result = process_layer(layer->next_layer, new_code, new_code_len, ccode, 0); + free(ccode); + return result; + } + + int current_position[2]; + current_position[0] = layer->current_position[0]; + current_position[1] = layer->current_position[1]; + + int (*panel)[2] = layer->type == NUMERIC ? numeric_panel : directional_panel; + // Check if we are on the right button + if (current_position[0] == panel[code[0]][0] && current_position[1] == panel[code[0]][1]) { + // Click button + new_code[new_code_len] = 4; + return process_layer(layer, code + 1, code_len - 1, new_code, new_code_len+1); + } + + uint64_t ncl = new_code_len; + uint64_t value1 = UINT64_MAX, value2 = UINT64_MAX; + // Try going in each direction towards the next button + if (current_position[0] != panel[code[0]][0]) { + // Up or down + if (current_position[0] - panel[code[0]][0] > 0) { + for (int i = 0; i < current_position[0] - panel[code[0]][0]; i++) { + new_code[new_code_len++] = UP; + } + } else { + for (int i = 0; i < panel[code[0]][0] - current_position[0]; i++) { + new_code[new_code_len++] = DOWN; + } + } + layer->current_position[0] = panel[code[0]][0]; + // Cannot go to empty space + if ((layer->type == NUMERIC && layer->current_position[0] == 3 && layer->current_position[1] == 0) || (layer->type == DIRECTIONAL && layer->current_position[0] == 0 && layer->current_position[1] == 0)) { + } else { + value1 = process_layer(layer, code, code_len, new_code, new_code_len); + } + } + layer->current_position[0] = current_position[0]; + layer->current_position[1] = current_position[1]; + new_code_len = ncl; + + if (current_position[1] != panel[code[0]][1]) { + // Up or down + if (current_position[1] - panel[code[0]][1] > 0) { + for (int i = 0; i < current_position[1] - panel[code[0]][1]; i++) { + new_code[new_code_len++] = LEFT; + } + } else { + for (int i = 0; i < panel[code[0]][1] - current_position[1]; i++) { + new_code[new_code_len++] = RIGHT; + } + } + layer->current_position[1] = panel[code[0]][1]; + // Cannot go to empty space + if ((layer->type == NUMERIC && layer->current_position[0] == 3 && layer->current_position[1] == 0) || (layer->type == DIRECTIONAL && layer->current_position[0] == 0 && layer->current_position[1] == 0)) { + } else { + value2 = process_layer(layer, code, code_len, new_code, new_code_len); + } + } + layer->current_position[0] = current_position[0]; + layer->current_position[1] = current_position[1]; + + + uint64_t value = min(value1, value2); + + return value; +} + +uint64_t process_layer2(layer_t *layer, int* code, uint64_t code_len) { + uint64_t sum = 0; + if (layer == NULL) { + return 1; + } + int (*panel)[2] = layer->type == NUMERIC ? numeric_panel : directional_panel; + int current_position[2]; + int button_count = layer->type == NUMERIC ? 11 : 5; + + // Build cache for each combination + if (layer->trie->children[0] == NULL) { + for (int i = 0; i < button_count; i++) { + for (int j = 0; j < button_count; j++) { + int combination[2] = {i, j}; + int start1[2] = {panel[i][0], panel[i][1]}; + int start2[2] = {panel[i][0], panel[i][1]}; + int end[2] = {panel[j][0], panel[j][1]}; + + int last_action1 = 4; + // X first, Y second + uint64_t distance1 = 0; + while (start1[0] != end[0]) { + int dir = sgn(end[0] - start1[0]); + start1[0] += dir; + if ((layer->type == NUMERIC && start1[0] == 3 && start1[1] == 0) || (layer->type == DIRECTIONAL && start1[0] == 0 && start1[1] == 0)) { + distance1 = UINT64_MAX; + break; + } + int subcombination[2]; + subcombination[0] = last_action1; + if (dir == -1) { + subcombination[1] = UP; + last_action1 = UP; + } else { + subcombination[1] = DOWN; + last_action1 = DOWN; + } + distance1 += process_layer2(layer->next_layer, subcombination, 2); + } + if (distance1 != UINT64_MAX) { + while (start1[1] != end[1]) { + int dir = sgn(end[1] - start1[1]); + start1[1] += dir; + int subcombination[2]; + subcombination[0] = last_action1; + if (dir == -1) { + subcombination[1] = LEFT; + last_action1 = LEFT; + } else { + subcombination[1] = RIGHT; + last_action1 = RIGHT; + } + distance1 += process_layer2(layer->next_layer, subcombination, 2); + } + } + if (distance1 != UINT64_MAX) { + int activatecombination[2] = {last_action1, 4}; + distance1 += process_layer2(layer->next_layer, activatecombination, 2); + } + + int last_action2 = 4; + // Y first, X second + uint64_t distance2 = 0; + while (start2[1] != end[1]) { + int dir = sgn(end[1] - start2[1]); + start2[1] += dir; + if ((layer->type == NUMERIC && start2[0] == 3 && start2[1] == 0) || (layer->type == DIRECTIONAL && start2[0] == 0 && start2[1] == 0)) { + distance2 = UINT64_MAX; + break; + } + int subcombination[2]; + subcombination[0] = last_action2; + if (dir == -1) { + subcombination[1] = LEFT; + last_action2 = LEFT; + } else { + subcombination[1] = RIGHT; + last_action2 = RIGHT; + } + distance2 += process_layer2(layer->next_layer, subcombination, 2); + } + if (distance2 != UINT64_MAX) { + while (start2[0] != end[0]) { + int dir = sgn(end[0] - start2[0]); + start2[0] += dir; + int subcombination[2]; + subcombination[0] = last_action2; + if (dir == -1) { + subcombination[1] = UP; + last_action2 = UP; + } else { + subcombination[1] = DOWN; + last_action2 = DOWN; + } + distance2 += process_layer2(layer->next_layer, subcombination, 2); + } + } + if (distance2 != UINT64_MAX) { + int activatecombination[2] = {last_action2, 4}; + distance2 += process_layer2(layer->next_layer, activatecombination, 2); + } + + uint64_t distance = distance1; + int last_action = last_action1; + if (distance1 > distance2) { + distance = distance2; + last_action = last_action2; + } + + //printf("Layer %i: cost from %i to %i is %lu\n", layer->id, i, j, distance); + insert(layer->trie, combination, 2, distance, last_action); + } + } + } + + for (uint64_t i = 1; i < code_len; i++) { + int combination[2] = {code[i-1], code[i]}; + node_t *node = search(layer->trie, combination, 2); + sum += node->value; + } + + return sum; +} + +int coords_to_idx(layer_t *layer, int coords[2]) { + int idx; + int (*panel)[2] = layer->type == NUMERIC ? numeric_panel : directional_panel; + for (idx = 0; idx < (layer->type == NUMERIC ? 11 : 5); idx++) { + if (coords[0] == panel[idx][0] && coords[1] == panel[idx][1]) { + break; + } + } + + return idx; +} + +uint64_t cost_of_activation(layer_t *layer, int button) { + if (layer == NULL) { + return 1; + } + + // Build cache if not present + if (layer->trie->children[0] == NULL) { + process_layer2(layer, NULL, 0); + } + + int combination[2] = {button, 4}; + node_t *node = search(layer->trie, combination, 2); + + return node->value + cost_of_activation(layer->next_layer, node->value2); +} diff --git a/day21/input.txt b/day21/input.txt new file mode 100644 index 0000000..89a734f --- /dev/null +++ b/day21/input.txt @@ -0,0 +1,5 @@ +279A +286A +508A +463A +246A diff --git a/day21/sample.txt b/day21/sample.txt new file mode 100644 index 0000000..4cf0c29 --- /dev/null +++ b/day21/sample.txt @@ -0,0 +1,5 @@ +029A +980A +179A +456A +379A