1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-01-22 23:17:04 +00:00

new analyzers, based on decision trees

This commit is contained in:
Zeno Rogue 2021-12-27 13:03:15 +01:00
parent f43f80b0ad
commit 94e06b3bbf

View File

@ -579,7 +579,7 @@ EX void find_new_shortcuts(tcell *c, int d, tcell *alt, int newdir, int delta) {
} }
EX void remove_parentdir(tcell *c) { EX void remove_parentdir(tcell *c) {
sidecache.clear(); clear_sidecache_and_codes();
if(c->parent_dir) c->old_parent_dir = c->parent_dir; if(c->parent_dir) c->old_parent_dir = c->parent_dir;
c->parent_dir = MYSTERY; c->parent_dir = MYSTERY;
c->code = MYSTERY; c->code = MYSTERY;
@ -630,10 +630,9 @@ EX void fix_distances(tcell *c) {
if(tgt->is_solid) if(tgt->is_solid)
find_new_shortcuts(tgt, new_d, tgt, tgtw.spin, 0); find_new_shortcuts(tgt, new_d, tgt, tgtw.spin, 0);
ufind(tgtw); tgt = tgtw.at; ufind(tgtw); tgt = tgtw.at;
tgt_d = new_d;
sidecache.clear();
tgt->any_nearer = tgtw.spin;
remove_parentdir(tgt); remove_parentdir(tgt);
tgt_d = new_d;
tgt->any_nearer = tgtw.spin;
return true; return true;
} }
return false; return false;
@ -671,7 +670,7 @@ EX void handle_distance_errors() {
bool b = solid_errors; bool b = solid_errors;
solid_errors = 0; solid_errors = 0;
if(b && !no_errors) { if(b && !no_errors) {
sidecache.clear(); clear_sidecache_and_codes();
if(flags & w_always_clean) clean_data(); if(flags & w_always_clean) clean_data();
debuglist = solid_errors_list; debuglist = solid_errors_list;
solid_errors_list = {}; solid_errors_list = {};
@ -951,26 +950,27 @@ EX twalker get_parent_dir(twalker& cw) {
#if HDR #if HDR
using aid_t = pair<int, int>; using aid_t = pair<int, int>;
struct analyzer { struct analyzer_state {
vector<twalker> spread; int analyzer_id;
vector<int> parent_id; int id, dir;
vector<int> spin; array<analyzer_state*, 10> substates;
void add_step(int pid, int s); analyzer_state() { id = MYSTERY; dir = MYSTERY; for(int i=0; i<10; i++) substates[i] = nullptr; }
vector<twalker> inhabitants;
}; };
#endif #endif
void analyzer::add_step(int pid, int s) { int next_analyzer_id;
twalker cw = spread[pid];
cw = cw + s;
cw.peek();
ufind(cw);
cw = cw + wstep;
spread.push_back(cw);
parent_id.push_back(pid);
spin.push_back(s);
}
EX map<aid_t, analyzer> analyzers; EX map<aid_t, analyzer_state*> analyzers;
EX vector<analyzer_state*> all_analyzers;
analyzer_state *alloc_analyzer() {
auto a = new analyzer_state;
a->analyzer_id = next_analyzer_id++;
all_analyzers.push_back(a);
return a;
}
EX aid_t get_aid(twalker cw) { EX aid_t get_aid(twalker cw) {
ufind(cw); ufind(cw);
@ -978,76 +978,64 @@ EX aid_t get_aid(twalker cw) {
return {ide, gmod(cw.to_spin(0), arb::current.shapes[ide].cycle_length)}; return {ide, gmod(cw.to_spin(0), arb::current.shapes[ide].cycle_length)};
} }
EX analyzer& get_analyzer(twalker cw) { void extend_analyzer(twalker cwmain, int z, twalker giver) {
auto aid = get_aid(cw); ufind(giver);
auto& a = analyzers[aid]; ufind(cwmain);
if(a.spread.empty()) {
a.spread.push_back(cw);
a.parent_id.push_back(-1);
a.spin.push_back(-1);
for(int i=0; i<cw.at->type; i++)
a.add_step(0, i);
}
return a;
}
EX vector<twalker> spread(analyzer& a, twalker cw) { vector<twalker> giver_sprawl, main_sprawl, sub_sprawl;
vector<twalker> res; vector<analyzer_state*> giver_states, main_states, sub_states;
int N = isize(a.spread);
res.reserve(N);
res.push_back(cw);
for(int i=1; i<N; i++) {
auto& r = res[a.parent_id[i]];
ufind(r);
auto r1 = r + a.spin[i];
r1.peek(); ufind(r1);
res.push_back(r1 + wstep);
}
return res;
}
void extend_analyzer(twalker cw_target, int dir, int id, int mism, twalker rg) { id_at_spin(cwmain, main_sprawl, main_states);
ufind(cw_target); ufind(rg); id_at_spin((cwmain+z)+wstep, sub_sprawl, sub_states);
if(debugflags & DF_GEOM) id_at_spin((giver+z)+wstep, giver_sprawl, giver_states);
println(hlog, "extend called, cw_target = ", cw_target);
twalker cw_conflict = cw_target + dir + wstep; int currently_at = 1+z;
auto &a_target = get_analyzer(cw_target);
auto &a_conflict = get_analyzer(cw_conflict); vector<int> idlist;
// twalker model = a_target.spread[0] + dir + wstep;
// auto res = spread(a_conflict, model); for(int i=0;; i++) {
vector<int> ids_to_add; if(i == isize(sub_states) || i == isize(giver_states))
int k = id; throw rulegen_failure("reached the end");
while(k) { if(giver_states[i] != sub_states[i]) {
ids_to_add.emplace_back(a_conflict.spin[k]); i--;
k = a_conflict.parent_id[k]; while(i != 0) {
} idlist.push_back(i);
int gid = 1 + dir; i = giver_states[i]->id;
bool added = false;
while(!ids_to_add.empty()) {
int spin = ids_to_add.back();
ids_to_add.pop_back();
int next_gid = -1;
for(int i=0; i<isize(a_target.parent_id); i++)
if(a_target.parent_id[i] == gid && a_target.spin[i] == spin) {
next_gid = i;
} }
if(next_gid == -1) { break;
next_gid = isize(a_target.parent_id);
a_target.add_step(gid, spin);
added = true;
} }
gid = next_gid;
} }
if(mism == 0 && !added) {
if(debugflags & DF_GEOM) println(hlog, "no extension"); reverse(idlist.begin(), idlist.end());
if(flags & w_no_queued_extensions)
/* in rare cases this happens due to unification or something */ auto v = main_states.back();
throw rulegen_retry("no extension"); auto v1 = v;
int new_id = isize(main_states)-1;
for(auto l: idlist) {
/* check if already tested */
for(int u=1; u<isize(main_states); u++)
if(main_states[u]->id == currently_at && main_states[u]->dir == sub_states[l]->dir) {
currently_at = u;
goto next_l;
}
v->id = currently_at;
v->dir = sub_states[l]->dir;
for(int i=0; i<10; i++) if(sub_states[l]->substates[i] == sub_states[l+1]) {
v = v->substates[i] = alloc_analyzer();
currently_at = new_id++;
goto next_l;
}
next_l: ;
} }
update_all_codes(v1);
} }
#if HDR #if HDR
using code_t = pair<aid_t, vector<int> >;
struct treestate { struct treestate {
int id; int id;
@ -1057,7 +1045,6 @@ struct treestate {
int sid; int sid;
int parent_dir; int parent_dir;
twalker where_seen; twalker where_seen;
code_t code;
bool is_live; bool is_live;
bool is_possible_parent; bool is_possible_parent;
bool is_root; bool is_root;
@ -1168,56 +1155,66 @@ int get_side(twalker what) {
return res; return res;
} }
code_t id_at_spin(twalker cw) { int move_code(twalker cs) {
code_t res; bool child = false;
ufind(cw); if(cs.at->dist) {
res.first = get_aid(cw); auto csd = get_parent_dir(cs);
auto& a = get_analyzer(cw); child = cs == csd;
vector<twalker> sprawl = spread(a, cw); }
int id = 0; if(child)
for(auto cs: sprawl) { return C_CHILD;
be_solid(cs.at); else {
be_solid(cw.at); auto cs2 = cs + wstep;
ufind(cw); be_solid(cs.at); ufind(cs); ufind(cs2); be_solid(cs2.at);
ufind(cs); fix_distances(cs.at);
int x; int y = cs.at->dist - cs.peek()->dist;
int pid = a.parent_id[id];
if(pid > -1 && (res.second[pid] != C_CHILD)) {
x = C_IGNORE;
}
else if(id == 0) x = C_CHILD;
else {
bool child = false;
if(cs.at->dist) {
auto csd = get_parent_dir(cs);
child = cs == csd;
}
if(child)
x = C_CHILD;
else {
auto cs2 = cs + wstep;
ufind(cs); ufind(cs2); be_solid(cs2.at);
fix_distances(cs.at);
int y = cs.at->dist - cs.peek()->dist;
if(!(flags & w_no_relative_distance)) x = C_EQUAL; int x;
else if(y == 1) x = C_NEPHEW;
else if(y == 0) x = C_EQUAL; if(!(flags & w_no_relative_distance)) x = C_EQUAL;
else if(y == -1) x = C_UNCLE; else if(y == 1) x = C_NEPHEW;
else throw rulegen_failure("distance problem y=" + its(y) + lalign(0, " cs=", cs, " cs2=", cs2, " peek=", cs.peek(), " dist=", cs.at->dist, " dist2=", cs2.at->dist)); else if(y == 0) x = C_EQUAL;
auto gs = get_side(cs); else if(y == -1) x = C_UNCLE;
if(gs == 0 && x == C_UNCLE) x = C_PARENT; else throw rulegen_failure("distance problem y=" + its(y) + lalign(0, " cs=", cs, " cs2=", cs2, " peek=", cs.peek(), " dist=", cs.at->dist, " dist2=", cs2.at->dist));
if(gs > 0) x++; auto gs = get_side(cs);
} if(gs == 0 && x == C_UNCLE) x = C_PARENT;
} if(gs > 0) x++;
res.second.push_back(x); return x;
id++; }
} }
return res;
EX void id_at_spin(twalker cw, vector<twalker>& sprawl, vector<analyzer_state*>& states) {
ufind(cw);
auto aid = get_aid(cw);
auto a_ptr = &(analyzers[aid]);
sprawl = { cw };
states = { nullptr };
indenter ind(2);
while(true) {
auto& a = *a_ptr;
if(!a) {
a = alloc_analyzer();
}
states.push_back(a);
if(isize(sprawl) <= cw.at->type) {
a->id = 0, a->dir = isize(sprawl)-1;
// println(hlog, "need to go in direction ", a->dir);
}
if(a->id == MYSTERY) {
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);
a_ptr = &(a->substates[mc]);
}
} }
map<code_t, int> code_to_id;
EX pair<int, int> get_code(twalker& cw) { EX pair<int, int> get_code(twalker& cw) {
tcell *c = cw.at; tcell *c = cw.at;
if(c->code != MYSTERY && c->parent_dir != MYSTERY) { if(c->code != MYSTERY && c->parent_dir != MYSTERY) {
@ -1233,30 +1230,32 @@ EX pair<int, int> get_code(twalker& cw) {
indenter ind(2); indenter ind(2);
code_t v = id_at_spin(cd); static vector<twalker> sprawl;
static vector<analyzer_state*> states;
id_at_spin(cd, sprawl, states);
auto v = states.back();
if(code_to_id.count(v)) { v->inhabitants.push_back(cw);
cd.at->code = code_to_id[v];
return {cd.spin, code_to_id[v]};
}
int id = isize(treestates); cd.at->code = v->analyzer_id;
code_to_id[v] = id; return {cd.spin, v->analyzer_id};
if(cd.at->code != MYSTERY && (cd.at->code != id || cd.at->parent_dir != cd.spin)) { }
throw rulegen_retry("exit from get_code");
}
cd.at->code = id;
treestates.emplace_back(); EX pair<int, int> get_treestate_id(twalker& cw) {
auto& nts = treestates.back(); auto co = get_code(cw);
auto v = all_analyzers[co.second];
nts.id = id; if(v->dir == MYSTERY) {
nts.code = v; int id = isize(treestates);
nts.where_seen = cw; v->dir = id;
nts.known = false; treestates.emplace_back();
nts.is_live = true; auto& nts = treestates.back();
nts.id = id;
return {cd.spin, id}; nts.where_seen = cw;
nts.known = false;
nts.is_live = true;
}
co.second = v->dir;
return co;
} }
/* == rule generation == */ /* == rule generation == */
@ -1286,7 +1285,7 @@ vector<int> gen_rule(twalker cwmain, int id) {
be_solid(c1.at); be_solid(c1.at);
if(a == 0 && cwmain.at->dist) { cids.push_back(DIR_PARENT); continue; } if(a == 0 && cwmain.at->dist) { cids.push_back(DIR_PARENT); continue; }
if(c1.at->dist <= cwmain.at->dist) { cids.push_back(DIR_UNKNOWN); continue; } if(c1.at->dist <= cwmain.at->dist) { cids.push_back(DIR_UNKNOWN); continue; }
auto co = get_code(c1); auto co = get_treestate_id(c1);
auto& d1 = co.first; auto& d1 = co.first;
auto& id1 = co.second; auto& id1 = co.second;
if(c1.at->cmove(d1) != cwmain.at || c1.at->c.spin(d1) != front.spin) { if(c1.at->cmove(d1) != cwmain.at || c1.at->c.spin(d1) != front.spin) {
@ -1295,16 +1294,8 @@ vector<int> gen_rule(twalker cwmain, int id) {
cids.push_back(id1); cids.push_back(id1);
} }
for(int i=0; i<isize(cids); i++) if(cids[i] == DIR_UNKNOWN) { for(int i=0; i<isize(cids); i++) if(cids[i] == DIR_UNKNOWN)
int val = treestates[id].code.second[i+1]; cids[i] = get_side(cwmain+i) < 0 ? DIR_RIGHT : DIR_LEFT;
if(val < 2 || val >= 8) {
debuglist = { cwmain };
if(debugflags & DF_GEOM)
println(hlog, "i = ", i, " val = ", val, " code = ", treestates[id].code);
throw rulegen_retry("wrong code in gen_rule");
}
cids[i] = ((val & 1) ? DIR_RIGHT : DIR_LEFT);
}
return cids; return cids;
} }
@ -1320,7 +1311,7 @@ void handle_queued_extensions() {
void rules_iteration_for(twalker& cw) { void rules_iteration_for(twalker& cw) {
indenter ri(2); indenter ri(2);
ufind(cw); ufind(cw);
auto co = get_code(cw); auto co = get_treestate_id(cw);
auto& d = co.first; auto& d = co.first;
auto& id = co.second; auto& id = co.second;
twalker cwmain(cw.at, d); twalker cwmain(cw.at, d);
@ -1342,7 +1333,6 @@ void rules_iteration_for(twalker& cw) {
auto& r = ts.rules; auto& r = ts.rules;
if(debugflags & DF_GEOM) { if(debugflags & DF_GEOM) {
println(hlog, "merging ", ts.rules, " vs ", cids); println(hlog, "merging ", ts.rules, " vs ", cids);
println(hlog, "C ", treestates[id].code, " [", id, "]");
} }
int mismatches = 0; int mismatches = 0;
for(int z=0; z<isize(cids); z++) { for(int z=0; z<isize(cids); z++) {
@ -1352,43 +1342,22 @@ void rules_iteration_for(twalker& cw) {
throw rulegen_failure("neg rule mismatch"); throw rulegen_failure("neg rule mismatch");
} }
auto& c1 = treestates[r[z]].code.second; auto tg = ts.giver;
auto& c2 = treestates[cids[z]].code.second;
if(debugflags & DF_GEOM) {
println(hlog, "direction ", z, ":");
println(hlog, "A ", treestates[r[z]].code, " [", r[z], "]");
println(hlog, "B ", treestates[cids[z]].code, " [", cids[z], "]");
}
if(isize(c1) != isize(c2)) { if(!(flags & w_no_queued_extensions)) {
throw rulegen_failure("length mismatch"); queued_extensions.push_back([cwmain, z, tg] {
extend_analyzer(cwmain, z, tg);
});
return;
} }
for(int k=0; k<isize(c1); k++) {
if(c1[k] == C_IGNORE || c2[k] == C_IGNORE) continue;
if(c1[k] != c2[k]) {
if(debugflags & DF_GEOM) {
println(hlog, "code mismatch (", c1[k], " vs ", c2[k], " at position ", k, " out of ", isize(c1), ")");
println(hlog, "rulegiver = ", treestates[id].giver, " c = ", cwmain);
println(hlog, "gshvid = ", cw.at->id);
println(hlog, "cellcount = ", tcellcount, "-", tunified, " codes discovered = ", isize(treestates));
}
auto& a = get_analyzer(cw); extend_analyzer(cwmain, z, tg);
int q = isize(a.spread); mismatches++;
if(!(flags & w_no_queued_extensions)) {
queued_extensions.push_back([&a, q, cwmain, z, k, mismatches, id] { extend_analyzer(cwmain, z, k, mismatches, treestates[id].giver); });
return;
}
extend_analyzer(cwmain, z, k, mismatches, treestates[id].giver); debuglist = { cwmain, ts.giver };
mismatches++;
debuglist = { cwmain, ts.giver }; if(!(flags & w_conflict_all))
throw rulegen_retry("mismatch error");
if(!(flags & w_conflict_all))
throw rulegen_retry("mismatch error");
}
}
} }
debuglist = { cwmain, ts.giver }; debuglist = { cwmain, ts.giver };
@ -1459,8 +1428,6 @@ void minimize_rules() {
for(auto& r: ts.rules) for(auto& r: ts.rules)
if(r >= 0) r = new_id[r]; if(r >= 0) r = new_id[r];
} }
for(auto& p: code_to_id) p.second = new_id[p.second];
} }
} }
@ -1502,7 +1469,7 @@ void find_possible_parents() {
using tsinfo = pair<int, int>; using tsinfo = pair<int, int>;
tsinfo get_tsinfo(twalker& tw) { tsinfo get_tsinfo(twalker& tw) {
auto co = get_code(tw); auto co = get_treestate_id(tw);
int spin; int spin;
if(co.first == -1) spin = tw.spin; if(co.first == -1) spin = tw.spin;
else spin = gmod(tw.spin - co.first, tw.at->type); else spin = gmod(tw.spin - co.first, tw.at->type);
@ -1554,7 +1521,7 @@ set<conflict_id_type> branch_conflicts_seen;
void verified_treewalk(twalker& tw, int id, int dir) { void verified_treewalk(twalker& tw, int id, int dir) {
if(id >= 0) { if(id >= 0) {
auto tw1 = tw + wstep; auto tw1 = tw + wstep;
auto co = get_code(tw1); auto co = get_treestate_id(tw1);
if(co.second != id || co.first != tw1.spin) { if(co.second != id || co.first != tw1.spin) {
handle_distance_errors(); handle_distance_errors();
@ -1663,20 +1630,26 @@ void examine_branch(int id, int left, int right) {
/* == main algorithm == */ /* == main algorithm == */
bool need_clear_codes;
void clear_codes() { void clear_codes() {
treestates.clear(); need_clear_codes = false;
code_to_id.clear();
auto c = first_tcell; auto c = first_tcell;
while(c) { while(c) {
c->code = MYSTERY; c->code = MYSTERY;
c = c->next; c = c->next;
} }
for(auto a: all_analyzers) {
for(auto tw: a->inhabitants) tw.at->code = MYSTERY;
a->inhabitants.clear();
}
} }
void find_single_live_branch(twalker& at) { void find_single_live_branch(twalker& at) {
handle_distance_errors(); handle_distance_errors();
rules_iteration_for(at); rules_iteration_for(at);
int id = get_code(at).second; handle_queued_extensions();
int id = get_treestate_id(at).second;
int t = at.at->type; int t = at.at->type;
auto r = treestates[id].rules; /* no & because may move */ auto r = treestates[id].rules; /* no & because may move */
int q = 0; int q = 0;
@ -1693,18 +1666,46 @@ void find_single_live_branch(twalker& at) {
} }
} }
EX void clean_data() { EX void clean_analyzers() {
for(auto a: all_analyzers) delete a;
analyzers.clear(); analyzers.clear();
all_analyzers.clear();
next_analyzer_id = 0;
}
EX void clean_data() {
clean_analyzers();
important = t_origin; important = t_origin;
} }
EX void clean_parents() { EX void clear_sidecache_and_codes() {
clean_data();
sidecache.clear(); sidecache.clear();
need_clear_codes = true;
}
EX void update_all_codes(analyzer_state *a) {
vector<twalker> old;
swap(old, a->inhabitants);
for(auto tw: old) {
ufind(tw);
if(tw.at->code == a->analyzer_id)
tw.at->code = MYSTERY;
}
}
EX void clean_parents() {
clear_sidecache_and_codes();
clean_data();
auto c = first_tcell; auto c = first_tcell;
while(c) { c->parent_dir = MYSTERY; c = c->next; } while(c) { c->parent_dir = MYSTERY; c = c->next; }
} }
void clear_treestates() {
treestates.clear();
for(auto a: all_analyzers)
if(a->id == MYSTERY) a->dir = MYSTERY;
}
EX void rules_iteration() { EX void rules_iteration() {
try_count++; try_count++;
debuglist = {}; debuglist = {};
@ -1718,11 +1719,9 @@ EX void rules_iteration() {
if(debugflags & DF_GEOM) println(hlog, "attempt: ", try_count, " important = ", isize(important), " cells = ", tcellcount); if(debugflags & DF_GEOM) println(hlog, "attempt: ", try_count, " important = ", isize(important), " cells = ", tcellcount);
auto c = first_tcell;
while(c) { c->code = MYSTERY; c = c->next; }
clear_codes();
parent_updates = 0; parent_updates = 0;
clear_treestates();
if(need_clear_codes) clear_codes();
cq = important; cq = important;
@ -1736,7 +1735,7 @@ EX void rules_iteration() {
handle_distance_errors(); handle_distance_errors();
if(debugflags & DF_GEOM) if(debugflags & DF_GEOM)
println(hlog, "number of treestates = ", isize(treestates)); println(hlog, "number of treestates = ", isize(treestates));
rule_root = get_code(t_origin[0]).second; rule_root = get_treestate_id(t_origin[0]).second;
if(debugflags & DF_GEOM) if(debugflags & DF_GEOM)
println(hlog, "rule_root = ", rule_root); println(hlog, "rule_root = ", rule_root);
@ -1825,7 +1824,7 @@ EX void rules_iteration() {
if(debugflags & DF_GEOM) if(debugflags & DF_GEOM)
println(hlog, "changed single_live_branch_close_to_root from ", q, " to ", v); println(hlog, "changed single_live_branch_close_to_root from ", q, " to ", v);
debuglist = { treestates[id].giver }; debuglist = { treestates[id].giver };
sidecache.clear(); clear_sidecache_and_codes();
throw rulegen_retry("single live branch"); throw rulegen_retry("single live branch");
} }
if(treestates[id].is_root) examine_branch(id, last_live_branch, first_live_branch); if(treestates[id].is_root) examine_branch(id, last_live_branch, first_live_branch);
@ -1863,8 +1862,7 @@ void clear_tcell_data() {
EX void cleanup() { EX void cleanup() {
clear_tcell_data(); clear_tcell_data();
analyzers.clear(); clean_analyzers();
code_to_id.clear();
important.clear(); important.clear();
shortcuts.clear(); shortcuts.clear();
single_live_branch_close_to_root.clear(); single_live_branch_close_to_root.clear();
@ -1908,7 +1906,7 @@ EX void generate_rules() {
cell_to_tcell.clear(); cell_to_tcell.clear();
tcell_to_cell.clear(); tcell_to_cell.clear();
branch_conflicts_seen.clear(); branch_conflicts_seen.clear();
sidecache.clear(); clear_sidecache_and_codes();
fix_queue = queue<reaction_t>();; in_fixing = false; fix_queue = queue<reaction_t>();; in_fixing = false;
if(flags & (w_numerical | w_known_structure)) { if(flags & (w_numerical | w_known_structure)) {