/* aoc-c.c: Advent of Code 2019, day 6 parts 1 & 2 * * Copyright (C) 2022 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 . * * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include "br.h" #include "debug.h" #include "pool.h" /** * As the character set is [1-9A-Z], the trie arrays size will be 26 + 9 = 35, * organized as: * char: 1 2 ... 8 9 A B ... Y Z * index: 0 1 ... 7 8 9 10 ... 33 34 */ #define TRIESIZE ('Z' - 'A' + 1 + '9' - '1' + 1) #define c2index(c) ((c) >= 'A'? (c) - 'A' + 9: (c) - '1') #define index2c(c) ((c) >= 9? (c) + 'A' - 9: (c) + '1') /** * object_t - object representation * @parent: a pointer to the object we orbit around * @sibling: a list of objects orbiting around @parent * @child: a list of object orbiting around this object * @name: the object name * * Example: if N1 and N2 orbit around O and S orbits around N1, we will * have : * +---------+ * +---->| 0 |<---------+ * | |---------| | * | | parent |--->NIL | * | |---------| | * +-------------------+---->| child |<---------+-----------------+ * | | |---------| | | * | | +-->| sibling |<--+ | | * | | | +---------+ | | | * | | | | | | * | +---------+ | +-----------------+ | +---------+ | * | | N1 |<---+-----+ | | N2 | | * | |---------| | | | |---------| | * | | parent |----+ | +--| parent | | * | |---------| | |---------| | * | +->| child |<---------+----+ NIL<---| child | | * | | |---------| | | |---------| | * +-+->| sibling |<---------+----+----------------->| sibling |<---+ * | +---------+ | | +---------+ * | | | * | +---------+ | | * | | S | | | * | |---------| | | * | | parent |----------+ | * | |---------| | * | | child |--->NIL | * | |---------| | * +->| sibling |<--------------+ * +---------+ * */ typedef struct object { struct object *parent; struct list_head sibling, child; char name[8]; } object_t; /** * trie_t - trie node * @child: array of pointers to trie_t children of current node * @object: pointer to object data (NULL if node only) * * For example, if objects N1, N2, and S exist, the structure will be: * * Root trie * +--------+-------------------------------------+ * | object | 0 | 1 | ... | N | ... | S | ... | Z | * +--------+---------------+---------------------+ * | | | | | * v v | | v * NIL NIL | | NIL * +----------------------------+ +-----+ * | "N" trie | "S" trie * | +--------+-------------+ | +--------------------------+ * +-->| object | 0 | ... | Z | +->| object | 0 | 1 | 2 | ... | * +--------+-------------+ +--------------------------+ * | | | | | | | * | v v v v | | * | NIL NIL NIL NIL | | * | +---------------------------------+ | * | | +-----------+ * | | "S1" trie | "S2" trie * | | +------------------+ | +------------------+ * | +-->| object | 0 | ... | +-->| object | 0 | ... | * | +------------------+ +------------------+ * | | | | | * | | v | v * v v NIL v NIL * +-----------+ +-----------+ +-----------+ * | Object N | | Object S1 | | Object S2 | * +-----------+ +-----------+ +-----------+ */ typedef struct trie { struct trie *child[TRIESIZE]; object_t *object; } trie_t; static pool_t *pool_tries, *pool_objects; static trie_t *trie_get(trie_t *parent, char *name, int pos) { trie_t *trie; static int total = 0; total ++; if ((trie = pool_get(pool_tries))) { for (int i = 0; i < TRIESIZE; ++i) trie->child[i] = NULL; trie->object = NULL; if (parent) parent->child[c2index(name[pos])] = trie; } return trie; } static trie_t *trie_find(trie_t *root, char *name) { int len = strlen(name); for (int i = 0; i < len; ++i) { int ind = c2index(name[i]); root = root->child[ind] ? root->child[ind]: trie_get(root, name, i); } return root; } static object_t *object_find(trie_t *root, char *name) { trie_t *trie = trie_find(root, name); static int total = 0; if (!trie->object) { total ++; trie->object = pool_get(pool_objects); trie->object->parent = NULL; strcpy(trie->object->name, name); INIT_LIST_HEAD(&trie->object->child); INIT_LIST_HEAD(&trie->object->sibling); } return trie->object; } /** * get_orbits - get all orbits (direct and indirect) around an object * @object: object address * @depth: depth of current object */ static int get_orbits(object_t *object, int depth) { int ret = depth; object_t *cur; if (!list_empty(&object->child)) list_for_each_entry(cur, &object->child, sibling) ret += get_orbits(cur, depth + 1); return ret; } static int part1(trie_t *root, char *name) { object_t *object = object_find(root, name); return get_orbits(object, 0); } /** * get_depth - get depth of an object in a tree * @object: object address * Return: object depth */ static int get_depth(object_t *obj) { int res = 0; for (; obj; obj = obj->parent) res++; return res; } static int part2(trie_t *root, char *name1, char *name2) { object_t *obj1 = object_find(root, name1), *obj2 = object_find(root, name2); int count = 0, depth1, depth2; depth1 = get_depth(obj1); depth2 = get_depth(obj2); /* ensure highest depth is obj1 */ if (depth1 < depth2) { swap(obj1, obj2); swap(depth1, depth2); } /* make the 2 depths equal */ for (; depth1 > depth2; count++, depth1--) obj1 = obj1->parent; /* find common parent */ for (; obj1 != obj2; count += 2) { obj1 = obj1->parent; obj2 = obj2->parent; } return count - 2; /* coz' we want parents objects */ } static void parse(trie_t *root) { char str1[8], str2[8]; while (scanf(" %7[^)])%s", str1, str2) == 2) { object_t *star = object_find(root, str1); object_t *planet = object_find(root, str2); /* link planet to star, add planet to star's planets list */ planet->parent = star; list_add(&planet->sibling, &star->child); } } 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) default: return usage(*av); } } if (optind < ac) return usage(*av); pool_tries = pool_create("tries", 1024, sizeof(trie_t)); pool_objects = pool_create("objects", 1024, sizeof(object_t)); trie_t *root = trie_get(NULL, NULL, 0); parse(root); printf("%s : res=%d\n", *av, part == 1 ? part1(root, "COM") : part2(root, "YOU", "SAN")); pool_destroy(pool_tries); pool_destroy(pool_objects); exit (0); }