Files
advent-of-code/2021/day18/aoc-c.c

297 lines
7.3 KiB
C

/* aoc-c.c: Advent of Code 2021, day 18 parts 1 & 2
*
* Copyright (C) 2021 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "pool.h"
#include "debug.h"
#include "list.h"
static pool_t *pool_nodes;
#define MAX_LINES 128 /* I know, I know... */
static char *lines[MAX_LINES];
static int nlines;
#define LEFT 0
#define RIGHT 1
#define IS_LEAF(node) (!(node)->tree[LEFT])
typedef struct node {
int val;
struct node *tree[2]; /* left & right subtrees */
struct list_head leaf_list; /* tree root is list head */
} node_t;
/* get a node from memory pool
*/
static inline node_t *node_get()
{
node_t *node = pool_get(pool_nodes);
node->tree[LEFT] = NULL;
node->tree[RIGHT] = NULL;
return node;
}
/* recursive cleanup of node
*/
static inline void node_free(node_t *node)
{
if (!IS_LEAF(node)) {
node_free(node->tree[LEFT]);
node_free(node->tree[RIGHT]);
}
pool_add(pool_nodes, node);
}
/* split_node - split first node with value >= 10
* return: 1: finished
*/
static int node_split(node_t *node)
{
if (IS_LEAF(node)) {
if (node->val >= 10) { /* eligible leaf */
node_t *left, *right;
/* create and populate new nodes */
left = node_get();
left->val = node->val / 2;
right = node_get();
right->val = (node->val + 1) / 2;
node->tree[LEFT] = left;
node->tree[RIGHT] = right;
/* add new nodes in leaves list, remove current one */
list_add(&node->tree[RIGHT]->leaf_list, &node->leaf_list);
list_add(&node->tree[LEFT]->leaf_list, &node->leaf_list);
list_del(&node->leaf_list);
return 1;
}
} else {
return node_split(node->tree[LEFT]) || node_split(node->tree[RIGHT]);
}
return 0;
}
/* explode node - explode first node with level == 4
* return: 1: finished
*/
static int node_explode(node_t *node, int depth)
{
static node_t *root;
node_t *left, *right;
if (!(left = node->tree[LEFT]))
return 0;
right = node->tree[RIGHT];
/* Need to keep leaves list head */
if (depth == 0)
root = node;
if (depth == 4) {
/* increment left and right leaves values */
if (!list_is_first(&left->leaf_list, &root->leaf_list))
list_prev_entry(left, leaf_list)->val += left->val;
if (!list_is_last(&right->leaf_list, &root->leaf_list))
list_next_entry(right, leaf_list)->val += right->val;
/* node becomes a leaf */
node->val = 0;
node->tree[LEFT] = NULL;
node->tree[RIGHT] = NULL;
list_add(&node->leaf_list, &left->leaf_list);
/* remove children from leaves list, and put back in mem pool */
list_del(&left->leaf_list);
list_del(&right->leaf_list);
pool_add(pool_nodes, left);
pool_add(pool_nodes, right);
return 1;
} else {
return node_explode(left, depth + 1) ||
node_explode(right, depth + 1);
}
}
/* node reduce:
*/
static node_t *node_reduce(node_t *node)
{
while (1) {
if (!node_explode(node, 0) && !node_split(node))
return node;
}
}
/* add 2 nodes
*/
static node_t *node_add(node_t *n1, node_t *n2)
{
node_t *head = pool_get(pool_nodes);
INIT_LIST_HEAD(&head->leaf_list);
/* create new tree from the two
*/
head->tree[LEFT] = n1;
head->tree[RIGHT] = n2;
/* link corresponding leaves
*/
list_splice_tail(&n1->leaf_list, &head->leaf_list);
list_splice_tail(&n2->leaf_list, &head->leaf_list);
return head;
}
/* read node, from '[' to corresponding ']'
*/
static inline node_t *_node_read(char **p, int depth)
{
static node_t *root;
node_t *node = node_get();
if (!depth) { /* root node */
root = node;
INIT_LIST_HEAD(&node->leaf_list);
}
switch (**p) {
case '[':
(*p)++; /* skip left bracket */
node->tree[LEFT] = _node_read(p, depth + 1);
(*p)++; /* skip comma */
node->tree[RIGHT] = _node_read(p, depth + 1);
break;
default: /* number: add to tail list */
node->val = **p - '0';
list_add_tail(&node->leaf_list, &root->leaf_list);
}
(*p)++;
return node;
}
/* wrapper for recursive function
*/
static inline node_t *node_read(char *p)
{
char *tmp = p;
return _node_read(&tmp, 0);
}
/* read input
*/
static void read_lines()
{
size_t alloc = 0;
ssize_t buflen;
while ((buflen = getline(&lines[nlines], &alloc, stdin)) > 0) {
lines[nlines][--buflen] = 0;
nlines++;
}
free(lines[nlines]); /* EOF, need to be freed */
}
/* free lines memory
*/
static void free_lines()
{
for (int i = 0; i < nlines; ++i)
free(lines[i]);
}
static inline int node_magnitude(node_t *node)
{
if (!node->tree[LEFT])
return node->val;
return 3 * node_magnitude(node->tree[LEFT]) +
2 * node_magnitude(node->tree[RIGHT]);
}
static int part1()
{
node_t *head, *next;
int res;
head = node_read(lines[0]);
for (int i = 1; i < nlines; ++i) {
next = node_read(lines[i]);
head = node_reduce(node_add(head, next));
}
res = node_magnitude(head);
node_free(head);
return res;
}
static int part2()
{
node_t *list;
int max = 0, cur;
/* calculate magnitude for any combination of two snailfish numbers */
for (int i = 0; i < nlines; ++i) {
for (int j = 0; j < nlines; ++j) {
if (j != i) {
list = node_reduce(node_add(node_read(lines[i]),
node_read(lines[j])));
if ((cur = node_magnitude(list)) > max)
max = cur;
node_free(list);
}
}
}
return max;
}
static int usage(char *prg)
{
fprintf(stderr, "Usage: %s [-d debug_level] [-p part]\n", prg);
return 1;
}
int main(int ac, char **av)
{
int opt, part = 1;
while ((opt = getopt(ac, av, "d:p:")) != -1) {
switch (opt) {
case 'd':
debug_level_set(atoi(optarg));
break;
case 'p': /* 1 or 2 */
part = atoi(optarg);
if (part < 1 || part > 2)
return usage(*av);
break;
default:
return usage(*av);
}
}
if (optind < ac)
return usage(*av);
if (!(pool_nodes = pool_create("nodes", 1024, sizeof(node_t))))
exit(1);
read_lines();
printf("%s : res=%d\n", *av, part == 1? part1(): part2());
free_lines();
pool_destroy(pool_nodes);
exit(0);
}