mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-30 15:39:54 +00:00
262 lines
6.5 KiB
C++
262 lines
6.5 KiB
C++
|
// algorithms to compute distances between mycells, and an implementation of tally counter
|
||
|
|
||
|
namespace dhrg {
|
||
|
|
||
|
int segmentcount;
|
||
|
|
||
|
struct qtybox {
|
||
|
int minv, maxv;
|
||
|
int qty[BOXSIZE];
|
||
|
int& operator [] (int i) {
|
||
|
if(i>=BOXSIZE)
|
||
|
throw hr_exception("not enough memory reserved -- increase BOXSIZE constant");
|
||
|
return qty[i];
|
||
|
}
|
||
|
qtybox() { for(int i=0; i<BOXSIZE; i++) qty[i] = 0; minv = BOXSIZE; maxv = 0; }
|
||
|
};
|
||
|
|
||
|
struct segment {
|
||
|
mycell *left;
|
||
|
mycell *right;
|
||
|
segment *nextleft;
|
||
|
segment *nextright;
|
||
|
segment *parent;
|
||
|
segment *nextchild;
|
||
|
segment *firstchild;
|
||
|
qtybox qty;
|
||
|
int seen;
|
||
|
segment() { seen = -1; segmentcount++; }
|
||
|
~segment() { segmentcount--; }
|
||
|
};
|
||
|
|
||
|
segment *getsegment(mycell *pleft, mycell *pright, int setid, bool docreate = true) {
|
||
|
pleft->build(); pright->build();
|
||
|
segment *c1 = pleft->byleft[setid];
|
||
|
while(c1) {
|
||
|
if(c1->right == pright) {
|
||
|
return c1;
|
||
|
}
|
||
|
c1 = c1 -> nextleft;
|
||
|
}
|
||
|
if(!docreate) return c1;
|
||
|
segment *p = new segment;
|
||
|
p->left = pleft;
|
||
|
p->right = pright;
|
||
|
p->nextleft = pleft->byleft[setid]; pleft->byleft[setid] = p;
|
||
|
p->nextright = pright->byright[setid]; pright->byright[setid] = p;
|
||
|
p->nextchild = NULL; p->firstchild = NULL;
|
||
|
if(pleft->leftparent) {
|
||
|
p->parent = getsegment(pleft->leftparent, pright->rightparent, setid);
|
||
|
p->nextchild = p->parent->firstchild;
|
||
|
p->parent->firstchild = p;
|
||
|
}
|
||
|
else
|
||
|
p->parent = NULL;
|
||
|
#ifdef LONG_BRACKETS
|
||
|
if(pleft != pright) {
|
||
|
pleft = pleft->grightsibling();
|
||
|
int slen = 0;
|
||
|
while(pleft != pright) {
|
||
|
segmentlist *n = new segmentlist;
|
||
|
slen++;
|
||
|
n->s = p;
|
||
|
n->next = pleft->bracketing;
|
||
|
pleft->bracketing = n;
|
||
|
pleft = pleft->grightsibling();
|
||
|
}
|
||
|
if(slen > 10) throw hr_exception("bad bracket");
|
||
|
}
|
||
|
#endif
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
ll tally[MAXDIST];
|
||
|
|
||
|
ll *whichtally = tally;
|
||
|
|
||
|
vector<segment*> acknowledged;
|
||
|
|
||
|
void tallybox(qtybox& box, int d, int mul) {
|
||
|
for(int i=box.minv; i<box.maxv; i++)
|
||
|
whichtally[i+d] += box[i] * mul;
|
||
|
}
|
||
|
|
||
|
void acknowledge(segment *p, int d) {
|
||
|
if(!p) return;
|
||
|
if(p->seen == -1) {
|
||
|
p->seen = d;
|
||
|
acknowledged.emplace_back(p);
|
||
|
}
|
||
|
else if(p->seen > d)
|
||
|
p->seen = d;
|
||
|
}
|
||
|
|
||
|
void acknowledgments(int mul) {
|
||
|
for(segment* p: acknowledged) {
|
||
|
tallybox(p->qty, p->seen, mul);
|
||
|
segment *p2 = p->parent;
|
||
|
int dist = 1;
|
||
|
while(p2) {
|
||
|
if(p2->seen != -1) {
|
||
|
tallybox(p->qty, p2->seen+dist, -mul);
|
||
|
break;
|
||
|
}
|
||
|
p2=p2->parent; dist++;
|
||
|
}
|
||
|
p->seen = -1;
|
||
|
}
|
||
|
acknowledged.clear();
|
||
|
}
|
||
|
|
||
|
void trim(qtybox& b) {
|
||
|
while(b.minv < b.maxv && !b[b.minv]) b.minv++;
|
||
|
if(b.minv == b.maxv) b.minv = BOXSIZE, b.maxv = 0;
|
||
|
else while(!b[b.maxv-1]) b.maxv--;
|
||
|
}
|
||
|
|
||
|
void add_to_set(mycell *c, int mul, int setid) {
|
||
|
// assume mul does not equal 0 !
|
||
|
segment *p = getsegment(c, c, setid);
|
||
|
int d = 0;
|
||
|
while(p) {
|
||
|
p->qty[d] += mul;
|
||
|
if(p->qty[d]) {
|
||
|
p->qty.minv = min(p->qty.minv, d);
|
||
|
p->qty.maxv = max(p->qty.maxv, d+1);
|
||
|
}
|
||
|
else trim(p->qty);
|
||
|
d++; p = p->parent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int segmentdist(segment *p1, segment *p2, int d);
|
||
|
|
||
|
void build_ack(mycell *c, int setid) {
|
||
|
segment *p = getsegment(c, c, setid);
|
||
|
int d = 0;
|
||
|
|
||
|
#ifdef LONG_BRACKETS
|
||
|
segmentlist *brackets = c->bracketing;
|
||
|
while(brackets) {
|
||
|
acknowledge(brackets->s, 0);
|
||
|
brackets = brackets->next;
|
||
|
}
|
||
|
#else
|
||
|
acknowledge(getsegment(c->gleftsibling(), c->grightsibling(), setid, false), 0); // larger!
|
||
|
#endif
|
||
|
|
||
|
while(p) {
|
||
|
if(p->left != p->right) {
|
||
|
mycell *cc = p->left->grightsibling();
|
||
|
while(cc != p->right) {
|
||
|
acknowledge(getsegment(cc, cc, setid, false), d);
|
||
|
cc = cc->grightsibling();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int sl = cgi.expansion->sibling_limit;
|
||
|
|
||
|
mycell *mright = p->right;
|
||
|
mycell *mright0 = mright;
|
||
|
for(int u=0; u<=sl; u++) {
|
||
|
mright->build();
|
||
|
segment *mrightbyleft = mright->byleft[setid];
|
||
|
while(mrightbyleft) {
|
||
|
if(u == 3 && p->right->rightparent == mright->leftparent)
|
||
|
acknowledge(mrightbyleft, d+2);
|
||
|
else if(u > 3)
|
||
|
acknowledge(mrightbyleft, segmentdist(getsegment(mright0, mright0, setid), mrightbyleft, d));
|
||
|
else
|
||
|
acknowledge(mrightbyleft, d+u);
|
||
|
mrightbyleft = mrightbyleft->nextleft;
|
||
|
}
|
||
|
mright=mright->grightsibling();
|
||
|
}
|
||
|
|
||
|
mycell *mleft = p->left;
|
||
|
mycell *mleft0 = mleft;
|
||
|
for(int u=0; u<=sl; u++) {
|
||
|
mleft->build();
|
||
|
segment *mleftbyright = mleft->byright[setid];
|
||
|
while(mleftbyright) {
|
||
|
if(u == 3 && p->left -> leftparent == mleft->rightparent)
|
||
|
acknowledge(mleftbyright, d+2);
|
||
|
else if(u > 3)
|
||
|
acknowledge(mleftbyright, segmentdist(mleftbyright, getsegment(mleft0, mleft0, setid), d));
|
||
|
else
|
||
|
acknowledge(mleftbyright, d+u);
|
||
|
mleftbyright = mleftbyright->nextright;
|
||
|
}
|
||
|
mleft=mleft->gleftsibling();
|
||
|
}
|
||
|
|
||
|
// go forth
|
||
|
d++; p = p->parent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void add_to_tally(mycell *c, int mul, int setid) {
|
||
|
build_ack(c, setid);
|
||
|
acknowledgments(mul);
|
||
|
}
|
||
|
|
||
|
bool is_insegment(mycell *c, segment *p) {
|
||
|
mycell *p1 = p->left;
|
||
|
mycell *p2 = p->right;
|
||
|
while(true) {
|
||
|
if(c == p1) return true;
|
||
|
if(p1 == p2) return false;
|
||
|
p1 = p1->grightsibling();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int segmentdist(segment *p1, segment *p2, int d) {
|
||
|
|
||
|
if(p1->left == p1->right && is_insegment(p1->left, p2)) return d;
|
||
|
if(p2->left == p2->right && is_insegment(p2->left, p1)) return d;
|
||
|
|
||
|
int best = 1000000;
|
||
|
|
||
|
while(true) {
|
||
|
if(d >= best) return best;
|
||
|
mycell *mright;
|
||
|
|
||
|
int sl = cgi.expansion->sibling_limit;
|
||
|
|
||
|
mright = p1->right;
|
||
|
for(int u=0; u<=sl; u++) {
|
||
|
mright->build();
|
||
|
if(mright == p2->left) best = min(best, d+u);
|
||
|
mright = mright->grightsibling();
|
||
|
}
|
||
|
|
||
|
mright = p2->right;
|
||
|
for(int u=0; u<=sl; u++) {
|
||
|
mright->build();
|
||
|
if(mright == p1->left) best = min(best, d+u);
|
||
|
mright = mright->grightsibling();
|
||
|
}
|
||
|
|
||
|
p1 = p1->parent;
|
||
|
p2 = p2->parent;
|
||
|
d += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int quickdist(mycell *c1, mycell *c2, int setid=0) {
|
||
|
int d = 0;
|
||
|
c1->build();
|
||
|
c2->build();
|
||
|
int d1 = c1->lev;
|
||
|
int d2 = c2->lev;
|
||
|
segment *p1 = getsegment(c1, c1, setid);
|
||
|
segment *p2 = getsegment(c2, c2, setid);
|
||
|
|
||
|
while(d1>d2) { p1 = p1->parent; d1--; d++; }
|
||
|
while(d2>d1) { p2 = p2->parent; d2--; d++; }
|
||
|
|
||
|
return segmentdist(p1, p2, d);
|
||
|
}
|
||
|
|
||
|
}
|