From f28c2d04069abcf6c747d6d3416acf3f1aa6f8a3 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Tue, 11 Apr 2023 09:18:48 +0200 Subject: [PATCH] 2022 day 16 part 1 (C) - before cleaning useless hash --- 2022/day16/README.org | 94 ++++++++++++++++++++- 2022/day16/aoc-c.c | 190 ++++++++++++++++++++++++------------------ 2 files changed, 201 insertions(+), 83 deletions(-) diff --git a/2022/day16/README.org b/2022/day16/README.org index 25a4aec..8d46549 100644 --- a/2022/day16/README.org +++ b/2022/day16/README.org @@ -178,6 +178,96 @@ with this valve layout, =1651=. Work out the steps to release the most pressure in 30 minutes. /What is the most pressure you can release?/ -To begin, [[file:16/input][get your puzzle input]]. +Your puzzle answer was =1737=. -Answer: +The first half of this puzzle is complete! It provides one gold star: * + +** --- Part Two --- +You're worried that even with an optimal approach, the pressure released +won't be enough. What if you got one of the elephants to help you? + +It would take you 4 minutes to teach an elephant how to open the right +valves in the right order, leaving you with only /26 minutes/ to +actually execute your plan. Would having two of you working together be +better, even if it means having less time? (Assume that you teach the +elephant before opening any valves yourself, giving you both the same +full 26 minutes.) + +In the example above, you could teach the elephant to help you as +follows: + +#+begin_example +== Minute 1 == +No valves are open. +You move to valve II. +The elephant moves to valve DD. + +== Minute 2 == +No valves are open. +You move to valve JJ. +The elephant opens valve DD. + +== Minute 3 == +Valve DD is open, releasing 20 pressure. +You open valve JJ. +The elephant moves to valve EE. + +== Minute 4 == +Valves DD and JJ are open, releasing 41 pressure. +You move to valve II. +The elephant moves to valve FF. + +== Minute 5 == +Valves DD and JJ are open, releasing 41 pressure. +You move to valve AA. +The elephant moves to valve GG. + +== Minute 6 == +Valves DD and JJ are open, releasing 41 pressure. +You move to valve BB. +The elephant moves to valve HH. + +== Minute 7 == +Valves DD and JJ are open, releasing 41 pressure. +You open valve BB. +The elephant opens valve HH. + +== Minute 8 == +Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +You move to valve CC. +The elephant moves to valve GG. + +== Minute 9 == +Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +You open valve CC. +The elephant moves to valve FF. + +== Minute 10 == +Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure. +The elephant moves to valve EE. + +== Minute 11 == +Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure. +The elephant opens valve EE. + +(At this point, all valves are open.) + +== Minute 12 == +Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +... + +== Minute 20 == +Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +... + +== Minute 26 == +Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. +#+end_example + +With the elephant helping, after 26 minutes, the best you could do would +release a total of =1707= pressure. + +/With you and an elephant working together for 26 minutes, what is the +most pressure you could release?/ diff --git a/2022/day16/aoc-c.c b/2022/day16/aoc-c.c index e147db8..4c6ec4d 100644 --- a/2022/day16/aoc-c.c +++ b/2022/day16/aoc-c.c @@ -29,12 +29,11 @@ static DEFINE_HASHTABLE(hasht_valves, HBITS); pool_t *pool_valve; union val { - u16 val; - char str[2]; + u32 val; + char str[3]; }; enum state { -// BLOCKED = 0, CLOSED, OPENED }; @@ -44,10 +43,13 @@ struct valve { union val val; enum state state; int rate; + int evalflow, evaltime; + int playedflow, playedtime; struct hlist_node hlist; struct list_head index_sorted; struct list_head flow_sorted; struct list_head permute; + struct list_head eval; struct list_head played; int ntunnels, tottunnels; struct valve **tunnels; /* array */ @@ -63,6 +65,7 @@ static struct graph { struct list_head index_sorted; /* TO REMOVE ? */ struct list_head flow_sorted; struct list_head permute; + struct list_head eval; struct list_head played; struct valve **indexed; int *dist; /* 2-D array */ @@ -74,13 +77,14 @@ static struct graph { .index_sorted = LIST_HEAD_INIT(graph.index_sorted), .flow_sorted = LIST_HEAD_INIT(graph.flow_sorted), .permute = LIST_HEAD_INIT(graph.permute), + .eval = LIST_HEAD_INIT(graph.eval), .played = LIST_HEAD_INIT(graph.played), .indexed = NULL, .dist = NULL }; -#define pos(a, b) ((a)*graph.nvalves + (b)) -#define dist(a, b) (graph.dist[pos((a), (b))]) +#define POS(a, b) ((a)*graph.nvalves + (b)) +#define DIST(a, b) (graph.dist[POS((a), (b))]) static void print_valves() { @@ -89,44 +93,44 @@ static void print_valves() printf("**** graph: .head=%p npositive=%d nzero=%d\n", graph.aa, graph.npositive, graph.nzero); hash_for_each(hasht_valves, bucket, cur, hlist) { - printf("Valve %2.2s: rate=%d ntunnels=%d tottunnels=%d ( ", + printf("Valve %s: rate=%d ntunnels=%d tottunnels=%d ( ", cur->val.str, cur->rate, cur->ntunnels, cur->tottunnels); for (int i=0; i < cur->ntunnels; ++i) - printf("%2s ", cur->tunnels[i]->val.str); + printf("%s ", cur->tunnels[i]->val.str); printf(")\n"); } printf("index1: "); list_for_each_entry(cur, &graph.index_sorted, index_sorted) { - printf("%d:%2.2s ", cur->index, cur->val.str); + printf("%d:%s ", cur->index, cur->val.str); } - printf(")\n"); + printf("\n"); printf("index2: "); for (int i = 0; i < graph.nvalves; ++i) { - printf("%d:%d:%2.2s ", i, graph.indexed[i]->index, graph.indexed[i]->val.str); + printf("%d:%s ", graph.indexed[i]->index, graph.indexed[i]->val.str); } - printf(")\n"); + printf("\n"); if (testmode()) { printf("distances:\n "); for (int i = 0; i < graph.nvalves; ++i) { - printf(" %2.2s", graph.indexed[i]->val.str); + printf(" %s", graph.indexed[i]->val.str); } printf("\n"); for (int i = 0; i < graph.nvalves; ++i) { - printf("%2.2s ", graph.indexed[i]->val.str); + printf("%s ", graph.indexed[i]->val.str); for (int j = 0; j < graph.nvalves; ++j) { - printf("%5d ", dist(i, j)); + printf("%5d ", DIST(i, j)); } printf("\n"); } } printf("flow_sorted: "); list_for_each_entry(cur, &graph.flow_sorted, flow_sorted) { - printf("%2.2s:%d ", cur->val.str, cur->rate); + printf("%s:%d ", cur->val.str, cur->rate); } printf("\n"); printf("permute: "); list_for_each_entry(cur, &graph.permute, permute) { - printf("%2.2s:%d ", cur->val.str, cur->rate); + printf("%s:%d ", cur->val.str, cur->rate); } printf("\n"); printf("openable: %#lx ", graph.openable); @@ -139,51 +143,79 @@ static void print_valves() } -static struct valve *valve_nth(struct list_head *start, struct list_head *head, - int n) -{ - struct valve *cur = list_first_entry_or_null(start, struct valve, flow_sorted); - int i = 1; +//#define flow2valve(p) list_entry(p, struct valve, flow_sorted) - if (cur) { - list_for_each_entry_from(cur, head, flow_sorted) { - if (i == n || cur->flow_sorted.next == head) - break; - i++; + +/** + * eval() - eval possible moves from @flow_sorted list. + * @valve: &starting valve (where we are). + * @depth: remaining depth (-1: full depth). + * @pick: max position (in @flow_sorted) to pick moves from (-1 for all). + * @time: remaining time. + * + * Find the "best" next move by evaluating up to @depth moves, using only the + * first @pick elements in @flow_sorted list, and within @time remaining time. + * + * @depth and @picked may be linked, for instance to fully explore the first N + * possibilities in @flow_sorted with a N depth. + * + * @Return: the current position eval. + */ +#define PAD3 log(3, "%*s", _depth, "") +#define PAD4 log(4, "%*s", _depth, "") + +static struct valve *eval(int _depth, struct valve *pos, int depth, int pick, int time, int pressure) +{ + struct valve *cur, *best = NULL, *sub; + struct list_head *list_flow, *tmp; + int _pick = pick, val = 0, val1, max = 0; + + PAD3; + log(3, "EVAL _depth=%d pos=%d[%s] depth=%d pick=%d time=%d pressure=%d\n", + _depth, pos->index, pos->val.str, depth, pick, time, pressure); + list_for_each_safe(list_flow, tmp, &graph.flow_sorted) { + cur = list_entry(list_flow, struct valve, flow_sorted); + int d = DIST(pos->index, cur->index); + PAD4; log(4, "dist(%s,%s) = %d\n", pos->val.str, cur->val.str, d); + if (!--_pick) { + PAD4; log(4, "pick exhausted\n"); + continue; } + if (time - (d + 1 + 1) < 0) { + PAD4; log(4, "time exhausted\n"); + continue; + } + val = (time - (d + 1)) * cur->rate; + PAD4; log(4, "val=%d\n", val); + + if (depth > 0) { + /* do not use list_del() here, to preserve prev/next pointers */ + __list_del_entry(list_flow); + sub = eval(_depth + 2, cur, depth - 1, pick - 1, time - d - 1, pressure + pos->rate); + list_flow->prev->next = list_flow; + list_flow->next->prev = list_flow; + } else { + sub = NULL; + } + val1 = sub? sub->evalflow: 0; + PAD3; log(3, "eval(%s->%s)= %5d = %d + %d", pos->val.str, cur->val.str, + val+val1, val, val1); + if (val + val1 > max) { + max = val + val1; + best = cur; + log(3, " NEW MAX !"); + } + log(3, "\n"); } - return cur; + if (best) { + best->evalflow = max; + PAD3; log(3, "EVAL returning best [%s] eval=%d\n", best->val.str, max); + //best->evaltime = time - (d + 2); + } + return best; } -#define flow2valve(p) list_entry(p, struct valve, flow_sorted) - -static struct valve *list_nth(struct list_head *start, struct list_head *head, - int n) -{ - struct list_head *cur = NULL; - - if (n == 0 || start->next == head) - return NULL; - list_for_each_continue(cur, start) { - if (!--n || cur == head) - break; - } - return cur ? flow2valve(cur): NULL; -} - -static void list_reverse(struct list_head *start, struct list_head *head, int n) -{ - struct list_head *cur = start->next, *tmp; - - list_for_each_safe(cur, tmp, start) { - if (!--n || cur == head) - break; - list_move_tail(cur, start); - start = cur; - } -} - -static void permute_prepare(int n) +static __unused void permute_prepare(int n) { struct valve *cur; INIT_LIST_HEAD(&graph.permute); @@ -198,7 +230,7 @@ static void permute_prepare(int n) * permute() - get next permutation in graph.permute list. * @n: permutation number (0 first first one) * - * Will construct next permutation in graph.permute list, following the + * Construct next permutation in graph.permute list, following the * "lexicographic order algorithm" : * https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order * @@ -207,7 +239,7 @@ static void permute_prepare(int n) * 2) permute_prepare() should have been called. * @Return: 0 if no more permutation, 1 otherwise. */ -static int permute(int n) +static __unused int permute(int n) { struct valve *last, *first, *k, *l; @@ -244,7 +276,7 @@ static struct valve *find_valve(union val val, int ntunnels, int rate) struct valve *cur; uint hash = val.val, bucket = hash_32(hash, HBITS); - log_f(3, "val=%2.2s ntunnels=%d rate=%d h=%u b=%d\n", val.str, ntunnels, + log_f(3, "val=%s ntunnels=%d rate=%d h=%u b=%d\n", val.str, ntunnels, rate, hash, bucket); hlist_for_each_entry(cur, &hasht_valves[bucket], hlist) { if (cur->val.val == val.val) { @@ -258,6 +290,8 @@ static struct valve *find_valve(union val val, int ntunnels, int rate) cur->val.val = val.val; cur->ntunnels = 0; cur->state = CLOSED; + cur->evalflow = cur->playedflow = 0; + cur->evaltime = cur->playedtime = 30; INIT_LIST_HEAD(&cur->index_sorted); INIT_LIST_HEAD(&cur->flow_sorted); INIT_LIST_HEAD(&cur->permute); @@ -297,7 +331,7 @@ static struct graph *parse() while ((buflen = getline(&buf, &alloc, stdin)) > 0) { buf[--buflen] = 0; - cur.val = *(u16 *)getnth(buf, 1); + strncpy(cur.str, getnth(buf, 1), sizeof(cur.str)); //printf("valve=%s ", tok); rate = atoi(getnth(NULL, 3)); //printf("rate=%s ", tok); @@ -308,7 +342,8 @@ static struct graph *parse() // TODO: remove this list ? list_add_tail(&v1->index_sorted, &graph.index_sorted); graph.nvalves++; - if (rate || v1->val.val == ('A' << 8 | 'A')) { + //if (rate || v1->val.val == ('A' << 8 | 'A')) { + if (rate) { struct valve *cur; graph.npositive++; /* keep this list sorted by flow decrasing */ @@ -334,7 +369,7 @@ static struct graph *parse() v2 = find_valve(link, 0, 0); *(v1->tunnels + v1->ntunnels++) = v2; //printf(",%s", tok); - } while ((tok = getnth(NULL, 1))); + } while ((tok = getnth(NULL, 0))); //printf("\n"); } graph.aa = find_valve((union val) { .str="AA" }, 0, 0); @@ -363,12 +398,12 @@ static void build_distances() graph.dist = calloc(graph.nvalves * graph.nvalves, sizeof(int)); /* initialize values */ for (i = 0; i < graph.nvalves; ++i) { - for (j = i; j < graph.nvalves; ++j) { + for (j = 1; j < graph.nvalves; ++j) { if (i != j) { if (is_neighbour(i, j)) - dist(i, j) = dist(j, i) = 1; + DIST(i, j) = DIST(j, i) = 1; else - dist(i, j) = dist(j, i) = 10000; + DIST(i, j) = DIST(j, i) = 10000; } //printf("pos(%d,%d)=%d\n", i, j, pos(i, j)); } @@ -390,11 +425,9 @@ static void build_distances() for (i = 0; i < graph.nvalves; i++) { /* Pick all vertices as destination for the above picked source */ for (j = i + 1; j < graph.nvalves; j++) - dist(i, j) = dist(j, i) = min(dist(i, j), dist(i, k) + dist(k, j)); + DIST(i, j) = DIST(j, i) = min(DIST(i, j), DIST(i, k) + DIST(k, j)); } } - print_valves(); - /* first, build an array */ return; } @@ -416,28 +449,23 @@ static ulong part1() printf("part1\n"); build_distances(); - struct valve *v; - for (int i = 1; i < 10; ++i) { - v = valve_nth(&graph.flow_sorted, &graph.flow_sorted, i); - printf("sorted(%d): i=%d rate=%d\n", i, v->index, v->rate); - } - permute_prepare(4); print_valves(); - struct valve *cur; - printf("permutation 0: "); - list_for_each_entry(cur, &graph.permute, permute) { - printf("%d ", cur->rate); - } - printf("\n"); + puts("zob1"); + eval(0, graph.aa, 7, 7, 30, 0); + puts("zob2"); + /* + permute_prepare(4); for (int i = 0; permute(i); ++i) { + struct valve *cur; printf("permutation %d: ", i); list_for_each_entry(cur, &graph.permute, permute) { printf("%d ", cur->rate); } printf("\n"); } - //res = do_1(cur, 0, 0); + */ + return res; }