#include "../hyper.h" #include #include namespace hr { namespace rulegen { bool set_general(const string& s); } struct symbol { unsigned char sid; unsigned char eid; bool operator < (const symbol s1) const { return (int16_t&) (*this) < (int16_t&) s1; } bool operator == (const symbol s1) const { return (int16_t&) (*this) == (int16_t&) s1; } bool operator != (const symbol s1) const { return (int16_t&) (*this) != (int16_t&) s1; } }; using seq = vector; void block(symbol& s) { s.sid ^= 128; } void unblock(symbol& s) { s.sid ^= 128; } bool is_blocked(symbol& s) { return s.sid & 128; } void print(hstream& hs, symbol s) { print(hs, "[", int(s.sid), ":", int(s.eid), "]"); } bool reversed; bool lexless(const seq& a, const seq& b) { int al = isize(a); int bl = isize(b); if(al != bl) return al < bl; if(reversed) { for(int i=al-1; i>=0; i--) if(a[i].eid < 120 && a[i] != b[i]) return a[i] < b[i]; return a.back() < b.back(); } else { return a < b; } } struct lexless_comparator { bool operator()(const seq& a, const seq& b) const { return lexless(a, b); } }; map rules; seq sub(const seq& s, int a, int l) { seq result; for(int i=0; i> rules_checked; queue> rulequeue; void enqueue_rule(seq l, seq r) { rulequeue.emplace(l, r); } cellwalker trace(cellwalker cw, const seq& sq, bool debug) { for(auto sym: sq) { if(shvid(cw.at) != sym.sid) println(hlog, "error: wrong type"); if(debug) println(hlog, "at: ", cw); cw += sym.eid; cw += wstep; cw -= cw.spin % arb::current.shapes[shvid(cw.at)].cycle_length; } return cw; } cell *get_sample(int sid) { celllister cl(currentmap->gamestart(), 20, 100000, nullptr); for(cell *c: cl.lst) if(shvid(c) == sid) return c; println(hlog, "sample not found of ", sid); exit(1); } string verify_rule(const pair& rule, bool debug = false) { cell *cstart = get_sample(rule.first[0].sid); cellwalker cw(cstart); cellwalker cw1 = trace(cw, rule.first, debug); cellwalker cw2 = trace(cw, rule.second, debug); return cw1 == cw2 ? "OK" : lalign(0, "FAIL ", cw1, " VS ", cw2); } int add_rule(seq l, seq r) { indenter ind(2); if(l == r) return 0; if(lexless(l, r)) swap(l, r); again: if(!rules.count(l)) { // println(hlog, "rule added: ", l, " -> ", r, " verify: ", verify_rule({l, r})); rules[l] = r; return 1; } else if(rules[l] == r) return 0; else if(lexless(r, rules[l])) { l = rules[l]; goto again; } else { // println(hlog, "rule simplified: ", l, " -> ", r); auto x = rules[l]; x = r; l = x; goto again; } } symbol gen_symbol(int sid, int eid) { symbol sym; sym.sid = sid; sym.eid = eid; return sym; } bool include_rotations = false; void prepare_rules() { auto& ac = arb::current; int N = isize(ac.shapes); /* move and back and move */ if(!include_rotations) for(int i=0; i nothing or rotate */ if(include_rotations) for(int i=0; i move */ if(include_rotations) for(int i=0; i& r, seq& result) { int rl = isize(r.first); int sl = isize(s); for(int i=0; i<=sl-rl; i++) if(sub_at(s, i, r.first)) { result = sub(s, 0, i); catto(result, r.second); catto_sub(result, s, i+rl, sl-rl-i); return true; } return false; } void find_critical(const pair& p, const pair& q) { int pl = isize(p.first); int ql = isize(q.first); for(int i=1; i& nr) { auto& lh = nr.first; auto& rh = nr.second; for(auto& r: rules) { seq res; if(is_reducible(lh, r, res)) { enqueue_rule(res, rh); return; } if(is_reducible(rh, r, res)) { enqueue_rule(lh, res); return; } } vector to_erase; for(auto& r: rules) { seq res; if(is_reducible(r.first, nr, res)) { to_erase.push_back(r.first); enqueue_rule(res, r.second); } if(is_reducible(rh, r, res)) { to_erase.push_back(r.first); enqueue_rule(r.first, res); } } for(auto s: to_erase) rules.erase(s); for(auto& r: rules) { find_critical(r, nr); find_critical(nr, r); } rules[nr.first] = nr.second; } int kb_result; double total_time; int main_loop(int timelimit = 5, int lenlimit = 300) { clock_t start = clock(); while(!rulequeue.empty()) { if(clock() > start + timelimit * CLOCKS_PER_SEC) return 1; auto p = rulequeue.front(); rulequeue.pop(); if(p.first == p.second) continue; if(lexless(p.first, p.second)) swap(p.first, p.second); if(rules_checked.count(p)) continue; rules_checked.insert(p); int len = p.first.size() + p.second.size(); if(len > max_rule_length) max_rule_length = len; if(len > lenlimit) return 2; handle_rule(p); } println(hlog, "finished in ", (clock() - start) * 1. / CLOCKS_PER_SEC); return 0; } void test_knuth_bendix() { rules.clear(); rulequeue = {}; rules_checked.clear(); max_rule_length = 0; start_game(); prepare_rules(); clock_t total_start = clock(); println(hlog, "total_start = ", total_start * 1. / CLOCKS_PER_SEC); kb_result = main_loop(10); println(hlog, "after first phase = ", clock() * 1. / CLOCKS_PER_SEC); if(kb_result == 0) { println(hlog, "intermediate rules:"); for(auto s: rules) println(hlog, s.first, " => ", s.second); final_rules(); kb_result = main_loop(50, 999999); if(kb_result) kb_result += 2; } println(hlog, "after second phase = ", clock() * 1. / CLOCKS_PER_SEC); total_time = (clock() - total_start) * 1. / CLOCKS_PER_SEC; } bool is_end_reducible(const seq& s1) { for(auto r: rules) if(isize(s1) >= isize(r.first) && sub(s1, isize(s1)-isize(r.first), isize(r.first)) == r.first) return true; return false; } int count_tree_states() { set all_prefixes; for(auto r: rules) { auto left = r.first; for(int i=0; i filenames; std::ifstream is("devmods/rulegen-tests/"+setname+".lst"); string s; while(getline(is, s)) { while(s != "" && s[0] == ' ') s = s.substr(1); if(s != "" && s[0] != '#') filenames.push_back(s); } println(hlog, "CSV;kbres;Tp;kbrules;tree;maxlen;file"); int children = 0; sem_t sem; if(forked) sem_init(&sem, true, 1); fflush(stdout); for(const string& s: filenames) { if(forked) { int pid; if(children >= max_children) { wait(&pid); children--; } if((pid = fork())) children++; else goto doit; continue; } doit: if(rulegen::set_general(s)) { if(!arb::in()) try { arb::convert::convert(); arb::convert::activate(); } catch(hr_exception& e) { println(hlog, "CSV; failed to convert ", s); return; } // println(hlog, "will call test_knuth_bendix"); test_knuth_bendix(); // println(hlog, "after test_knuth_bendix"); if(forked) sem_wait(&sem); println(hlog, "CSV;",kb_result,";", total_time, ";", isize(rules), ";", kb_result == 0 ? count_tree_states() : 0, ";", max_rule_length, ";", s); if(forked) sem_post(&sem); fflush(stdout); } if(forked) exit(0); } while(children) { int pid; wait(&pid); children--; } if(forked) sem_destroy(&sem); } void kb_merge() { vector filenames; std::ifstream is("devmods/rulegen-tests/all.lst"); string s; while(getline(is, s)) { while(s != "" && s[0] == ' ') s = s.substr(1); if(s != "" && s[0] != '#') filenames.push_back(s); } map cas; if(std::ifstream is = std::ifstream("devmods/rulegen-tests/kbmerge.txt")) {; while(getline(is, s)) { int i = 0, j = 0; for(char c: s) { i++; if(c == ';') j = i; } string fname = s.substr(j); if(cas.count(fname) && cas[fname].substr(0,5) != s.substr(0,5)) { // println(hlog, "repetition:\n", cas[fname], "\n", s); s = min(s, cas[fname]); } cas[fname] = s; // println(hlog, "assigned to ", fname, ": ", s); } } println(hlog, "CSV;kbres;Tp;kbrules;tree;maxlen;file"); for(auto ss: filenames) if(!cas.count(ss)) println(hlog, "MISSING: ", ss); else println(hlog, cas[ss]); } map first_seq_last; map first_seq; map qty; void view_kb_tree(const shiftmatrix& V, cellwalker cw, seq s) { qty[cw.at]++; if(cw.at == lmouseover) println(hlog, s, " ; ", cw.spin); for(int i=0; itype; i++) { seq s1 = s; s1.emplace_back(gen_symbol(shvid(cw.at), i)); if(is_end_reducible(s1)) continue; auto cw1 = cw; cw1 += i; int is = cw1.spin; cw1 += wstep; if(!gmatrix.count(cw1.at)) continue; transmatrix A = currentmap->adj(cw.at, is); int eid = shvid(cw1.at); int clen = arb::current.shapes[eid].cycle_length; cw1 -= cw1.spin % clen; // queueline(V * get_corner_position(cw.at, cw.spin, 6), V * A * get_corner_position(cw1.at, cw1.spin, 6), 0xC00000FF); seq s2 = s1; s2.emplace_back(gen_symbol(shvid(cw1.at), 120)); if(!is_end_reducible(s2)) { // first_seq_last[cw1.at] == s1) { if(cw1.at == lmouseover) println(hlog, "is not end_reducible: ", s2); vid.linewidth *= 3; queueline(V * C0, V * mid(tC0(A), mid(C0, tC0(A))), 0xFFC0C0FF, 3); vid.linewidth /= 3; } if(!first_seq.count(cw1.at)) first_seq[cw1.at] = s1; else first_seq[cw1.at] = min(s1, first_seq[cw1.at]); view_kb_tree(V*A, cw1, s1); } } void kb_marker() { qty.clear(); first_seq_last = std::move(first_seq); first_seq.clear(); view_kb_tree(gmatrix[cwt.at], cellwalker(cwt.at, 0), {}); for(auto p: qty) queuestr(gmatrix[p.first], .5, its(p.second), 0xFFFFFFFF, 1); } void add_kb_view() { addHook(hooks_markers, 100, kb_marker); } int u = arg::add3("-kb", [] { test_knuth_bendix(); println(hlog, "result: ", kb_result, " rules: ", isize(rules)); println(hlog, "final rules:"); for(auto s: rules) println(hlog, s.first, " => ", s.second); if(kb_result == 0) println(hlog, "tree states: ", count_tree_states()); }) + arg::add3("-kb-test-all", [] { arg::shift(); test_all(arg::args()); }) + arg::add3("-kb-forked", [] { arg::shift(); max_children = arg::argi(); forked = max_children; }) + arg::add3("-kb-rev", [] { arg::shift(); reversed = arg::argi(); }) + arg::add3("-kb-view", add_kb_view) + arg::add3("-kb-merge", kb_merge); }