/* some extra routines for debugging and testing the rulegen algorithm */ #include "../hyper.h" #include namespace hr { namespace rulegen { string testroot = "devmods/rulegen-tests/"; string testdir = testroot; struct hrmap_testproto : hrmap { map counterpart; map counterpart2; heptagon* clone(tcell *o) { auto& h = counterpart2[o]; if(!h) { h = tailored_alloc (o->type); counterpart[h] = o; h->zebraval = 0; h->emeraldval = 0; h->distance = 0; h->cdata = nullptr; h->c7 = newCell(o->type, h); h->c7->land = laCanvas; } return h; } ~hrmap_testproto() { println(hlog, "clearing ", isize(counterpart), " heptagons from testproto"); for(auto p: counterpart) { auto& at = p.first; if(at->cdata) delete at->cdata; destroy_cell(at->c7); tailored_delete(at); } } heptagon *getOrigin() override { return clone(t_origin[0]); } heptagon *create_step(heptagon *h, int d) override { auto ch = counterpart[h]; if(!ch->move(d)) { h->c.connect(d, &oob, 0, false); h->c7->c.connect(d, &out_of_bounds, 0, false); out_of_bounds.master = &oob; oob.c7 = &out_of_bounds; return &oob; } auto ch1 = ch->cmove(d); auto d1 = ch->c.spin(d); auto h1 = clone(ch1); if(ch == ch1) throw rulegen_failure("connected to self??"); if(h == h1) throw rulegen_failure("connected to self on hept level??"); h->c.connect(d, h1, d1, false); return h1; } void find_cell_connection(cell *c, int d) { heptagon *h2 = createStep(c->master, d); if(h2 == &oob) return; c->c.connect(d, h2->c7,c->master->c.spin(d), c->master->c.mirror(d)); } transmatrix adj(heptagon *h, int dir) override { return arb::get_adj(arb::current_or_slided(), shvid(h->c7), dir, -1, h->c.move(dir) ? h->c.spin(dir) : -1); } int shvid(cell *c) override { if(!counterpart.count(c->master)) { /* may happen while handling floorshapes */ return arb::id_of(c->master); } auto cc = counterpart[c->master]; return cc->id; } bool strict_tree_rules() { return true; } }; reaction_t clear_debug = [] {}; map sprawl_shown; void view_debug() { auto m = dynamic_cast (currentmap); if(m) { int ah = addHook(hooks_drawcell, 50, [m] (cell *c, const shiftmatrix& V) { tcell *tc = m->counterpart[c->master]; string s; auto label = (tc->code == MYSTERY ? "?" : its(tc->code)) + "/" + (tc->dist == MYSTERY ? "?" : tc->dist == MYSTERY_DIST ? "*" : its(tc->dist)); color_t col = 0xFFFFFF + 0x512960 * tc->code; if(pointer_indices.count(tc)) label += " " + index_pointer(tc); queuestr(V, 0.4, label, col, 1); if(tc->parent_dir >= 0 && tc->parent_dir < tc->type) { vid.linewidth *= 8; queueline(V * C0, V * currentmap->adj(c, tc->parent_dir) * C0, 0xFFFFFFFF); vid.linewidth /= 8; } if(sprawl_shown.count(tc)) queuepoly(V, cgi.shDisk, 0xFF0000FF); return false; }); vector dh; dh.push_back(addHook(hooks_o_key, 15, [m] (o_funcs& v) { v.push_back(named_functionality("sprawl", [m] { tcell *c = m->counterpart[centerover->master]; auto [d, id] = get_code(c); twalker cw(c, d); auto res = spread(get_analyzer(cw), cw); println(hlog, "sprawl result = ", res); sprawl_shown.clear(); for(int i=0; icounterpart[centerover->master]; println(hlog, "parent_dir = ", c->parent_dir); c->parent_dir = MYSTERY; parent_debug = true; get_parent_dir(c); parent_debug = false; println(hlog, "parent_dir = ", c->parent_dir); })); })); for(auto dw: debuglist) dh.push_back(addHook(hooks_o_key, 10, [m,dw] (o_funcs& v) { v.push_back(named_functionality(lalign(0, "go to ", dw), [dw,m] { cwt = cellwalker(m->clone(dw.at)->c7, dw.spin, dw.mirrored); centerover = cwt.at; View = Id; })); })); clear_debug = [ah, dh] { delHook(hooks_drawcell, ah); for(auto dhk: dh) delHook(hooks_o_key, dhk); clear_debug = [] {}; }; } else if(currentmap->strict_tree_rules()) { int ah = addHook(hooks_drawcell, 50, [] (cell *c, const shiftmatrix& V) { string s; int id = get_state(c); auto label = its(id); color_t col = 0xFFFFFF + 0x512960 * id; if(pointer_indices.count(c->master)) label += " " + index_pointer(c->master); queuestr(V, 0.3, label, col, 1); vid.linewidth *= 8; queueline(V * C0, V * currentmap->adj(c, 0) * C0, 0xFFFFFFFF, 4); vid.linewidth /= 8; return false; }); clear_debug = [ah] { delHook(hooks_drawcell, ah); }; } } int test_rotate_val = 0; int test_count = 10000; void test_rules() { cell *c = currentmap->gamestart(); int N = isize(treestates); if(!N) { println(hlog, "no states generated"); return; } vector howmany(N); howmany[0] = 1; celllister cl(c, 100, test_count, nullptr); vector bydist(64, 0); for(cell *cc: cl.lst) { int d = cl.getdist(cc); if(d < 64) bydist[d]++; } vector vals; vector seq; for(int iter=0; iter<30; iter++) { bignum total; for(auto& h: howmany) total += h; seq.push_back(total.get_str(100)); println(hlog, iter, " : ", total.get_str(100), " vs ", bydist[iter]); if(bydist[iter] && bydist[iter] != total.approx_ll()) println(hlog, "ERROR count mismatch"); vector next(N); for(int id=0; id= 0) next[s] += howmany[id]; howmany = std::move(next); } println(hlog, "sequence: ", seq); } void list_sequence() { int N = isize(treestates); if(!N) { println(hlog, "no states generated"); return; } vector howmany(N); howmany[0] = 1; vector seq; for(int iter=0; iter<30; iter++) { bignum total; for(auto& h: howmany) total += h; seq.push_back(total.get_str(100)); vector next(N); for(int id=0; id= 0) next[s] += howmany[id]; howmany = std::move(next); } println(hlog, "sequence: ", seq); } void print_rules(); string rule_name(int r) { if(r == DIR_UNKNOWN) return "??"; else if(r == DIR_LEFT) return "L"; else if(r == DIR_RIGHT) return "R"; else if(r == DIR_PARENT) return "P"; else return its(r); } void print_rules() { for(int i=0; iis_solid) qsolid++; if(c->code != MYSTERY) qcode++; if(c->dist != MYSTERY) qdist++; c = c->next; } vector areas; for(auto& sh: arb::current.shapes) { ld s = 0; int i = 0; for(auto a: sh.angles) { while(a > 2 * M_PI) a -= 2 * M_PI; while(a<0) a += 2 * M_PI; s += a; i++; } areas.push_back((i-2) * M_PI - s); } sort(areas.begin(), areas.end()); again: print(hlog, "CSV"); #define Out(title,value) if(add_header) print(hlog, ";", title); else if(add_labels) print(hlog, " ", title, "=", value); else print(hlog, ";", value); break; for(char c: test_stats) switch(c) { case 'g': Out("geom", euclid ? "E" : hyperbolic ? "H" : "?"); case 's': Out("status", status); case 'm': Out("message", message); case 'c': Out("cells", tcellcount); case 'u': Out("unis", tunified); case 'q': Out("solid", qsolid); case 'd': Out("dist", qdist); case 'C': Out("code", qcode); case 't': Out("try", try_count); case 'T': Out("T", tstart / 1000.); case 'y': Out("tree", isize(treestates)); case 'a': Out("amin;amax", lalign(0, areas[0], ";", areas.back())); case 'h': Out("shapes", isize(arb::current.shapes)); case 'f': Out("file", arb::current.filename); } println(hlog); fflush(stdout); if(add_header) { add_header = false; goto again; } // for(auto& sh: shortcuts) println(hlog, sh.first, " : ", isize(sh.second), " shortcuts (CSV)"); print_rules(); /* for(auto& a: analyzers) println(hlog, "analyzer ", a.first, " size is ", isize(a.second.spread)); */ fflush(stdout); list_sequence(); fflush(stdout); } void out_reg() { println(hlog, "X ", int(geometry), " ", int(variation), " ", int(gp::param.first), " ", int(gp::param.second)); } void test_all_regular(vector glist) { for(eGeometry g: glist) { set_geometry(g); set_variation(eVariation::pure); out_reg(); set_geometry(g); set_variation(eVariation::bitruncated); out_reg(); for(int a=1; a<5; a++) for(int b=0; b<=a; b++) { if(a==1 && b == 0) continue; if(a==1 && b == 1 && S3 == 3) continue; stop_game(); set_geometry(g); gp::param = {a, b}; set_variation(eVariation::goldberg); out_reg(); if(S3 == 4 && (geometry == g46 || ((a+b)%2 == 0))) { stop_game(); set_variation(eVariation::unrectified); out_reg(); } if(S3 == 3 && (geometry == gOctagon || (a-b)%3 == 0)) { stop_game(); set_variation(eVariation::untruncated); out_reg(); stop_game(); set_variation(eVariation::warped); out_reg(); } } } } void set_dir(string s) { testdir = testroot + s; system(("mkdir -p " + testdir).c_str()); } void test_from_file(string list) { set_dir("devmods/rulegen-tests/" + list); vector filenames; std::ifstream is("devmods/rulegen-tests/" + list + ".lst"); string s; while(getline(is, s)) { if(s != "" && s[0] != '#') filenames.push_back(s); } int trv = test_rotate_val; int id = 0; for(const string& s: filenames) { if(trv) { trv--; id++; continue; } stop_game(); set_geometry(gArbitrary); try { println(hlog, "loading ", s, "... ", id++, "/", isize(filenames)); arb::load(s); } catch(hr_parse_exception& ex) { println(hlog, "failed: ", ex.s); continue; } catch(arb::hr_polygon_error& ex) { println(hlog, "poly error"); continue; } catch(hr_exception& ex) { println(hlog, "other exception"); continue; } if(cgflags & qAFFINE) { println(hlog, "illgeal tessellation found: affine"); continue; } if(sphere) { println(hlog, "illgeal tessellation found: spherical"); continue; } test_current(); } } void test_all_geoms(int i) { if(i&1) { set_dir("regular/"); test_all_regular({gNormal, gOctagon, g45, g46, g47, gEuclid, gEuclidSquare}); } } int testargs() { using namespace arg; if(0) ; else if(argis("-testproto")) { restart_game_on(new hrmap_testproto); println(hlog, "creatad a testproto map with ", tcellcount, " cells"); } else if(argis("-test-stats")) { shift(); test_stats = args(); add_header = true; add_labels = false; } else if(argis("-test-stats-label")) { shift(); test_stats = args(); add_header = false; add_labels = true; } else if(argis("-test-this")) { PHASEFROM(3); try { test_current(); } catch(rulegen_failure& e) { } } else if(argis("-test-list")) { PHASEFROM(3); shift(); test_from_file(args()); } else if(argis("-test-all")) { PHASEFROM(3); shift(); test_all_geoms(argi()); start_game(); } else if(argis("-trv")) { shift(); test_rotate_val = argi(); } else if(argis("-resolve")) try_to_resolve_confusion = true; else if(argis("-view-debug")) view_debug(); else if(argis("-print-rules")) print_rules(); else if(argis("-clear-debug")) clear_debug(); else return 1; return 0; } auto testhooks = addHook(hooks_args, 100, testargs); } }