/* some extra routines for debugging and testing the rulegen algorithm */ #include "../hyper.h" #include #include #include // extra ruleflags: // 30: do not clear memory namespace hr { namespace rulegen { pair longest_shortcut(); EX flagtype sub_rulegen_flags; 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].at); } 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 { if(h->move(dir) == &oob || h == &oob) return Id; return arb::get_adj(arb::current_or_slided(), shvid(h->c7), dir, -1, h->c.move(dir) ? h->c.spin(dir) : -1); } hyperpoint get_corner(cell *c, int cid, ld cf) override { auto h = c->master; if(!counterpart.count(h)) { auto& sh = arb::current_or_slided().shapes[c->master->zebraval]; cid = gmod(cid, sh.size()); return normalize(C0 + (sh.vertices[cid] - C0) * 3 / cf); } int s = counterpart[h]->id; auto& sh = arb::current_or_slided().shapes[s]; return normalize(C0 + (sh.vertices[cid] - C0) * 3 / cf); } 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 false; } }; reaction_t clear_debug = [] {}; map sprawl_shown; vector old_sprawl, cur_sprawl; int old_sprawl_id; int total_analyzers(); void cleanup_protomap() { auto m = dynamic_cast (currentmap); auto *c = first_tcell; while(c) { auto wh = m->clone(c); for(int i=0; itype; i++) if(wh->move(i) == &oob) wh->move(i) = nullptr; for(int i=0; ic7->type; i++) if(wh->c7->move(i) == &out_of_bounds) wh->c7->move(i) = nullptr; c = c->next; } } void iterate(int qty) { dynamicval set_timeout(rulegen_timeout, 999999); for(int i=0; i (currentmap); ufind(dw); move_to(cellwalker(m->clone(dw.at)->c7, dw.spin, dw.mirrored)); } vector old_givers; void try_sprawling(tcell *c) { twalker cw = c; cw = get_parent_dir(cw); ufind(cw); auto aid = get_aid(cw); auto a_ptr = &(analyzers[aid.first][aid.second]); vector sprawl = { cw }; vector states = { nullptr }; while(true) { auto& a = *a_ptr; if(!a) { println(hlog, "analyzer not allocated"); return; } states.push_back(a); if(isize(sprawl) <= cw.at->type) { a->id = 0, a->dir = isize(sprawl)-1; } if(a->id == MYSTERY) { println(hlog, "reached codeid ", a->analyzer_id, " which is state ", a->dir); return; } auto t = sprawl[a->id]; twalker tw = t + a->dir; ufind(tw); tw.cpeek(); ufind(tw); int mc = move_code(tw + wstep); sprawl.push_back(tw + wstep); println(hlog, "codeid ", a->analyzer_id, ": going from ", tw, " in direction ", a->dir, " reaching ", sprawl.back(), " of movecode ", mc); a_ptr = &(a->substates[mc]); } } void debug_menu() { cmode = sm::SIDE | sm::MAYDARK; gamescreen(); auto m = dynamic_cast (currentmap); dialog::init("debug menu"); dialog::addItem("parent_dir", 'p'); dialog::add_action([m] { tcell *c = m->counterpart[centerover->master]; auto c1 = c; ufindc(c1); if(c != c1) { println(hlog, "ufindc changes ", c, " to ", c1); for(int i=0; itype; i++) println(hlog, twalker(c,i), twalker(c,i)+wstep, twalker(c,i)+wstep+wstep); } println(hlog, "parent_dir = ", c->parent_dir); c->parent_dir = MYSTERY; rulegen::rdebug_flags |= 16; try { twalker cw(c, 0); get_parent_dir(cw); } catch(rulegen_failure& f) { println(hlog, "catched: ", f.what()); } rulegen::rdebug_flags &= ~16; println(hlog, "parent_dir = ", c->parent_dir); cleanup_protomap(); }); dialog::addItem("iterate", 'm'); dialog::add_action([] { iterate(1); }); dialog::addItem("iterate x100", 'M'); dialog::add_action([] { iterate(100); }); dialog::addItem("iterate x1000", 'T'); dialog::add_action([] { iterate(1000); }); dialog::addItem("debug tiles", 'd'); dialog::add_action_push([m] { cmode = sm::SIDE | sm::MAYDARK; gamescreen(); dialog::init(); for(auto dw: debuglist) { dialog::addItem("go to " + index_pointer(dw.at), 'a'); dialog::add_action([dw] { move_to(dw); }); } dialog::display(); }); dialog::addItem("furthest", 'f'); dialog::add_action([m] { auto *c = first_tcell; tcell *furthest = c; while(c) { if(c->unified_to.at == c) if(c->dist < MYSTERY && c->dist > furthest->dist) furthest = c; c = c->next; } println(hlog, "furthest in distance = ", furthest->dist); move_to(furthest); }); dialog::addItem("print rules", 'P'); dialog::add_action(print_rules); dialog::addItem("clean data", 'c'); dialog::add_action(clean_data); dialog::addItem("clean parents", 'p'); dialog::add_action(clean_parents); dialog::addItem("irradiate", 'i'); dialog::add_action(irradiate); dialog::addItem("irradiate x10", 'I'); dialog::add_action([] { for(int i=0; i<10; i++) irradiate(); }); dialog::addItem("record givers", 'g'); dialog::add_action([] { old_givers.clear(); for(int i=0; icounterpart[cwt.at->master]); }); dialog::addItem("name", 'n'); dialog::add_action([m] { println(hlog, "name = ", index_pointer(m->counterpart[cwt.at->master])); }); dialog::display(); } color_t label_color = 1, wall_color = 0, tree_color = 0xFFFFFFFF, out_color = 0xFF0000FF; bool show_codes = true; bool show_dist = true; 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->dist == MYSTERY ? "?" : its(tc->dist)); if(show_codes) { int code = tc->code; if(code != MYSTERY_LARGE) code = all_analyzers[code]->dir; else code = MYSTERY; string codestr = (code == MYSTERY ? "?" : its(code)); if(show_dist) label = label + "/" + codestr; else label = codestr; } color_t col = label_color == 1 ? 0xFFFFFF + 0x512960 * tc->code : label_color; if(pointer_indices.count(tc)) label += " " + index_pointer(tc); queuestr(V, 0.4, label, col, 1); if(tree_color) { if(tc->parent_dir >= 0 && tc->parent_dir < tc->type) { vid.linewidth *= 8; queueline(V * C0, V * currentmap->adj(c, tc->parent_dir) * C0, tree_color); vid.linewidth /= 8; } } if(sprawl_shown.count(tc)) queuepoly(V, cgi.shDisk, 0xFF0000FF); if(out_color) { for(int i=0; itype; i++) if(!tc->move(i)) { vid.linewidth *= 8; hyperpoint h1 = currentmap->get_corner(c, (i+1)%tc->type); hyperpoint h2 = currentmap->get_corner(c, i); queueline(V * h1, V * h2, 0xFF0000FF); vid.linewidth /= 8; } } return false; }); vector dh; dh.push_back(addHook(hooks_o_key, 15, [m] (o_funcs& v) { /* v.push_back(named_functionality("mark", [m] { for(auto c: marklist) m->clone(c.at)->c7->item = itGold; })); */ v.push_back(named_dialog("debug menu", debug_menu)); })); 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); } hstream *seq_stream, *test_out; void list_all_sequences(string tesname) { int N = isize(treestates); if(!N) { println(hlog, "no states generated"); return; } for(int i=0; i howmany(N); howmany[i] = 1; vector seq; for(int iter=0; iter<60; 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(*seq_stream, (hyperbolic ? "H " : "E "), "FILE ", tesname, ", id "); println(*seq_stream, seq); } seq_stream->flush(); } void view_actual_seq(int max) { celllister cl(cwt.at, 1000, max, nullptr); vector dlist(1000, 0); for(auto d: cl.dists) dlist[d]++; while(dlist.back() == 0) dlist.pop_back(); println(hlog, "obtained dlist = ", dlist); } 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 if(r < -100) return "S"+its(r); else return its(r); } void print_rules() { for(int i=0; i longest_shortcut() { int res = 0; int qty = 0; for(auto& p: shortcuts) for(auto& v: p) { res = max(res, isize(v->pre)); qty++; } return {qty, res}; } int total_analyzers() { return isize(all_analyzers); } int shape_edges() { int res = 0; for(auto& sh: arb::current.shapes) res += sh.size(); return res; } int max_edge() { int res = 0; for(auto& sh: arb::current.shapes) res = max(res, sh.size()); return res; } int max_valence() { int res = 0; for(auto& sh: arb::current.shapes) for(auto& va: sh.vertex_valence) res = max(res, va); return res; } bool same_shape_at(const arb::shape& s1, const arb::shape& s2, int i) { auto N = s1.size(); for(int j=0; j 1e-6) return false; if(abs(s1.angles[j] - s2.angles[(i+j)%N]) > 1e-6) return false; } return true; } bool same_shape_inv_at(const arb::shape& s1, const arb::shape& s2, int i) { auto N = s1.size(); for(int j=0; j 1e-6) return false; if(abs(s1.angles[(N*2-2-j) % N] - s2.angles[(i+j)%N]) > 1e-6) return false; } return true; } bool same_shape(arb::shape& s1, arb::shape& s2, bool sym) { if(s1.size() != s2.size()) return false; for(int i=0; i dsi; for(auto& sh: arb::current.shapes) { for(auto& ksh: dsi) if(same_shape(sh, *ksh, sym)) goto next; dsi.push_back(&sh); next: ; } return isize(dsi); } int count_vertex_orbits() { double t = 0; for(auto& sh: arb::current.shapes) { for(int i=0; i .01) throw hr_exception("count_vertex_orbits error"); return res; } int count_edge_orbits() { int eo = 0; for(auto& sh: arb::current.shapes) eo += sh.cycle_length; return eo; } vector normalize_anglelist(vector v, bool sym) { for(auto& va: v) va = int(va * 1000000 + .5); vector res = v; for(int r=0; r<2; r++) { for(int u=0; u> seen; for(auto& sh: arb::current.shapes) for(auto& al: sh.vertex_angles) { al = normalize_anglelist(al, sym); seen.insert(al); } return isize(seen); } EX long long get_shapelist() { long long res = 0; for(auto& sh: arb::current.shapes) res |= 1ll << min(sh.size(), 61); return res; } EX long long get_valence_list() { long long res = 0; for(auto& sh: arb::current.shapes) for(auto& vv: sh.vertex_valence) res |= 1ll << min(vv, 61); return res; } int count_different_edges() { vector seen; for(auto& sh: arb::current.shapes) for(auto& e: sh.edges) seen.push_back(e); sort(seen.begin(), seen.end()); int res = 1; for(int i=1; i seen[i-1] + 1e-5) res++; return res; } string count_uniform() { auto& sh = arb::current.shapes; int N = sh.size(); vector starts; int qty = 0; for(auto& s: sh) { starts.push_back(qty); qty += s.cycle_length * 2; } // for(auto s: sh) println(hlog, "CSV;clen: ", s.cycle_length); // println(hlog, "CSV;size: ", starts, " N=", N); vector rtile(qty), rvert(qty, -4), redge(qty), rangle(qty); for(int i=0; i eq_class(qty, 0); int num_eq_class = 1; int last_num_eq_class = 0; while (num_eq_class > last_num_eq_class) { // println(hlog, "CSV;", eq_class); using vertex_data = std::array; std::vector > data(qty); last_num_eq_class = num_eq_class; for (int i = 0; i < qty; i++) { data[i].first[0] = eq_class[i]; data[i].first[1] = rangle[i]; data[i].first[2] = redge[i]; data[i].first[3] = eq_class[rtile[i]]; data[i].first[4] = eq_class[rvert[i]]; data[i].first[5] = eq_class[i^1]; data[i].second = i; } sort(data.begin(), data.end()); eq_class[data[0].second] = 0; num_eq_class = 0; for (int i = 1; i < qty; i++) { if (data[i].first != data[i - 1].first) num_eq_class++; eq_class[data[i].second] = num_eq_class; } num_eq_class++; } std::vector reps(num_eq_class, -1); for(int i=0; i= i1 && i >= i2 && i >= i3) num_edges_sym++; if(i >= i1) num_edges_ev++; if(i >= i2) num_edges_et++; if(i >= i3) num_edges_ez++; } for (int i: reps) { if(rtile[i] >= 0) { num_tile++; int maxj = i; int j = i; while(rtile[j] >= 0) maxj = max(maxj, j), tie(j, rtile[j]) = make_pair(reps[eq_class[rtile[j]]], -1); if(maxj&1) num_tile_sym++; // println(hlog, "CSV;found ", i); } if(rvert[i] >= 0) { num_vert++; int maxj = i; int j = i; while(rvert[j] >= 0) maxj = max(maxj, j), tie(j, rvert[j]) = make_pair(reps[eq_class[rvert[j]]], -1); if(maxj&1) num_vert_sym++; } } // println(hlog, "CSV;eq_class = ", eq_class); // println(hlog, "CSV;", lalign(0, num_tile, ";", num_vert, ";", num_tile_sym, ";", num_vert_sym, ";", num_edges), " = tv/stv/e"); return lalign(0, num_tile, ";", num_vert, ";", num_edges, ";", num_tile_sym, ";", num_vert_sym, ";", num_edges_sym,";", num_edges_ev, ";", num_edges_et, ";", num_edges_ez); } sem_t sem; int max_children = 7; bool forked; void setup_fork(int m, string fname) { max_children = m; forked = true; sem_init(&sem, true, 1); test_out = new fhstream(fname, "wt"); } int max_dist() { int result = -1; tcell* c1 = first_tcell; while(c1) { if(c1->dist != MYSTERY && c1->dist > result) result = c1->dist; c1 = c1->next; } return result; } void test_current(string tesname) { disable_bigstuff = true; worst_precision_error = 0; stop_game(); pointer_indices.clear(); // if(s.find("884-211-045") == string::npos) return; start_game(); string t = testdir + "/"; for(char c: tesname) if(isalnum(c)) t += c; else if(c == ',') t += "_"; else if(c == ' ') t += "__"; else if(c == '(') t += "op"; else if(c == '(') t += "cp"; else if(c == '[') t += "ob"; else if(c == '[') t += "cb"; else if(c == '/') t += "__"; if(euclid) pconf.scale = .1; if(hyperbolic) pconf.scale = .95; if(sphere) pconf.scale = .5; shot::transparent = hyperbolic; println(hlog, "TESTING: ", tesname, " AS: ", t); indenter ind(2); if(draw_which & 1) { View = Id; shot::take(t+"-old.png"); } string status, message; bool ok = false; // make print_rules() not crash in case of a conversion error treestates.clear(); /* we do not want to include the conversion time */ if(!arb::in() && WDIM != 3) try { arb::convert::convert(); arb::convert::activate(); } catch(hr_exception& e) { println(hlog, "CSV; failed to convert ", tesname); return; } if(flags & w_known_structure) { dynamicval f(rulegen::flags, sub_rulegen_flags); prepare_rules(); if(!known()) return; alt_treestates = treestates; pointer_indices.clear(); } int tstart = SDL_GetTicks(); int attempts = 0; double max_time = 0, avg_time = 0, variance_time = 0; auto begin = clock(); // std::chrono::high_resolution_clock::now(); auto last = begin; try { while(!attempts) { // || (clock() < begin + 0.1 * CLOCKS_PER_SEC && attempts < 1000)) { if(true) { rulegen::delete_tmap(); rulegen::clear_all(); last = clock(); rulegen::movecount = 0; } generate_rules(); auto cur = clock(); double t = (cur - last) * 1. / CLOCKS_PER_SEC; last = cur; if(t > max_time) max_time = t; avg_time += t; variance_time += t * t; attempts++; } status = "ACC"; message = "OK"; ok = true; rules_known_for = arb::current.name; } catch(rulegen_surrender& e) { println(hlog, "surrender: ** ", e.what()); status = "SUR"; message = e.what(); } catch(rulegen_retry& e) { println(hlog, "try exceeded: ** ", e.what()); status = "TRY"; message = e.what(); } catch(rulegen_failure& e) { println(hlog, "error: ** ", e.what()); status = "ERR"; message = e.what(); } catch(hr_precision_error& e) { println(hlog, "precision error: ** ", e.what()); status = "PRE"; message = e.what(); } if(!attempts) { auto cur = clock(); double t = (cur - last) * 1. / CLOCKS_PER_SEC; avg_time += t; variance_time += t*t; max_time = t; attempts = 1; } avg_time /= attempts; variance_time /= attempts; variance_time -= avg_time * avg_time; if(attempts > 1) { variance_time *= attempts; variance_time /= (attempts-1); } auto end = clock(); // std::chrono::high_resolution_clock::now(); if(t_origin.size() && (draw_which & 2)) { restart_game_on(new hrmap_testproto); view_debug(); View = Id; shot::take(t+"-orig.png"); clear_debug(); } if(ok && (draw_which & 4)) { restart_game_on(new_hrmap_rulegen()); view_debug(); View = Id; shot::take(t+"-rule.png"); clear_debug(); } tstart = SDL_GetTicks() - tstart; int qsolid = 0, qcode = 0, qdist = 0; auto *c = first_tcell; while(c) { if(c->is_solid) qsolid++; if(c->code != MYSTERY) qcode++; if(c->dist != MYSTERY) qdist++; c = c->next; } vector areas; for(auto& sh: arb::current.shapes) { if(hyperbolic) { 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); } else { ld s = 0; for(int i=0; i edgelens; for(auto& sh: arb::current.shapes) { for(int i=0; i(end-begin).count() / 1000000000.); case 'P': Out("Tp", (end-begin) * 1. / CLOCKS_PER_SEC); case 'N': Out("attempts", attempts); case 'M': Out("maxtime", max_time); case 'E': Out("avgtime", avg_time); case 'V': Out("vartime", variance_time); case 'y': Out("tree", isize(treestates)); case 'a': Out("amin;amax", lalign(0, areas[0], ";", areas.back())); case 'j': Out("emin;emax", lalign(0, edgelens[0], ";", edgelens.back())); case 'h': Out("shapes", isize(arb::current.shapes)); case 'e': Out("edges", shape_edges()); case 'W': Out("max_valence;max_edge", lalign(0, max_valence(), ";", max_edge())); case 'D': Out("dshapes;dverts;dedges;bshapes;bverts", lalign(0, count_different_shapes(true), ";", count_different_vertices(true), ";", count_different_edges(), ";", count_different_shapes(false), ";", count_different_vertices(false))); case 'O': Out("overts;oedges", lalign(0, count_vertex_orbits(), ";", count_edge_orbits())); case 'U': Out("vshapes;vverts;vedges;ushapes;uverts;uedges;xea;xeb;xec", count_uniform()); case 'L': Out("mirror_rules", arb::current.mirror_rules); case 'B': Out("listshape;listvalence", format("%lld;%lld", get_shapelist(), get_valence_list())); case 'F': Out("maxdist", max_dist()); case 'f': Out("file", tesname); case 'l': Out("shortcut", longest_shortcut()); case '3': Out("shqty", longest_shortcut().first); case '4': Out("shlong", longest_shortcut().second); case 'A': Out("analyzer", total_analyzers()); case 'H': Out("hard", hard_parents); case '1': Out("single", single_live_branches); case '2': Out("double", double_live_branches); case 'p': Out("premini", states_premini); case 'K': Out("movecount", format("%ld", rulegen::movecount)); } println(*test_out); test_out->flush(); if(add_header) { add_header = false; goto again; } // for(auto& sh: shortcuts) println(hlog, sh.first, " : ", isize(sh.second), " shortcuts (CSV)"); if(status == "ACC" && !forked) print_rules(); if(status != "ACC") treestates = alt_treestates; /* for(auto& a: analyzers) println(hlog, "analyzer ", a.first, " size is ", isize(a.second.spread)); */ fflush(stdout); if(seq_stream) list_all_sequences(tesname); test_out->flush(); if(forked) sem_post(&sem); } 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 set_arcm(eVariation v, string symbol) { auto& c = arcm::current; c.parse(symbol); c.compute_geometry(); set_geometry(gArchimedean); set_variation(v); if(c.errors) { println(hlog, "ERROR: incorrect Archimedean symbol ", symbol); exit(1); } } bool set_general(const string& s) { stop_game(); arb::current.name = s; if(s[0] == 'X') { int a, b, c, d; sscanf(s.c_str()+2, "%d%d%d%d", &a, &b, &c, &d); geometry = eGeometry(a); variation = eVariation(b); gp::param = {c, d}; println(hlog, "parsed ", s, " as ", full_geometry_name()); } else if(s[0] == 'P') set_arcm(eVariation::pure, s.c_str()+2); else if(s[0] == 'D') set_arcm(eVariation::dual, s.c_str()+2); else if(s[0] == 'B') set_arcm(eVariation::bitruncated, s.c_str()+2); else try { set_geometry(gArbitrary); arb::load(s); } catch(hr_parse_exception& ex) { println(hlog, "failed: ", ex.s); return false; } catch(arb::hr_polygon_error& ex) { println(hlog, "poly error"); return false; } catch(hr_exception& ex) { println(hlog, "other exception"); return false; } if(cgflags & qAFFINE) { println(hlog, "illegal tessellation found: affine"); return false; } if(arb::current.is_star) { println(hlog, "illegal tessellation found: star"); return false; } if(arb::current.have_tree) { println(hlog, "illegal tessellation found: tree"); return false; } if(sphere) { println(hlog, "illegal tessellation found: spherical"); return false; } return true; } void test_from_file(string list) { dynamicval df(floorshapes_level); if(specialland == laCanvas) floorshapes_level = 1; set_dir(list); vector filenames; std::ifstream is("devmods/rulegen-tests/" + list + ".lst"); string s; while(getline(is, s)) { while(s != "" && s[0] == ' ') s = s.substr(1); if(s != "" && s[0] != '#') filenames.push_back(s); } int trv = test_rotate_val; int id = 0; int children = 0; fflush(stdout); for(const string& s: filenames) { println(hlog, "loading ", s, "... ", id++, "/", isize(filenames)); println(hlog, "START: ", s); fflush(stdout); if(trv) { trv--; id++; continue; } if(forked && id > 1) { int pid; if(children >= max_children) { wait(&pid); children--; } if((pid = fork())) children++; else goto doit; continue; } doit: if(set_general(s)) test_current(s); println(hlog, "DONE: ", s); fflush(stdout); if(forked && id > 1) exit(0); if(forked && id == 1) stop_game(); } while(children) { int pid; wait(&pid); children--; } } void rulecat(string list) { set_dir(list); vector filenames; std::ifstream is("devmods/rulegen-tests/" + list + ".lst"); string s; while(getline(is, s)) { while(s != "" && s[0] == ' ') s = s.substr(1); if(s != "" && s[0] != '#') filenames.push_back(s); } for(const string& s: filenames) { string cat1; /* if(s[0] == 'X' && s[4] == '1') cat1 = "regular"; else if(s[0] == 'X' && s[4] == '0') cat1 = "bitruncated"; else if(s[0] == 'X' && s[4] == '5' && s[6] == 1 && s[7] == 0) cat1 = "regular"; */ if(0) ; else if(s[0] == 'X') cat1 = "variations"; /* else if(s[0] == 'P' && is_reg(s)) cat1 = "regular"; else if(s[0] == 'D' && is_reg(s)) cat1 = "regular"; else if(s[0] == 'B' && is_reg(s)) cat1 = "bitruncated"; */ else if(s[0] == 'P') cat1 = "archimedean"; else if(s[0] == 'D') cat1 = "lavasz"; else if(s[0] == 'B') cat1 = "archibi"; else { int i = 0; while(s[i] != '/') i++; i++; int i1 = i; while(s[i] != '/') i++; cat1 = s.substr(i1, i-i1); if(cat1 == "multitile" && s.substr(i+1, 9) == "polyforms") cat1 = "polyforms"; } printf("CSV;%s;%s\n", cat1.c_str(), s.c_str()); } } void label_all(int i, int mode) { queue to_label; auto m = dynamic_cast (currentmap); set visited; tcell *next_dist = nullptr; int cdist = 0; auto visit = [&] (tcell *c) { if(visited.count(c)) return; visited.insert(c); to_label.push(c); if(!next_dist) next_dist = c; }; visit(m->counterpart[cwt.at->master]); while(true) { tcell *c = to_label.front(); if(c == next_dist) { println(hlog, "next distance for ", c); next_dist = nullptr; cdist++; if(cdist == i) break; } try { twalker cw(c, 0); if(mode & 4) index_pointer(c); if(mode & 1) get_parent_dir(cw); if(mode & 2) get_code(cw); } catch(hr_exception& ex) { } to_label.pop(); for(int i=0; itype; i++) visit(c->cmove(i)); } cleanup_protomap(); } void seek_label(string s) { auto *c = first_tcell; while(c) { if(pointer_indices.count(c) && index_pointer(c) == s) { move_to(c); return; } c = c->next; } println(hlog, "not found"); } void tesgen(string s) { set_general(s); if(!arb::in()) try { arb::convert::convert(); arb::convert::activate(); } catch(hr_exception& e) { println(hlog, "failed to convert ", s); } } std::mutex lock; std::condition_variable cv; shared_ptr anim_thread; int state; // 0 = computing, 1 = animating, 2 = finished, 3 = post-finished struct edgedata { int type; twalker tw; int gtick; int ftick; }; struct animdata { map where; vector data; }; animdata ad; void seek(twalker tw, int gt) { for(auto& d: ad.data) if(d.tw == tw && d.ftick == -1) d.ftick = gt; } void pcurvepoint(hyperpoint h) { hyperpoint last = glhr::gltopoint(curvedata.back()); if(hdist(last, h) > .2) { pcurvepoint(mid(h, last)); pcurvepoint(h); } else curvepoint(h); } map marked; void drawline(twalker tw, color_t col) { vid.linewidth *= 3; queueline(ad.where[tw.peek()] * C0, ad.where[tw.at] * C0, col, 4, PPR::FLOOR); vid.linewidth /= 3; auto tw1 = tw+wstep; if(tw.at->parent_dir == tw.spin || tw1.at->parent_dir == tw1.spin) { shiftmatrix& M1 = ad.where[tw.at]; shiftmatrix& M2 = ad.where[tw.peek()]; transmatrix pre = inverse_shift(M1, M2); auto& sh = arb::current_or_slided().shapes[tw.at->id]; auto& sh1 = arb::current_or_slided().shapes[tw1.at->id]; curvepoint(C0); pcurvepoint(normalize(5 * C0 + sh.vertices[tw.spin])); pcurvepoint(pre * normalize(5 * C0 + sh1.vertices[(tw1.spin+1)%tw1.at->type])); pcurvepoint(pre * C0); pcurvepoint(pre * normalize(5 * C0 + sh1.vertices[tw1.spin])); pcurvepoint(normalize(5 * C0 + sh.vertices[(tw.spin+1)%tw.at->type])); pcurvepoint(C0); queuecurve(M1, 0, 0xFF000080, PPR::LINE); } } void animate_draw() { ad.where.clear(); for(auto& p: ad.data) { if(p.type == 0) ad.where[p.tw.at] = ggmatrix(cwt.at); // shiftless(Id); else if(p.type == 1) { transmatrix T = arb::get_adj(arb::current_or_slided(), p.tw.peek()->id, (p.tw+wstep).spin, -1, p.tw.spin); transmatrix prespin = rspintox(tC0(T)); T = spintox(tC0(T)) * T; ld length = hdist0(tC0(T)); T = xpush(-length) * T; ld age = min(ticks - p.gtick, 1000); ld extension = lerp(3, 1.2, age / 1000.); color_t col = gradient(0, 0xFFFF, 0, age, 1000); if(p.ftick != -1) { age = min(ticks - p.ftick, 1000); extension = lerp(extension, 1, age / 1000.); col = gradient(col, 0xFFFFFFFF, 0, age, 1000); } ad.where[p.tw.at] = ad.where[p.tw.peek()] * prespin * xpush(length * extension) * T; drawline(p.tw, col); } else if(p.type == 2) { auto tw1 = p.tw; do { seek(tw1, p.gtick); seek(tw1+wstep, p.gtick); tw1 = tw1 + wstep - 1; } while(p.tw != tw1); ld age = min(ticks - p.gtick, 1000); color_t col = gradient(0, 0xFFFFFFFF, 0, age, 1000); drawline(p.tw, col); } else if(p.type == 3 || p.type == 4) { for(int i=0; i lk(lock); cv.wait(lk, [] { return state == 1 || state == 2; }); } int f = addHook(hooks_frame, 100, animate_draw); println(hlog, "f = ", f); vid.cells_drawn_limit = 0; mapeditor::drawplayer = false; cwt.at->wall = waChasm; no_find_player = true; int *k = new int; *k = addHook(hooks_handleKey, 0, [i, k, f] (int sym, int uni) { if(uni == 'y' && state == 2) { println(hlog, "on finished"); anim_thread->join(); anim_thread = nullptr; delHook(hooks_gen_tcell, i); println(hlog, "deleting handleKey hook at ", *k); int pk = *k; delete k; delHook(hooks_handleKey, pk); delHook(hooks_frame, f); println(hlog, "finished"); return true; } if(uni == 't') { if(state == 2) { println(hlog, "finished!"); return true; } else if(state == 3) { println(hlog, "wrong state"); return true; } else { println(hlog, "waiting..."); wait_one_step(); return true; } } return false; }); } void animate_to(int i) { int steps = 0; while(ad.data.back().type != i) { if(state != 1) break; wait_one_step(); steps++; } println(hlog, "steps = ", steps); } void animate_steps(int i) { while(i--) { if(state != 1) break; wait_one_step(); } println(hlog, "after ", i, " steps"); } 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(arb::current.filename); } catch(rulegen_failure& e) { } } else if(argis("-test-list")) { PHASEFROM(3); shift(); test_from_file(args()); } else if(argis("-test-fork")) { PHASEFROM(3); shift(); int i = argi(); shift(); setup_fork(i, args()); } else if(argis("-rulecat")) { PHASEFROM(3); shift(); rulecat(args()); } else if(argis("-trv")) { shift(); test_rotate_val = argi(); } else if(argis("-ruleflag-sub")) { swap(rulegen::flags, sub_rulegen_flags); } else if(argis("-view-debug")) view_debug(); else if(argis("-print-rules")) print_rules(); else if(argis("-clear-debug")) clear_debug(); /* tools for taking screenshots -- after testproto */ else if(argis("-label-all")) { shift(); int q = argi(); shift(); int mode = argi(); label_all(q, mode); } else if(argis("-label-clean")) { pointer_indices.clear(); } else if(argis("-label-seek")) { shift(); seek_label(args()); } else if(argis("-color-this")) { auto m = dynamic_cast (currentmap); shift(); color_t hex = arghex(); println(hlog, "assigning ", hex, " to ", m->counterpart[cwt.at->master]); setdist(cwt.at, 7, nullptr); cwt.at->landparam = hex; } else if(argis("-color-label")) { shift(); label_color = arghex(); } else if(argis("-color-wall")) { shift(); wall_color = arghex(); } else if(argis("-color-out")) { shift(); out_color = arghex(); } else if(argis("-color-tree")) { shift(); tree_color = arghex(); } else if(argis("-draw-which")) { shift(); draw_which = argi(); } else if(argis("-no-codes")) { show_codes = false; } else if(argis("-no-dist")) { show_dist = false; } else if(argis("-dseek-start")) { shift(); move_to(t_origin[argi()]); } else if(argis("-dseek-giver")) { shift(); move_to(treestates[argi()].giver); } else if(argis("-origin-id")) { shift(); origin_id = argi(); } else if(argis("-seqf")) { shift(); seq_stream = new fhstream(args(), "w"); } else if(argis("-tesgen")) { shift(); tesgen(args()); } else if(argis("-tes-animate")) { animate(); } else if(argis("-tes-animate-to")) { shift(); animate_to(argi()); } else if(argis("-tes-animate-steps")) { shift(); animate_steps(argi()); } else if(argis("-tes-animate-marked")) { shift(); int i = argi(); shift(); color_t col = arghex(); marked[ad.data[i].tw.at] = col; } else if(argis("-veb")) { rulegen::rdebug_flags |= 32; } else if(argis("-act-seq")) { start_game(); shift(); view_actual_seq(argi()); } else if(argis("-dseek")) { shift(); int i = argi(); if(i >= 0 && i < isize(debuglist)) { auto m = dynamic_cast (currentmap); cwt = {m->clone(debuglist[i].at)->c7, debuglist[i].spin}; centerover = cwt.at; View = Id; } else println(hlog, "wrong dseek index"); } else return 1; return 0; } auto testhooks = addHook(hooks_args, 100, testargs); } }