// 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 #include "rogueviz.h" namespace rogueviz { namespace banachtarski { bool on; typedef vector cwpath; cwpath invertpath(cwpath p) { cwpath res; for(int i=0; i gens; cellwalker bttargets[4]; cellwalker trace(cellwalker cw, cwpath& p) { for(int i: p) if(i == 0) cw += wstep; else cw += i; return cw; } set 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, waNone, itNone, laNone, moNone, way, 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) { shiftpoint h1 = tC0(ggmatrix(cp.first)); shiftpoint 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); centerover = cwt.at; 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; imonst == 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 if(argis("-bantar_record")) { using namespace banachtarski; PHASE(3); peace::on = true; airmap.clear(); ForInfos if(cci.second.c->monst == moAirElemental) cci.second.c->monst = moFireElemental; bantar_record(); } else return 1; return 0; } auto hook = addHook(hooks_args, 100, readArgs) + addHook(hooks_initgame, 100, bantar) + addHook(hooks_frame, 100, bantar_stats) + addHook_rvslides(140, [] (string s, vector& v) { if(s != "mixed") return; using namespace pres; v.push_back( tour::slide{"Banach-Tarski-like", 62, LEGAL::NONE, "Banach-Tarski-like decomposition. Break a hyperbolic plane into two hyperbolic planes.\n\n" "Press '5' to show the decomposition. Press any key to stop.\n\n" "You will see a map of the decomposition. Press '5' again to return.", [] (presmode mode) { slidecommand = "Banach-Tarski switch"; slide_url(mode, 't', "Twitter link", "https://twitter.com/ZenoRogue/status/1001127253747658752"); if(mode == 3) { while(gamestack::pushed()) stop_game(), gamestack::pop(); banachtarski::bmap = false; banachtarski::on = false; } if(mode == 4) { if(!banachtarski::on) { bool b = mapeditor::drawplayer; specialland = cwt.at->land; gamestack::push(); banachtarski::init_bantar(); airmap.clear(); dynamicval vs(sightrange_bonus, 3); dynamicval vg(genrange_bonus, 3); doOvergenerate(); banachtarski::bantar_anim(); quitmainloop = false; mapeditor::drawplayer = b; banachtarski::init_bantar_map(); resetview(); } else if(banachtarski::on && banachtarski::bmap) { banachtarski::bmap = false; banachtarski::on = false; gamestack::pop(); } } }} ); }); }}