2022 day 16: Part 2, sooo slooowww (6m40s)
This commit is contained in:
@@ -180,8 +180,6 @@ the most pressure you can release?/
|
|||||||
|
|
||||||
Your puzzle answer was =1737=.
|
Your puzzle answer was =1737=.
|
||||||
|
|
||||||
The first half of this puzzle is complete! It provides one gold star: *
|
|
||||||
|
|
||||||
** --- Part Two ---
|
** --- Part Two ---
|
||||||
You're worried that even with an optimal approach, the pressure released
|
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?
|
won't be enough. What if you got one of the elephants to help you?
|
||||||
@@ -271,3 +269,7 @@ release a total of =1707= pressure.
|
|||||||
|
|
||||||
/With you and an elephant working together for 26 minutes, what is the
|
/With you and an elephant working together for 26 minutes, what is the
|
||||||
most pressure you could release?/
|
most pressure you could release?/
|
||||||
|
|
||||||
|
Your puzzle answer was =2216=.
|
||||||
|
|
||||||
|
Both parts of this puzzle are complete! They provide two gold stars: **
|
||||||
|
@@ -17,15 +17,11 @@
|
|||||||
#include "br.h"
|
#include "br.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "pool.h"
|
#include "pool.h"
|
||||||
#include "hashtable.h"
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "bits.h"
|
#include "bits.h"
|
||||||
|
|
||||||
#include "aoc.h"
|
#include "aoc.h"
|
||||||
|
|
||||||
#define SEP " ,;="
|
|
||||||
#define HBITS 6 /* 6 bits: 64 entries */
|
|
||||||
static DEFINE_HASHTABLE(hasht_valves, HBITS);
|
|
||||||
pool_t *pool_valve;
|
pool_t *pool_valve;
|
||||||
|
|
||||||
union val {
|
union val {
|
||||||
@@ -38,6 +34,12 @@ enum state {
|
|||||||
OPENED
|
OPENED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct worker {
|
||||||
|
struct valve *pos;
|
||||||
|
int depth;
|
||||||
|
int time;
|
||||||
|
};
|
||||||
|
|
||||||
struct valve {
|
struct valve {
|
||||||
int index; /* -1 for zero flow rate */
|
int index; /* -1 for zero flow rate */
|
||||||
union val val;
|
union val val;
|
||||||
@@ -48,8 +50,8 @@ struct valve {
|
|||||||
struct hlist_node hlist;
|
struct hlist_node hlist;
|
||||||
struct list_head index_sorted;
|
struct list_head index_sorted;
|
||||||
struct list_head flow_sorted;
|
struct list_head flow_sorted;
|
||||||
struct list_head permute;
|
|
||||||
struct list_head eval;
|
struct list_head eval;
|
||||||
|
int worker;
|
||||||
struct list_head played;
|
struct list_head played;
|
||||||
int ntunnels, tottunnels;
|
int ntunnels, tottunnels;
|
||||||
struct valve **tunnels; /* array */
|
struct valve **tunnels; /* array */
|
||||||
@@ -58,28 +60,25 @@ struct valve {
|
|||||||
static struct graph {
|
static struct graph {
|
||||||
struct valve *aa; /* head ("AA") */
|
struct valve *aa; /* head ("AA") */
|
||||||
int npositive; /* only "AA" & working valves */
|
int npositive; /* only "AA" & working valves */
|
||||||
int nzero; /* TO REMOVE ? */
|
|
||||||
int nvalves;
|
int nvalves;
|
||||||
u64 opened; /* bitmask of opened valves */
|
u64 opened; /* bitmask of opened valves */
|
||||||
u64 openable; /* bitmask of openable valves */
|
u64 openable; /* bitmask of openable valves */
|
||||||
struct list_head index_sorted; /* TO REMOVE ? */
|
struct list_head index_sorted; /* TO REMOVE ? */
|
||||||
struct list_head flow_sorted;
|
struct list_head flow_sorted;
|
||||||
struct list_head permute;
|
|
||||||
struct list_head eval;
|
struct list_head eval;
|
||||||
struct list_head played;
|
struct list_head played[2];
|
||||||
struct valve **indexed;
|
struct valve **indexed_all;
|
||||||
int *dist; /* 2-D array */
|
int *dist; /* 2-D array */
|
||||||
} graph = {
|
} graph = {
|
||||||
.aa = NULL,
|
.aa = NULL,
|
||||||
.npositive = 0,
|
.npositive = 0,
|
||||||
.nzero = 0,
|
|
||||||
.nvalves = 0,
|
.nvalves = 0,
|
||||||
.index_sorted = LIST_HEAD_INIT(graph.index_sorted),
|
.index_sorted = LIST_HEAD_INIT(graph.index_sorted),
|
||||||
.flow_sorted = LIST_HEAD_INIT(graph.flow_sorted),
|
.flow_sorted = LIST_HEAD_INIT(graph.flow_sorted),
|
||||||
.permute = LIST_HEAD_INIT(graph.permute),
|
|
||||||
.eval = LIST_HEAD_INIT(graph.eval),
|
.eval = LIST_HEAD_INIT(graph.eval),
|
||||||
.played = LIST_HEAD_INIT(graph.played),
|
.played[0] = LIST_HEAD_INIT(graph.played[0]),
|
||||||
.indexed = NULL,
|
.played[1] = LIST_HEAD_INIT(graph.played[1]),
|
||||||
|
.indexed_all = NULL,
|
||||||
.dist = NULL
|
.dist = NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,17 +87,8 @@ static struct graph {
|
|||||||
|
|
||||||
static void print_valves()
|
static void print_valves()
|
||||||
{
|
{
|
||||||
ulong bucket;
|
|
||||||
struct valve *cur;
|
struct valve *cur;
|
||||||
printf("**** graph: .head=%p npositive=%d nzero=%d\n", graph.aa, graph.npositive,
|
printf("**** graph: .head=%p npositive=%d\n", graph.aa, graph.npositive);
|
||||||
graph.nzero);
|
|
||||||
hash_for_each(hasht_valves, bucket, cur, hlist) {
|
|
||||||
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("%s ", cur->tunnels[i]->val.str);
|
|
||||||
printf(")\n");
|
|
||||||
}
|
|
||||||
printf("index1: ");
|
printf("index1: ");
|
||||||
list_for_each_entry(cur, &graph.index_sorted, index_sorted) {
|
list_for_each_entry(cur, &graph.index_sorted, index_sorted) {
|
||||||
printf("%d:%s ", cur->index, cur->val.str);
|
printf("%d:%s ", cur->index, cur->val.str);
|
||||||
@@ -106,17 +96,17 @@ static void print_valves()
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
printf("index2: ");
|
printf("index2: ");
|
||||||
for (int i = 0; i < graph.nvalves; ++i) {
|
for (int i = 0; i < graph.nvalves; ++i) {
|
||||||
printf("%d:%s ", graph.indexed[i]->index, graph.indexed[i]->val.str);
|
printf("%d:%s ", graph.indexed_all[i]->index, graph.indexed_all[i]->val.str);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
if (testmode()) {
|
if (testmode()) {
|
||||||
printf("distances:\n ");
|
printf("distances:\n ");
|
||||||
for (int i = 0; i < graph.nvalves; ++i) {
|
for (int i = 0; i < graph.nvalves; ++i) {
|
||||||
printf(" %s", graph.indexed[i]->val.str);
|
printf(" %s", graph.indexed_all[i]->val.str);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
for (int i = 0; i < graph.nvalves; ++i) {
|
for (int i = 0; i < graph.nvalves; ++i) {
|
||||||
printf("%s ", graph.indexed[i]->val.str);
|
printf("%s ", graph.indexed_all[i]->val.str);
|
||||||
for (int j = 0; j < graph.nvalves; ++j) {
|
for (int j = 0; j < graph.nvalves; ++j) {
|
||||||
printf("%5d ", DIST(i, j));
|
printf("%5d ", DIST(i, j));
|
||||||
}
|
}
|
||||||
@@ -128,11 +118,6 @@ static void print_valves()
|
|||||||
printf("%s:%d ", cur->val.str, cur->rate);
|
printf("%s:%d ", cur->val.str, cur->rate);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("permute: ");
|
|
||||||
list_for_each_entry(cur, &graph.permute, permute) {
|
|
||||||
printf("%s:%d ", cur->val.str, cur->rate);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
printf("openable: %#lx ", graph.openable);
|
printf("openable: %#lx ", graph.openable);
|
||||||
int pos, tmp;
|
int pos, tmp;
|
||||||
bit_for_each64_2(pos, tmp, graph.openable) {
|
bit_for_each64_2(pos, tmp, graph.openable) {
|
||||||
@@ -143,15 +128,17 @@ static void print_valves()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#define flow2valve(p) list_entry(p, struct valve, flow_sorted)
|
#define PAD3 log(3, "%*s", _depth * 2, "")
|
||||||
|
#define PAD4 log(4, "%*s", _depth * 2, "")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eval() - eval possible moves from @flow_sorted list.
|
* eval() - eval possible moves from @flow_sorted list.
|
||||||
|
* @_depth: recursivity depth (for debug only, TODO: remove).
|
||||||
* @valve: &starting valve (where we are).
|
* @valve: &starting valve (where we are).
|
||||||
* @depth: remaining depth (-1: full depth).
|
* @depth: remaining depth (-1: full depth).
|
||||||
* @pick: max position (in @flow_sorted) to pick moves from (-1 for all).
|
* @pick: max position (in @flow_sorted) to pick moves from (-1 for all).
|
||||||
* @time: remaining time.
|
* @time: remaining time.
|
||||||
|
* @pressure: total pressure per time unit so far.
|
||||||
*
|
*
|
||||||
* Find the "best" next move by evaluating up to @depth moves, using only the
|
* 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.
|
* first @pick elements in @flow_sorted list, and within @time remaining time.
|
||||||
@@ -161,18 +148,14 @@ static void print_valves()
|
|||||||
*
|
*
|
||||||
* @Return: the current position eval.
|
* @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)
|
static struct valve *eval(int _depth, struct valve *pos, int depth, int pick, int time, int pressure)
|
||||||
{
|
{
|
||||||
struct valve *cur, *best = NULL, *sub;
|
struct valve *cur, *best = NULL, *sub;
|
||||||
struct list_head *list_flow, *tmp;
|
struct list_head *list_flow, *tmp;
|
||||||
int _pick = pick, val = 0, val1, max = 0;
|
int _pick = pick, val = 0, val1, max = 0;
|
||||||
|
|
||||||
PAD3;
|
PAD3; log(3, "EVAL _depth=%d pos=%d[%s] depth=%d pick=%d time=%d pressure=%d\n",
|
||||||
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);
|
||||||
_depth, pos->index, pos->val.str, depth, pick, time, pressure);
|
|
||||||
list_for_each_safe(list_flow, tmp, &graph.flow_sorted) {
|
list_for_each_safe(list_flow, tmp, &graph.flow_sorted) {
|
||||||
cur = list_entry(list_flow, struct valve, flow_sorted);
|
cur = list_entry(list_flow, struct valve, flow_sorted);
|
||||||
int d = DIST(pos->index, cur->index);
|
int d = DIST(pos->index, cur->index);
|
||||||
@@ -191,7 +174,7 @@ static struct valve *eval(int _depth, struct valve *pos, int depth, int pick, in
|
|||||||
if (depth > 0) {
|
if (depth > 0) {
|
||||||
/* do not use list_del() here, to preserve prev/next pointers */
|
/* do not use list_del() here, to preserve prev/next pointers */
|
||||||
__list_del_entry(list_flow);
|
__list_del_entry(list_flow);
|
||||||
sub = eval(_depth + 2, cur, depth - 1, pick - 1, time - d - 1, pressure + pos->rate);
|
sub = eval(_depth + 1, cur, depth - 1, pick, time - d - 1, pressure + pos->rate);
|
||||||
list_flow->prev->next = list_flow;
|
list_flow->prev->next = list_flow;
|
||||||
list_flow->next->prev = list_flow;
|
list_flow->next->prev = list_flow;
|
||||||
} else {
|
} else {
|
||||||
@@ -210,75 +193,177 @@ static struct valve *eval(int _depth, struct valve *pos, int depth, int pick, in
|
|||||||
if (best) {
|
if (best) {
|
||||||
best->evalflow = max;
|
best->evalflow = max;
|
||||||
PAD3; log(3, "EVAL returning best [%s] eval=%d\n", best->val.str, max);
|
PAD3; log(3, "EVAL returning best [%s] eval=%d\n", best->val.str, max);
|
||||||
//best->evaltime = time - (d + 2);
|
|
||||||
}
|
}
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __unused void permute_prepare(int n)
|
/**
|
||||||
|
* eval() - eval possible moves from @flow_sorted list.
|
||||||
|
* @_depth: recursivity depth (for debug only, TODO: remove).
|
||||||
|
* @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.
|
||||||
|
* @pressure: total pressure per time unit so far.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
static struct valve *eval2(int _depth, struct worker *worker, int pick, int pressure)
|
||||||
{
|
{
|
||||||
struct valve *cur;
|
struct valve *cur, *best = NULL, *sub;
|
||||||
INIT_LIST_HEAD(&graph.permute);
|
struct list_head *list_flow, *tmp;
|
||||||
list_for_each_entry(cur, &graph.flow_sorted, flow_sorted) {
|
int _pick = pick, val = 0, val1, max = 0;
|
||||||
if (!n--)
|
|
||||||
break;
|
PAD3; log(3, "EVAL _depth=%d pos=%d[%s] depth=%d pick=%d time=%d pressure=%d\n",
|
||||||
list_add_tail(&cur->permute, &graph.permute);
|
_depth, worker->pos->index, worker->pos->val.str, worker->depth,
|
||||||
|
pick, worker->time, pressure);
|
||||||
|
list_for_each_safe(list_flow, tmp, &graph.flow_sorted) {
|
||||||
|
cur = list_entry(list_flow, struct valve, flow_sorted);
|
||||||
|
int d = DIST(worker->pos->index, cur->index),
|
||||||
|
remain = worker->time - (d + 1);
|
||||||
|
PAD4; log(4, "dist(%s,%s) = %d\n", worker->pos->val.str, cur->val.str, d);
|
||||||
|
if (!--_pick) {
|
||||||
|
PAD4; log(4, "pick exhausted\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (remain < 1) {
|
||||||
|
PAD4; log(4, "time exhausted\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
val = remain * cur->rate;
|
||||||
|
PAD4; log(4, "val=%d\n", val);
|
||||||
|
|
||||||
|
if (worker->depth > 0) {
|
||||||
|
struct worker w = {
|
||||||
|
.pos = cur,
|
||||||
|
.depth = worker->depth - 1,
|
||||||
|
.time = remain
|
||||||
|
};
|
||||||
|
/* do not use list_del() here, to preserve prev/next pointers */
|
||||||
|
__list_del_entry(list_flow);
|
||||||
|
sub = eval2(_depth + 1, &w, pick, pressure + worker->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, "eval2(%s->%s)= %5d = %d + %d", worker->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");
|
||||||
}
|
}
|
||||||
|
if (best) {
|
||||||
|
best->evalflow = max;
|
||||||
|
best->worker = 0; /* FIXME */
|
||||||
|
PAD3; log(3, "EVAL returning best [%s] worker=%d eval=%d\n", best->val.str,
|
||||||
|
best->worker, max);
|
||||||
|
}
|
||||||
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* permute() - get next permutation in graph.permute list.
|
* eval() - eval possible moves from @flow_sorted list.
|
||||||
* @n: permutation number (0 first first one)
|
* @_depth: recursivity depth (for debug only, TODO: remove).
|
||||||
|
* @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.
|
||||||
|
* @pressure: total pressure per time unit so far.
|
||||||
*
|
*
|
||||||
* Construct next permutation in graph.permute list, following the
|
* Find the "best" next move by evaluating up to @depth moves, using only the
|
||||||
* "lexicographic order algorithm" :
|
* first @pick elements in @flow_sorted list, and within @time remaining time.
|
||||||
* https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order
|
|
||||||
*
|
*
|
||||||
* Before first call for a given graph.permute list:
|
* @depth and @picked may be linked, for instance to fully explore the first N
|
||||||
* 1) the graph.flow_sorted should be (decreasing) sorted.
|
* possibilities in @flow_sorted with a N depth.
|
||||||
* 2) permute_prepare() should have been called.
|
*
|
||||||
* @Return: 0 if no more permutation, 1 otherwise.
|
* @Return: the current position eval.
|
||||||
*/
|
*/
|
||||||
static __unused int permute(int n)
|
static struct valve *eval3(int _depth, struct worker *worker, int pick, int pressure)
|
||||||
{
|
{
|
||||||
struct valve *last, *first, *k, *l;
|
struct valve *cur, *best = NULL, *sub;
|
||||||
|
struct list_head *list_flow, *tmp;
|
||||||
|
int _pick = pick, val = 0, val1, max = 0, bestworker;
|
||||||
|
|
||||||
if (!n)
|
PAD3; log_f(3, "EVAL _depth=%d w0={ pos=%d[%s] depth=%d time=%d } w1={ pos=%d[%s] depth=%d time=%d } pick=%d pressure=%d \n",
|
||||||
return 1;
|
_depth,
|
||||||
last = list_last_entry(&graph.permute, struct valve, permute);
|
worker[0].pos->index, worker[0].pos->val.str,
|
||||||
first = list_first_entry(&graph.permute, struct valve, permute);
|
worker[0].depth, worker[0].time,
|
||||||
l = last;
|
worker[1].pos->index, worker[1].pos->val.str,
|
||||||
k = list_prev_entry(l, permute);
|
worker[1].depth, worker[1].time,
|
||||||
|
pick, pressure);
|
||||||
while (k->rate <= l->rate) {
|
list_for_each_safe(list_flow, tmp, &graph.flow_sorted) {
|
||||||
if ((l = k) == first)
|
cur = list_entry(list_flow, struct valve, flow_sorted);
|
||||||
return 0;
|
int nworkers = worker[0].pos->index == worker[1].pos->index? 1: 2;
|
||||||
k = list_prev_entry(l, permute);
|
if (!--_pick) {
|
||||||
}
|
PAD4; log(4, "pick exhausted\n");
|
||||||
l = last;
|
|
||||||
while (l != k && k->rate <= l->rate) {
|
|
||||||
l = list_prev_entry(l, permute);
|
|
||||||
}
|
|
||||||
printf("found k=%d l=%d ", k->rate, l->rate);
|
|
||||||
list_swap(&l->permute, &k->permute);
|
|
||||||
struct list_head *anchor = l->permute.next, *cur, *tmp;
|
|
||||||
list_for_each_prev_safe(cur, tmp, &graph.permute) {
|
|
||||||
if (cur == anchor)
|
|
||||||
break;
|
break;
|
||||||
list_move_tail(cur, anchor);
|
}
|
||||||
}
|
for (int _w = 0; _w < nworkers; ++_w) {
|
||||||
return 1;
|
struct worker *w = worker + _w;
|
||||||
}
|
int d = DIST(w->pos->index, cur->index);
|
||||||
|
int remain = w->time - (d + 1);
|
||||||
|
PAD3; log(3, "worker %d/%d ", _w + 1, nworkers );
|
||||||
|
PAD3; log(3, "dist(%s,%s) = %d ", w->pos->val.str, cur->val.str, d);
|
||||||
|
if (remain < 1) {
|
||||||
|
PAD3; log(4, "time exhausted\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
val = remain * cur->rate;
|
||||||
|
PAD3; log(3, "--> val=%d\n", val);
|
||||||
|
|
||||||
|
if (w->depth > 0) {
|
||||||
|
struct worker _tmp = *w;
|
||||||
|
w->pos = cur;
|
||||||
|
w->depth--;
|
||||||
|
w->time = remain;
|
||||||
|
/* do not use list_del() here, to preserve prev/next pointers */
|
||||||
|
__list_del_entry(list_flow);
|
||||||
|
sub = eval3(_depth + 1, worker, pick, pressure + w->pos->rate);
|
||||||
|
list_flow->prev->next = list_flow;
|
||||||
|
list_flow->next->prev = list_flow;
|
||||||
|
*w = _tmp;
|
||||||
|
} else {
|
||||||
|
sub = NULL;
|
||||||
|
}
|
||||||
|
val1 = sub? sub->evalflow: 0;
|
||||||
|
PAD3; log(3, "eval3(%s->%s)= %5d = %d + %d", w->pos->val.str, cur->val.str,
|
||||||
|
val+val1, val, val1);
|
||||||
|
if (val + val1 > max) {
|
||||||
|
max = val + val1;
|
||||||
|
best = cur;
|
||||||
|
bestworker = _w;
|
||||||
|
log(3, " NEW MAX !");
|
||||||
|
}
|
||||||
|
log(3, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best) {
|
||||||
|
best->evalflow = max;
|
||||||
|
best->worker = bestworker; /* FIXME */
|
||||||
|
PAD3; log(3, "EVAL returning best [%s] worker=%d eval=%d\n", best->val.str,
|
||||||
|
best->worker, max);
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
static struct valve *find_valve(union val val, int ntunnels, int rate)
|
static struct valve *find_valve(union val val, int ntunnels, int rate)
|
||||||
{
|
{
|
||||||
struct valve *cur;
|
struct valve *cur;
|
||||||
uint hash = val.val, bucket = hash_32(hash, HBITS);
|
|
||||||
|
|
||||||
log_f(3, "val=%s ntunnels=%d rate=%d h=%u b=%d\n", val.str, ntunnels,
|
log_f(3, "val=%s ntunnels=%d rate=%d\n", val.str, ntunnels, rate);
|
||||||
rate, hash, bucket);
|
list_for_each_entry(cur, &graph.index_sorted, index_sorted) {
|
||||||
hlist_for_each_entry(cur, &hasht_valves[bucket], hlist) {
|
//log(3, "\tcomparing with found, addr=%p\n", cur);
|
||||||
if (cur->val.val == val.val) {
|
if (cur->val.val == val.val) {
|
||||||
log(3, "\tfound, addr=%p\n", cur);
|
log(3, "\tfound, addr=%p\n", cur);
|
||||||
if (ntunnels)
|
if (ntunnels)
|
||||||
@@ -290,13 +375,13 @@ static struct valve *find_valve(union val val, int ntunnels, int rate)
|
|||||||
cur->val.val = val.val;
|
cur->val.val = val.val;
|
||||||
cur->ntunnels = 0;
|
cur->ntunnels = 0;
|
||||||
cur->state = CLOSED;
|
cur->state = CLOSED;
|
||||||
|
cur->worker = -1;
|
||||||
cur->evalflow = cur->playedflow = 0;
|
cur->evalflow = cur->playedflow = 0;
|
||||||
cur->evaltime = cur->playedtime = 30;
|
cur->evaltime = cur->playedtime = 30;
|
||||||
INIT_LIST_HEAD(&cur->index_sorted);
|
INIT_LIST_HEAD(&cur->index_sorted);
|
||||||
INIT_LIST_HEAD(&cur->flow_sorted);
|
INIT_LIST_HEAD(&cur->flow_sorted);
|
||||||
INIT_LIST_HEAD(&cur->permute);
|
INIT_LIST_HEAD(&cur->eval);
|
||||||
INIT_LIST_HEAD(&cur->played);
|
INIT_LIST_HEAD(&cur->played);
|
||||||
hlist_add_head(&cur->hlist, &hasht_valves[bucket]);
|
|
||||||
log(3, "\talloc new, addr=%p\n", cur);
|
log(3, "\talloc new, addr=%p\n", cur);
|
||||||
init:
|
init:
|
||||||
if (ntunnels) {
|
if (ntunnels) {
|
||||||
@@ -308,20 +393,28 @@ end:
|
|||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *getnth(char *buf, int n)
|
/**
|
||||||
|
* nthtok - get nth token fron string.
|
||||||
|
* @buf: buffer to parse.
|
||||||
|
* @sep: separators string.
|
||||||
|
* @n: token number (0: first token).
|
||||||
|
*
|
||||||
|
* @Return: pointer to token.
|
||||||
|
*/
|
||||||
|
static char *nthtok(char *buf, const char *sep, int n)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
for (; n >= 0; n--) {
|
for (; n >= 0; n--) {
|
||||||
ret = strtok(buf, SEP);
|
ret = strtok(buf, sep);
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SEP " ,;="
|
||||||
static struct graph *parse()
|
static struct graph *parse()
|
||||||
{
|
{
|
||||||
int index = 0, ntunnels;
|
int index = 0, ntunnels;
|
||||||
ulong bucket;
|
|
||||||
size_t alloc = 0;
|
size_t alloc = 0;
|
||||||
ssize_t buflen;
|
ssize_t buflen;
|
||||||
char *buf = NULL, *tok;
|
char *buf = NULL, *tok;
|
||||||
@@ -331,11 +424,13 @@ static struct graph *parse()
|
|||||||
|
|
||||||
while ((buflen = getline(&buf, &alloc, stdin)) > 0) {
|
while ((buflen = getline(&buf, &alloc, stdin)) > 0) {
|
||||||
buf[--buflen] = 0;
|
buf[--buflen] = 0;
|
||||||
strncpy(cur.str, getnth(buf, 1), sizeof(cur.str));
|
/* valve name */
|
||||||
|
strncpy(cur.str, nthtok(buf, SEP, 1), sizeof(cur.str));
|
||||||
|
|
||||||
//printf("valve=%s ", tok);
|
//printf("valve=%s ", tok);
|
||||||
rate = atoi(getnth(NULL, 3));
|
rate = atoi(nthtok(NULL, SEP, 3));
|
||||||
//printf("rate=%s ", tok);
|
//printf("rate=%s ", tok);
|
||||||
tok = getnth(NULL, 4);
|
tok = nthtok(NULL, SEP, 4);
|
||||||
ntunnels = (buf + buflen - tok) / 4 + 1;
|
ntunnels = (buf + buflen - tok) / 4 + 1;
|
||||||
v1 = find_valve(cur, ntunnels, rate);
|
v1 = find_valve(cur, ntunnels, rate);
|
||||||
v1->index = index++;
|
v1->index = index++;
|
||||||
@@ -360,8 +455,6 @@ static struct graph *parse()
|
|||||||
graph.openable |= (1 << v1->index);
|
graph.openable |= (1 << v1->index);
|
||||||
//printf("->%#lx", graph.openable);
|
//printf("->%#lx", graph.openable);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
graph.nzero++;
|
|
||||||
}
|
}
|
||||||
//printf("lead=%s ntunnels=%d ", tok, ntunnels);
|
//printf("lead=%s ntunnels=%d ", tok, ntunnels);
|
||||||
do {
|
do {
|
||||||
@@ -369,23 +462,21 @@ static struct graph *parse()
|
|||||||
v2 = find_valve(link, 0, 0);
|
v2 = find_valve(link, 0, 0);
|
||||||
*(v1->tunnels + v1->ntunnels++) = v2;
|
*(v1->tunnels + v1->ntunnels++) = v2;
|
||||||
//printf(",%s", tok);
|
//printf(",%s", tok);
|
||||||
} while ((tok = getnth(NULL, 0)));
|
} while ((tok = nthtok(NULL, SEP, 0)));
|
||||||
//printf("\n");
|
//printf("\n");
|
||||||
}
|
}
|
||||||
graph.aa = find_valve((union val) { .str="AA" }, 0, 0);
|
graph.aa = find_valve((union val) { .str="AA" }, 0, 0);
|
||||||
/* build array of indexed valves */
|
/* build array of indexed valves */
|
||||||
graph.indexed = calloc(graph.nvalves, sizeof(struct valve *));
|
graph.indexed_all = calloc(graph.nvalves, sizeof(struct valve *));
|
||||||
index = 0;
|
list_for_each_entry(v1, &graph.index_sorted, index_sorted) {
|
||||||
hash_for_each(hasht_valves, bucket, v1, hlist) {
|
graph.indexed_all[v1->index] = v1;
|
||||||
graph.indexed[v1->index] = v1;
|
|
||||||
index++;
|
|
||||||
}
|
}
|
||||||
return &graph;
|
return &graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_neighbour(int i, int j)
|
static int is_neighbour(int i, int j)
|
||||||
{
|
{
|
||||||
struct valve *v1 = graph.indexed[i], *v2 = graph.indexed[j];
|
struct valve *v1 = graph.indexed_all[i], *v2 = graph.indexed_all[j];
|
||||||
for (int i = 0; i < v1->ntunnels; ++i)
|
for (int i = 0; i < v1->ntunnels; ++i)
|
||||||
if (v1->tunnels[i]->val.val == v2->val.val)
|
if (v1->tunnels[i]->val.val == v2->val.val)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -405,10 +496,8 @@ static void build_distances()
|
|||||||
else
|
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//print_valves();
|
|
||||||
|
|
||||||
/* get all distances using Floyd-Warshall
|
/* get all distances using Floyd-Warshall
|
||||||
* see https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
|
* see https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
|
||||||
@@ -431,56 +520,90 @@ static void build_distances()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//static ulong do_1(struct valve *cur, int min, int pressure)
|
static void print_played()
|
||||||
//{
|
{
|
||||||
// ulong tmp;
|
struct valve *p;
|
||||||
|
int total = 0;
|
||||||
|
for (int w = 0; w < 2; ++w) {
|
||||||
|
int remain = 26, i = 1;
|
||||||
|
struct valve *prev = graph.aa;
|
||||||
|
i = 1;
|
||||||
|
printf("played by %d:\n", w);
|
||||||
|
list_for_each_entry(p, &graph.played[w], played) {
|
||||||
|
printf("%2d: %s, ", i, p->val.str);
|
||||||
|
remain -= DIST(p->index, prev->index) + 1;
|
||||||
|
total += p->rate * remain;
|
||||||
|
printf("dist=%d remain=%d total=%d", DIST(p->index, prev->index), remain,
|
||||||
|
total);
|
||||||
|
printf("\n");
|
||||||
|
i++;
|
||||||
|
prev = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (cur->state == CLOSED) {
|
static void doit()
|
||||||
|
{
|
||||||
|
struct worker w[2];
|
||||||
|
w[0].pos = w[1].pos = graph.aa;
|
||||||
|
w[0].depth = w[1].depth = 3;
|
||||||
|
w[0].time = w[1].time = 26;
|
||||||
|
/* struct worker w[2] = {
|
||||||
|
{ .pos = graph.aa, .depth = 5, .time = 30 },
|
||||||
|
{ .pos = graph.aa, .depth = 5, .time = 30 },
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
struct valve *best;
|
||||||
|
|
||||||
//}
|
//list_add_tail(&w[0].pos->played, &graph.played[0]);
|
||||||
//}
|
//list_add_tail(&w[1].pos->played, &graph.played[1]);
|
||||||
|
//while ()
|
||||||
|
while ((best = eval3(0, w, 12, 0))) {
|
||||||
|
list_del(&best->flow_sorted);
|
||||||
|
list_add_tail(&best->played, &graph.played[best->worker]);
|
||||||
|
w[best->worker].time -= DIST(w[best->worker].pos->index, best->index) + 1;
|
||||||
|
w[best->worker].pos = best;
|
||||||
|
print_played();
|
||||||
|
}
|
||||||
|
|
||||||
//static union val start = { .str = "AA" };
|
}
|
||||||
|
|
||||||
static ulong part1()
|
static ulong part1()
|
||||||
{
|
{
|
||||||
ulong res = 1;
|
ulong res = 1;
|
||||||
//struct valve *cur = graph.aa;
|
struct worker w[2];
|
||||||
|
w[0].pos = w[1].pos = graph.aa;
|
||||||
|
w[0].depth = w[1].depth = 5;
|
||||||
|
w[0].time = w[1].time = 30;
|
||||||
|
|
||||||
printf("part1\n");
|
printf("part 1\n");
|
||||||
build_distances();
|
|
||||||
print_valves();
|
|
||||||
|
|
||||||
puts("zob1");
|
|
||||||
eval(0, graph.aa, 7, 7, 30, 0);
|
eval(0, graph.aa, 7, 7, 30, 0);
|
||||||
puts("zob2");
|
eval2(0, &w[0], 4, 0);
|
||||||
/*
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ulong part2()
|
static ulong part2()
|
||||||
{
|
{
|
||||||
ulong res = 2;
|
ulong res = 1;
|
||||||
|
//struct worker w = {
|
||||||
|
// .pos = graph.aa,
|
||||||
|
// .depth = 4,
|
||||||
|
// .time = 30
|
||||||
|
//};
|
||||||
|
printf("part 2\n");
|
||||||
|
//while ()
|
||||||
|
//eval2(0, &w, 7, 0);
|
||||||
|
doit();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int ac, char **av)
|
int main(int ac, char **av)
|
||||||
{
|
{
|
||||||
int part = parseargs(ac, av);
|
int part = parseargs(ac, av);
|
||||||
pool_valve = pool_create("valve", 512, sizeof(struct valve));
|
pool_valve = pool_create("valve", 512, sizeof(struct valve));
|
||||||
parse();
|
parse();
|
||||||
|
build_distances();
|
||||||
|
print_valves();
|
||||||
printf("%s: res=%lu\n", *av, part == 1? part1(): part2());
|
printf("%s: res=%lu\n", *av, part == 1? part1(): part2());
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user