// Banach-Tarski animation in RogueViz. // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details // good parameters: -fixx 10 -W Cros -bantar_anim // use -bantar_map to see how it works namespace rogueviz { namespace banachtarski { bool on; typedef vector cwpath; cwpath invertpath(cwpath p) { cwpath res; for(int i=0; i testlist; map parent; bool test_uniq(cellwalker cw, int z, int lev, cell *par) { if(testlist.count(cw.at)) return false; testlist.insert(cw.at); if(par) parent[cw.at] = par; if(celldist(cw.at) > 9) return true; /* if(cw.at->wall == waSea) { printf("test_uniq failed\n"); cw.at->wall = waEternalFire; } cw.at->wall = waSea; */ if(lev) for(int y=0; y<4; y++) if(y != z) if(!test_uniq(trace(cw, gens[y]), y^2, lev-1, cw.at)) return false; return true; } template void recursively(cell *c, cell *c1, const T& t) { t(c); for(int i=0; itype; i++) if(c->move(i) && c->c.spin(i) == 0 && c->move(i) != c1) recursively(c->move(i), c, t); } vector allcells; set seen; struct cellinfo { cell *c; int gid; int spdist; eWall w; eItem it; eLand land; eMonster mo; vector way; cwpath pinv; }; map infos; int cidd = 0; int more = 5; void debugpath(vector& way, cwpath& pinv) { printf("pinv:"); for(int i: pinv) printf(" %d", i); printf("\n"); cellwalker cw = cwt; cellwalker last_cw = cw; int lastd = 0; printf("way:"); for(int i: way) { cellwalker cw2 = trace(cw, pinv); printf(" [%d]", lastd = celldist(cw2.at)); printf(" %d", i); last_cw = cw; cw = trace(cw, gens[i]); } cellwalker cw2 = trace(cw, pinv); int curd; printf(" [%d]", curd = celldist(cw2.at)); printf("\n"); if(lastd == 10 && curd == 2) { int b = way.back(); way.pop_back(); printf("CW:"); for(int w: way) for(int wp: gens[w]) printf(" %d", wp); printf(" {"); for(int wp: gens[b]) printf(" %d", wp); printf(" }"); for(int i: pinv) printf(" %d", i); way.push_back(b); printf("\n"); } } void recursive_paint(cwpath& pinv, vector& way, int noway) { cellwalker cw = cwt; for(int i: way) cw = trace(cw, gens[i]); cw = trace(cw, pinv); cell *c = cw.at; /* if(cidd == 1 && way == vector{1,2,3}) c->item = itPirate; */ if(seen.count(c)) { printf("seen error [%d]\n", celldist(c)); debugpath(way, pinv); debugpath(infos[c].way, infos[c].pinv); return; } seen.insert(c); int hsh = 7; for(int w: way) hsh = 11301 * hsh + w * 37121; bool all0 = true; for(int w: way) if(w) all0 = false; int gid; /* if(d) c->landparam = 0x202020; else */ if(all0 || way[0] == 2) gid = 0; else if(way[0] == 0) gid = 1; else if(way[0] == 1) gid = 2; else if(way[0] == 3) gid = 3; else gid = 3; infos[c] = cellinfo{c, gid, 0}; infos[c].way = way; infos[c].pinv = pinv; // c->landparam ^= ((isize(way)&1) * 0x3F3F3F); // c->landparam = hsh; // d * 5 + 256 * (hsh&0xFFFF) + 0x400000; if(cidd>112899) c->landparam = 0x101010; // c->landparam = cidd * 0x1241C3; if(celldist(c) <= 11+more) for(int i=0; i<4; i++) if(i != noway) { vector newway = {i}; for(int ii: way) newway.push_back(ii); recursive_paint(pinv, newway, i^2); } } bool once = true; cwpath path_to(cell *c, int dir = 0) { cwpath p; cellwalker cw(c, dir); while(cw != cwt) { if(celldist((cw+wstep).at) < celldist(cw.at)) p.push_back(0), cw += wstep; else { if(p.size() && p.back()) p.back()++; else p.push_back(1); cw += 1; } } return p; } void bantar_note(cell *c) { if(seen.count(c)) return; cwpath pinv = invertpath(path_to(c)); vector way; recursive_paint(pinv, way, 4); cidd++; } using bantar_config = pair; tuple quality(bantar_config cp) { hyperpoint h1 = tC0(ggmatrix(cp.first)); hyperpoint h2 = tC0(ggmatrix(cp.second)); return make_tuple(hdist0(h1) * hdist0(h2), h2[1] > 0, abs(h2[0] / h2[1])); } int notry = 0; void bantar() { if(!on) return; cwt = cellwalker(currentmap->gamestart(), 0); viewctr = heptspin(cwt.at->master, 0); infos.clear(); vector genchoices; int lnotry = notry; { celllister clgen(cwt.at, 4, 1000000, NULL); for(cell *c1: clgen.lst) if(c1->type == S7) for(cell *c2: clgen.lst) if(c2->type == S7) genchoices.emplace_back(c1, c2); stable_sort(genchoices.begin(), genchoices.end(), [] (const bantar_config b1, const bantar_config b2) { return quality(b1) < quality(b2); }); for(bantar_config bc: genchoices) { if(get<0>(quality(bc)) >= 4) exit(1); for(int i=0; i(q), get<2>(q), celldist(bc.first), celldist(bc.second), tres); lnotry--; if(lnotry <= 0) goto picked; } // if(tres) goto picked; } } } picked: /* if(S7 == 8) { gens[1] = {0,4,0,4}; gens[0] = {2,0,2}; } else { gens[0] = {0,4,0,4}; gens[1] = {2,0,4,0,2}; } gens[2] = invertpath(gens[0]); gens[3] = invertpath(gens[1]); */ for(int i=0; i<4; i++) bttargets[i] = trace(cwt, gens[i]); celllister cl(cwt.at, 8+more, 1000000, NULL); // recursively(cwt.at, NULL, [] (cell *c) { allcells.push_back(c); } ); for(cell* c: cl.lst) bantar_note(c); for(cell *c: cl.lst) if(infos.count(c) && infos[c].gid == 0) forCellEx(c2, c) if(infos.count(c2) && infos[c2].gid != 0) c->bardir = NOBARRIERS; for(int it=0; it spdist; int curpart; /* bool hidebad(cell *c, const transmatrix& V) { if(c->wparam != curpart && curpart < 4) return true; return false; } */ heptspin cth(cellwalker cw) { return heptspin(cw.at->master, cw.spin, cw.mirrored); } ld alphaof(hyperpoint h) { return atan2(h[1], h[0]); } struct bantar_special { polytodraw *actual; ld xpos, ypos; }; #define ForInfos for(auto& cci: infos) void bantar_frame() { setGLProjection(); ForInfos cci.second.w = cci.second.c->wall, cci.second.it = cci.second.c->item, cci.second.mo = cci.second.c->monst, cci.second.land = cci.second.c->land, cci.second.c->wparam = cci.second.gid; calcparam(); stereo::set_projection(0); vector subscr[4]; compute_graphical_distance(); const int tmax = 2000; int t = ticks % (5*tmax); int tphase = t / tmax, tsub = t % tmax; ld xdst, ydst; for(int i=0; i<4; i++) { ptds.clear(); cellwalker xcw; ld part = 1; if(i == 1) xcw = bttargets[0]; else if(i == 2) xcw = bttargets[1], part = .5; else if(i == 3) xcw = bttargets[3], part = .5; else xcw = cwt; View = Id; transmatrix tView = actualV(cth(xcw), Id) * calc_relative_matrix(cwt.at, xcw.at, NOHINT) * inverse(actualV(cth(cwt), Id)); if(tphase < 2) part = 0; else if(tphase == 2) part = part * tsub / tmax; transmatrix itView = inverse(tView); transmatrix z = rspintox(itView*C0) * xpush(hdist0(itView*C0) * part) * spintox(itView*C0); transmatrix ful = rspintox(itView*C0) * xpush(hdist0(itView*C0) * tmax / tmax) * spintox(itView*C0); // rgpushxto0(itView*C0); hyperpoint C1 = xpush0(1); ld bof = alphaof(tView * ful * C1); z = z * spin(bof * part); View = inverse(z); if(tphase == 0 && tsub > tmax/2) { ld alpha = rand() % 10; ld d = (rand() % 1000) / 20000. * (tsub-tmax/2) / (tmax/2); View = spin(alpha) * xpush(d) * spin(-alpha); } /* int phasemask = 3; if(tphase == 0) phasemask = 0; if(tphase == 4) phasemask = 2; */ ForInfos if(cci.second.gid == i) cci.second.c->wall = cci.second.w, cci.second.c->item = cci.second.it, cci.second.c->monst = cci.second.mo, cci.second.c->land = cci.second.land; else cci.second.c->wall = waInvisibleFloor, cci.second.c->item = itNone, cci.second.c->monst = moNone, cci.second.c->land = laNone; mapeditor::drawplayer = cwt.at->wparam == i; switch(tphase) { case 0: xdst = ydst = 0; curpart = 4; break; case 1: xdst = ydst = .5 * tsub / tmax; break; case 2: xdst = ydst = .5; break; case 3: xdst = .5, ydst = .5 * (tmax-tsub) / tmax; break; case 4: xdst = .5, ydst = 0; break; default: xdst = ydst = 0; } /* ld xpos = (!(i&2)) ? xdst : -xdst; ld ypos = (!(i&1)) ? ydst : -ydst; */ gmatrix.clear(); drawrec(viewctr, hsOrigin, cview()); if(0) for(auto p: parent) if(gmatrix.count(p.first) && gmatrix.count(p.second) && infos[p.first].gid == i && infos[p.second].gid == i) queueline(tC0(gmatrix[p.first]), tC0(gmatrix[p.second]), 0xFFFFFFFF, 2); subscr[i] = move(ptds); } map>> xptds; for(int i=0; i<4; i++) for(auto& p: subscr[i]) xptds[int(p.prio)][i].push_back(p); for(auto& sm: xptds) for(auto& sm2: sm.second) { int i = sm2.first; ptds.clear(); for(auto& p: sm2.second) ptds.push_back(p); vid.scale = .5; vid.xposition = (!(i&2)) ? xdst : -xdst; vid.yposition = (!(i&1)) ? ydst : -ydst; calcparam(); stereo::set_projection(0); drawqueue(); } ForInfos cci.second.c->wall = cci.second.w, cci.second.c->item = cci.second.it, cci.second.c->monst = cci.second.mo, cci.second.c->land = cci.second.land; } void bantar_anim() { vid.aurastr = 0; bool breakanim = false; int t = SDL_GetTicks(); drawthemap(); while(!breakanim) { ticks = SDL_GetTicks() - t; bantar_frame(); SDL_GL_SwapBuffers(); SDL_Event ev; while(SDL_PollEvent(&ev)) if(ev.type == SDL_KEYDOWN || ev.type == SDL_MOUSEBUTTONDOWN) breakanim = true; } mapeditor::drawplayer = true; vid.xposition = vid.yposition = 0; vid.scale = 1; } bool bmap; bool bantar_stats() { if(bmap) { vid.linewidth *= (inHighQual ? 10 : 2); for(auto p: parent) if(gmatrix.count(p.first) && gmatrix.count(p.second)) queueline(tC0(gmatrix[p.first]), tC0(gmatrix[p.second]), 0x00FF00FF, 4); double x = hexvdist; for(auto gm: gmatrix) for(cell *c: {gm.first}) if(euclid || !pseudohept(c)) for(int t=0; ttype; t++) if(infos.count(c) && infos.count(c->move(t)) && c->move(t) && infos[c].gid != infos[c->move(t)].gid) if(euclid ? c->move(t)move(t) < c)) queueline(gm.second * ddspin(c,t,-M_PI/S6) * xpush(x) * C0, gm.second * ddspin(c,t,+M_PI/S6) * xpush(x) * C0, 0xFF0000FF, 1); vid.linewidth /= (inHighQual ? 10 : 2); drawqueue(); } return false; } void init_bantar() { if(!on) { stop_game(); on = true; start_game(); } } void init_bantar_map() { bmap = true; ForInfos { int hsh = 0x202047; for(int w: cci.second.way) hsh = (11301 * hsh + w * 37121) & 0x7F7F7F; cci.second.c->landparam = hsh; cci.second.c->land = laCanvas; cci.second.c->wall = waNone; cci.second.c->item = itNone; cci.second.c->monst = moNone; } } int readArgs() { using namespace arg; if(0) ; else if(argis("-bantar_anim")) { PHASE(3); init_bantar(); peace::on = true; airmap.clear(); ForInfos if(cci.second.c->monst == moAirElemental) cci.second.c->monst = moFireElemental; bantar_anim(); } else if(argis("-bantar_test")) { PHASE(3); init_bantar(); peace::on = true; airmap.clear(); ForInfos if(cci.second.c->monst == moAirElemental) cci.second.c->monst = moFireElemental; ForInfos if(cci.second.gid != 2) cci.second.c->wall = waInvisibleFloor, cci.second.c->item = itNone, cci.second.c->monst = moNone, cci.second.c->land = laNone; airmap.clear(); havewhat = 0; } else if(argis("-bantar_map")) { init_bantar(); init_bantar_map(); } else if(argis("-btry")) { shift(); notry = argi(); } else return 1; return 0; } auto hook = addHook(hooks_args, 100, readArgs) + addHook(hooks_initgame, 100, bantar) + addHook(hooks_frame, 100, bantar_stats); }}