2022 day 16 part 1 (C) - before cleaning useless hash

This commit is contained in:
2023-04-11 09:18:48 +02:00
parent e8bed49e13
commit f28c2d0406
2 changed files with 201 additions and 83 deletions

View File

@@ -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?/

View File

@@ -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;
}