// HyperRogue -- expansion_analyzer // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details namespace hr { int subtype(cell *c) { return patterns::getpatterninfo(c, patterns::PAT_NONE, 0).id; } bignum& bignum::operator +=(const bignum& b) { int K = isize(b.digits); if(K > isize(digits)) digits.resize(K); int carry = 0; for(int i=0; i= isize(digits)) digits.push_back(0); digits[i] += carry; if(i < K) digits[i] += b.digits[i]; if(digits[i] >= BASE) { digits[i] -= BASE; carry = 1; } else carry = 0; } return *this; } bool bignum::operator < (const bignum& b) const { if(isize(digits) != isize(b.digits)) return isize(digits) < isize(b.digits); for(int i = isize(digits)-1; i>=0; i--) if(digits[i] != b.digits[i]) return digits[i] < b.digits[i]; return false; } void bignum::addmul(const bignum& b, int factor) { int K = isize(b.digits); if(K > isize(digits)) digits.resize(K); int carry = 0; for(int i=0; i 0 && carry < -1) || (carry == -1 && i < isize(digits)); i++) { if(i >= isize(digits)) digits.push_back(0); long long l = digits[i]; l += carry; if(i < K) l += b.digits[i] * factor; carry = 0; if(l >= BASE) carry = l / BASE; if(l < 0) carry = -(BASE-1-l) / BASE; l -= carry * BASE; digits[i] = l; } if(carry < 0) digits.back() -= BASE; while(isize(digits) && digits.back() == 0) digits.pop_back(); } bignum hrand(bignum b) { bignum res; int d = isize(b.digits); while(true) { res.digits.resize(d); for(int i=0; i=0; i--) { if(isize(ret) > max_length && i) { ret += XLAT(" (%1 more digits)", its(9 * (i+1))); return ret; } ret += " "; string val = its(digits[i]); while(isize(val) < 9) val = "0" + val; ret += val; } return ret; } void canonicize(vector& t) { for(int i=2; i expansion_analyzer::gettype(cell *c) { vector res; res.push_back(subtype(c) * 4 + 2); int d = celldist(c); for(int i=0; itype; i++) { cell *c1 = c->cmove(i); res.push_back(subtype(c1) * 4 + celldist(c1) - d); } canonicize(res); return res; } int expansion_analyzer::sample_id(cell *c) { auto t = gettype(c); if(codeid.count(t)) return codeid[t]; auto &cit = codeid[t]; cit = isize(samples); samples.push_back(c); return cit; } template vector get_children_codes(cell *c, const T& distfun, const U& typefun) { vector res; int d = distfun(c); cellwalker cw(c, 0); if(d > 0) { forCellCM(c2, c) if(celldist(cw.peek()) < d) break; else cw++; } for(int k=0; ktype; k++) { cell *c1 = cw.cpeek(); cw++; if(distfun(c1) != d+1) continue; cell *c2 = cw.cpeek(); if(distfun(c2) != d+1) continue; res.push_back(typefun(c1)); } return res; } void expansion_analyzer::preliminary_grouping() { samples.clear(); codeid.clear(); children.clear(); sample_id(currentmap->gamestart()); // queue for, do not change to range-based for for(int i=0; i grouping; grouping.resize(N); int nogroups = 1; for(int i=0; i, int > > childgroups(N); for(int i=0; i groupsample(nogroups); for(int i=0; i> newchildren(nogroups); for(int i=0; i int size_upto(vector& v, int s) { int res = isize(v); if(res < s) v.resize(s); return res; } bignum& expansion_analyzer::get_descendants(int level) { if(!N) preliminary_grouping(), reduce_grouping(); return get_descendants(level, rootid); } bignum& expansion_analyzer::get_descendants(int level, int type) { auto& pd = descendants; size_upto(pd, level+1); for(int d=0; d<=level; d++) for(int i=size_upto(pd[d], N); i= bignum::BASE) return 0; ld matrix[100][128]; for(int i=0; i=0; k--) for(int l=k-1; l>=0; l--) if(matrix[l][k]) matrix[l][v] -= matrix[l][k] * matrix[k][v]; coef.resize(v); for(int i=0; i= 1) return growth; if(!N) preliminary_grouping(), reduce_grouping(); vector eigen(N, 1); ld total; for(int iter=0; iter<100000; iter++) { total = 0; vector neweigen(N, 0); for(int i=0; i int type_in(expansion_analyzer& ea, cell *c, const T& f) { if(!ea.N) ea.preliminary_grouping(), ea.reduce_grouping(); vector res; res.push_back(subtype(c) * 4 + 2); int d = f(c); for(int i=0; itype; i++) { cell *c1 = c->cmove(i); res.push_back(subtype(c1) * 4 + f(c1) - d); } canonicize(res); if(ea.codeid.count(res)) return ea.codeid[res]; int ret = ea.N++; ea.codeid[res] = ret; ea.children.emplace_back(); ea.children[ret] = get_children_codes(c, f, [&ea, &f] (cell *c1) { return type_in(ea, c1, f); }); return ret; } template int type_in_quick(expansion_analyzer& ea, cell *c, const T& f) { vector res; res.push_back(subtype(c) * 4 + 2); int d = f(c); for(int i=0; itype; i++) { cell *c1 = c->cmove(i); int dd = f(c1) - d; if(dd < -1 || dd > 1) return -1; res.push_back(subtype(c1) * 4 + dd); } canonicize(res); if(ea.codeid.count(res)) return ea.codeid[res]; return -1; } bool sizes_known() { if(bounded) return false; // Castle Anthrax is infinite if(binarytiling) return false; // not implemented if(archimedean) return false; return true; } string expansion_analyzer::approximate_descendants(int d, int max_length) { auto t = SDL_GetTicks(); while(isize(descendants) <= d && SDL_GetTicks() < t + 100) get_descendants(isize(descendants)); if(isize(descendants) > d) return get_descendants(d).get_str(max_length); int v = isize(descendants) - 1; bignum& b = get_descendants(v); if(b.digits.empty()) return "0"; ld log_10 = log(b.digits.back()) / log(10) + 9 * (isize(b.digits) - 1) + (d - v) * log(get_growth()) / log(10); int more_digits = int(log_10); return XLAT("about ") + fts(pow(10, log_10 - more_digits)) + "E" + its(more_digits); } enum eDistanceFrom { dfPlayer, dfStart, dfWorld }; string dfnames[3] = { "player", "start", "land" }; eDistanceFrom distance_from = dfPlayer; enum eNumberCoding { ncNone, ncDistance, ncType, ncDebug }; string ncnames[4] = { "NO", "distance", "type", "debug" }; eNumberCoding number_coding = ncDistance; bool mod_allowed() { return cheater || autocheat || archimedean || tour::on; } int curr_dist(cell *c) { switch(distance_from) { case dfPlayer: return c->cpdist < INFD ? c->cpdist : celldistance(cwt.at, c); case dfStart: return celldist(c); case dfWorld: if(!mod_allowed() && !among(c->land, laOcean, laIvoryTower, laEndorian, laDungeon, laTemple, laWhirlpool)) return 0; if(c->master->alt) return celldistAlt(c); return inmirror(c) ? (c->landparam & 255) : c->landparam; } return 0; } int position; template int type_in_reduced(expansion_analyzer& ea, cell *c, const T& f) { int a = ea.N; int t = type_in(ea, c, f); if(expansion.N != a) { expansion.reduce_grouping(); t = type_in(ea, c, f); } return t; } bool viewdists = false, use_color_codes = true, use_analyzer = true, show_distance_lists = true; int first_distance = 0, scrolltime = 0; bool scrolling_distances = false; color_t distribute_color(int id) { color_t v = 0xFFFFFF; for(int z=0; z<24; z++) if(id & (1<= 0) label = its(t), dc = distribute_color(t); break; } case ncDebug: { int d = celldistance(c, distance_from == dfPlayer ? cwt.at : currentmap->gamestart()); dc = (d != cd) ? 0xFF0000 : 0x00FF00; label = its(d); } case ncNone: ; } // string label = its(fieldpattern::getriverdistleft(c)) + its(fieldpattern::getriverdistright(c)); /* queuepolyat(V, shFloor[ct6], darkena(gradient(0, distcolors[cd&7], 0, .25, 1), fd, 0xC0), PPR::TEXT); */ if(label != "") queuestr(V, (isize(label) > 1 ? .6 : 1), label, 0xFF000000 + dc, 1); } void viewdist_configure_dialog() { dialog::init(""); cmode |= sm::SIDE | sm::MAYDARK | sm::EXPANSION; gamescreen(0); dialog::addSelItem("which distance", dfnames[distance_from], 'c'); dialog::add_action([] () { distance_from = mod_allowed() ? eDistanceFrom(2 - distance_from) : eDistanceFrom((distance_from + 1) % 3); }); dialog::addSelItem("number codes", ncnames[number_coding], 'n'); dialog::add_action([] () { number_coding = eNumberCoding((number_coding + 1) % (mod_allowed() ? 4 : 2)); }); dialog::addBoolItem("color codes", use_color_codes, 'u'); dialog::add_action([] () { use_color_codes = !use_color_codes; }); dialog::addBoolItem("use analyzer", use_analyzer, 'a'); dialog::add_action([] () { use_analyzer = !use_analyzer; }); dialog::addBoolItem("show distance lists", show_distance_lists, 'l'); dialog::add_action([] () { show_distance_lists = !show_distance_lists; }); dialog::addSelItem("display distances from", its(first_distance), 'd'); dialog::add_action([] () { scrolling_distances = false; dialog::editNumber(first_distance, 0, 3000, 0, 1, "display distances from", ""); }); int id = 0; for(auto& lp: linepatterns::patterns) { using namespace linepatterns; if(among(lp.id, patTriTree, patTriRings, patTriOther)) { dialog::addColorItem(XLAT(lp.lpname), lp.color, '1'+(id++)); dialog::add_action([&lp] () { dialog::openColorDialog(lp.color, NULL); dialog::dialogflags |= sm::MAYDARK | sm::SIDE | sm::EXPANSION; }); } } if(!mod_allowed()) dialog::addInfo(XLAT("note: enable the cheat mode for additional options")); else dialog::addBreak(100); if(distance_from) dialog::addInfo("numbers show the descendants of current player position"); else dialog::addBreak(100); dialog::addBack(); dialog::display(); } bool is_descendant(cell *c) { if(c == cwt.at) return true; if(curr_dist(c) < curr_dist(cwt.at)) return false; return is_descendant(chosenDown(c, -1, 0, curr_dist)); } const int scrollspeed = 100; void expansion_analyzer::view_distances_dialog() { static int lastticks; if(scrolling_distances && !bounded) { scrolltime += SDL_GetTicks() - lastticks; first_distance += scrolltime / scrollspeed; scrolltime %= scrollspeed; } lastticks = SDL_GetTicks(); distcolors[0] = forecolor; dialog::init(""); cmode |= sm::DIALOG_STRICT_X | sm::EXPANSION; int maxlen = bounded ? 128 : 16 + first_distance; vector qty(maxlen); bool really_use_analyzer = use_analyzer && sizes_known(); if(really_use_analyzer) { int t = type_in_reduced(expansion, cwt.at, curr_dist); for(int r=0; r= 0 && d < maxlen) qty[d]++; } else { celllister cl(cwt.at, bounded ? maxlen-1 : gamerange(), 100000, NULL); for(cell *c: cl.lst) if(is_descendant(c)) qty[curr_dist(c)]++; } if(sizes_known()) { find_coefficients(); if(gamerange()+1 >= valid_from && coefficients_known == 2) { for(int i=gamerange()+1; i 1) fmt += " + " + its(coef[i]); else if(coef[i] < -1) fmt += " - " + its(-coef[i]); fmt += "a(d"; if(i != isize(coef) - 1) fmt += "+" + its(isize(coef) - 1 - i); fmt += ")"; first = false; } fmt += " (d>" + its(valid_from-1) + ")"; dialog::addHelp(fmt); } else dialog::addBreak(100); char buf[20]; snprintf(buf, 20, "%.8lf", (double) get_growth()); dialog::addInfo("a(d) = Θ(" + string(buf) + "...ᵈ)", forecolor); } } dialog::addItem("scroll", 'S'); dialog::addItem("configure", 'C'); dialog::display(); } void enable_viewdists() { first_distance = 0; scrolltime = 0; viewdists = true; if(!mod_allowed()) { number_coding = ncDistance; distance_from = dfPlayer; } show_distance_lists = true; } bool expansion_handleKey(int sym, int uni) { if(cmode & sm::NORMAL) { if(uni == 'S') scrolling_distances = !scrolling_distances; else if(uni == 'C') pushScreen(viewdist_configure_dialog); else if(sym == SDLK_ESCAPE) first_distance = 0, viewdists = false; else return false; return true; } return false; } int expansion_hook = addHook(hooks_handleKey, 0, expansion_handleKey); #if !CAP_MINI void compute_coefficients() { printf("%s %s\n", gp::operation_name().c_str(), ginf[geometry].name); start_game(); printf(" sizes:"); for(int i=0; i<10; i++) printf(" %d", expansion.get_descendants(i).approx_int()); printf(" N = %d\n", expansion.N); expansion.find_coefficients(); if(expansion.coefficients_known == 2) { printf(" coefficients:"); for(int x: expansion.coef) printf(" %d", x); printf(" (tested on %d to %d)\n", expansion.valid_from, expansion.tested_to); } } int readArgs() { using namespace arg; if(0) ; else if(argis("-vap")) { PHASEFROM(2); start_game(); while(true) { string s = expansion.approximate_descendants(10000, 100); printf("s = %s\n", s.c_str()); if(isize(expansion.descendants) >= 10000) break; } } else if(argis("-csizes")) { PHASEFROM(2); start_game(); expansion.get_growth(); for(int i=0; i<30; i++) printf("%s / %s\n", expansion.get_descendants(i).get_str(1000).c_str(), expansion.get_descendants(i, expansion.diskid).get_str(1000).c_str()); } else if(argis("-csolve")) { PHASEFROM(2); start_game(); printf("preliminary_grouping...\n"); expansion.preliminary_grouping(); printf("N = %d\n", expansion.N); printf("reduce_grouping...\n"); expansion.reduce_grouping(); printf("N = %d\n", expansion.N); printf("growth = %lf\n", (double) expansion.get_growth()); expansion.find_coefficients(); if(expansion.coefficients_known == 2) { printf("coefficients:"); for(int x: expansion.coef) printf(" %d", x); printf(", valid from %d to %d\n", expansion.valid_from, expansion.tested_to); } } else if(argis("-csolve_tab")) { for(eGeometry geo: {gNormal, gOctagon, g45, g46, g47}) { set_geometry(geo); set_variation(eVariation::pure); compute_coefficients(); set_variation(eVariation::bitruncated); compute_coefficients(); for(int x=1; x<9; x++) for(int y=0; y<=x; y++) { if(x == 1 && y == 0) continue; if(x == 1 && y == 1 && S3 == 3) continue; if(x+y > 10) continue; stop_game(); gp::param = gp::loc(x, y); need_reset_geometry = true; set_variation(eVariation::goldberg); compute_coefficients(); } } } else return 1; return 0; } auto ea_hook = addHook(hooks_args, 100, readArgs); #endif expansion_analyzer expansion; }