mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-30 21:42:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			539 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			539 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // RogueViz - Grigorchuk group
 | |
| // Copyright (C) 2011-2019 Zeno and Tehora Rogue, see 'hyper.cpp' for details
 | |
| 
 | |
| /** \file rogueviz/grigorchuk.cpp
 | |
|  *  \brief Grigorchuk group
 | |
|  *
 | |
|  * This is a visualization of the Grigorchuk group. It is the first known group with
 | |
|  * intermediate growth (i.e., superpolynomial and subexponential).
 | |
|  *
 | |
|  * The implementation is based on:
 | |
|  *
 | |
|  * Rostislav Grigorchuk, Igor Pak,
 | |
|  * Groups of Intermediate Growth: an Introduction for Beginners
 | |
|  * https://arxiv.org/pdf/math/0607384.pdf
 | |
|  *
 | |
|  * which presents the material in a simple way.
 | |
|  *
 | |
|  * This creates a map whose tiles correspond to the elements of the Grigorchuk group.
 | |
|  * More precisely, the tiles correspond to the subgroup of index 2 generated by ac, ca, and b
 | |
|  * (this is "more playable"). The three tiles adjacent to g are gb, gac, and gca.
 | |
|  *
 | |
|  * The 'lines' drawn split each tile into two halves, which correspond to the elements of the
 | |
|  * actual Grigorchuk group (g and ga; ga is the one close to gac).
 | |
|  *
 | |
|  * Every element of the Grigorchuk group has finite order. Therefore, if you choose a specific
 | |
|  * way of travelling (e.g. turn left, go, turn right, go) you will always eventually reach the
 | |
|  * starting point.
 | |
|  *
 | |
|  * Command line options:
 | |
|  *
 | |
|  * -grigorchuk -- play on the Grigorchuk group
 | |
|  * -grig-limit 100000 -canvas G -- color the tiles according to the distance from the starting point
 | |
|  *   (i.e., the neutral element), the number is the number of tiles colored
 | |
|  * -grig-nolines -- show no splitting lines (also can be switched in the experiments menu)
 | |
|  * -grig-nolabels -- show no labels (also can be switched in the experiments menu)
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "rogueviz.h"
 | |
| 
 | |
| namespace grigorchuk {
 | |
| 
 | |
| using namespace hr;
 | |
| 
 | |
| typedef tuple<bool, string, string> splitter;
 | |
| 
 | |
| void add(string& s, char c) {
 | |
|   if(s.size() == 0) s.push_back(c);
 | |
|   else if(c == s.back()) s.pop_back();
 | |
|   else if(c != 'a' && s.back() != 'a')
 | |
|    s.back() = s.back() ^ c ^ 'd' ^ 'b' ^ 'c';
 | |
|   else s.push_back(c);
 | |
|   }
 | |
| 
 | |
| splitter split(string s) {
 | |
|   bool swapped = false;
 | |
|   string s0, s1;
 | |
|   for(char c: s) {
 | |
|     if(c == 'b') add(s0, swapped?'a':'c'), add(s1, swapped?'c':'a');
 | |
|     if(c == 'c') add(s0, swapped?'a':'d'), add(s1, swapped?'d':'a');
 | |
|     if(c == 'd') add(swapped ? s1 : s0, 'b');
 | |
|     if(c == 'a') swapped = !swapped;
 | |
|     }
 | |
|   return splitter{swapped, s0, s1};
 | |
|   }
 | |
| 
 | |
| splitter split_slow(string s) {
 | |
|   bool swapped = false;
 | |
|   string s0, s1;
 | |
|   for(char c: s) {
 | |
|     if(c == 'b') (s0 += swapped?'a':'c'), (s1 += swapped?'c':'a');
 | |
|     if(c == 'c') (s0 += swapped?'a':'d'), (s1 += swapped?'d':'a');
 | |
|     if(c == 'd') ((swapped ? s1 : s0) += 'b'), ((swapped ? s0 : s1) += '-');
 | |
|     if(c == 'a') swapped = !swapped, s0 += '-', s1 += '-';
 | |
|     }
 | |
|   return splitter{swapped, s0, s1};
 | |
|   }
 | |
| 
 | |
| string reduce(const string& x) {
 | |
|   string res;
 | |
|   for(char c: x) add(res, c);
 | |
|   return res;
 | |
|   }
 | |
| 
 | |
| #define Split(x) auto sw = split(x); auto swapped = get<0>(sw); auto s0 = get<1>(sw); auto s1 = get<2>(sw)
 | |
| 
 | |
| bool empt(const string& x) { 
 | |
|   Split(x); // auto [swapped, s0, s1] = split(x);
 | |
|   if(x == "") return true;
 | |
|   if(x == "d") return false;
 | |
|   if(swapped) return false;
 | |
|   return empt(s0) && empt(s1);
 | |
|   }
 | |
| 
 | |
| bool empt_slow(const string& x) { 
 | |
|   Split(x); // auto [swapped, s0, s1] = split_slow(x);
 | |
|   printf("%s -> %d %s %s\n", x.c_str(), swapped, s0.c_str(), s1.c_str());
 | |
|   if(x == "") return true;
 | |
|   if(x == "d") return false;
 | |
|   if(swapped) return false;
 | |
|   return empt(s0) && empt(s1);
 | |
|   }
 | |
| 
 | |
| typedef const struct rep* prep;
 | |
| 
 | |
| struct rep {
 | |
|   bool swapped;
 | |
|   prep a0;
 | |
|   prep a1;
 | |
|   mutable char last;
 | |
|   mutable bool visited = false;
 | |
|   mutable int len;
 | |
|   rep(bool s, prep a0, prep a1, char l, bool vis = false) : swapped(s), a0(a0), a1(a1), last(l), visited(vis), len(-1) {}
 | |
|   };
 | |
| 
 | |
| bool operator < (const rep a, const rep b) {
 | |
|   return tie(a.swapped, a.a0, a.a1) < tie(b.swapped, b.a0, b.a1);
 | |
|   }
 | |
| 
 | |
| bool operator == (const rep a, const rep b) {
 | |
|   return tie(a.swapped, a.a0, a.a1) == tie(b.swapped, b.a0, b.a1);
 | |
|   }
 | |
| 
 | |
| rep grig_I = rep{false, &grig_I, &grig_I, 0, false};
 | |
| extern rep grig_a, grig_b, grig_c, grig_d;
 | |
| 
 | |
| rep grig_a = rep{true, &grig_I, &grig_I, 'a', false};
 | |
| rep grig_b = rep{false, &grig_a, &grig_c, 'b', false};
 | |
| rep grig_c = rep{false, &grig_a, &grig_d, 'c', false};
 | |
| rep grig_d = rep{false, &grig_I, &grig_b, 'd', false};
 | |
| 
 | |
| // (ab) c = a (a,c) (a,d) = a(a,c) (a,d) = 
 | |
| map<rep, char> all_reps; //  = {grigid, &grigid};
 | |
| 
 | |
| prep lookup(rep x) {
 | |
|   if(x == grig_I) return &grig_I;
 | |
|   else if(x == grig_a) return &grig_a;
 | |
|   else if(x == grig_b) return &grig_b;
 | |
|   else if(x == grig_c) return &grig_c;
 | |
|   else if(x == grig_d) return &grig_d;
 | |
|   else if(all_reps.count(x)) return &(all_reps.find(x)->first);
 | |
|   else return &(all_reps.emplace(x, 0).first->first);
 | |
|   }
 | |
| 
 | |
| /*prep add_a(prep x) { 
 | |
|   return lookup({!x->swapped, x->a0, x->a1, 'a'}); 
 | |
|   }
 | |
| prep add_d(prep x) { 
 | |
|   if(x == &grig_I) return &grig_d;
 | |
|   if(x == &grig_d) return &grig_I;
 | |
|   return lookup({x->swapped, x->swapped?add_d(x->a0):x->a0, x->swapped?x->a1:add_d(x->a1), 'd'});
 | |
|   }
 | |
| prep add_c(prep x) { return lookup({x->swapped, (x->swapped?add_a:add_d)(x->a0), (x->swapped?add_d:add_a)(x->a1), 'c'}); }
 | |
| prep add_b(prep x) { return lookup({x->swapped, (x->swapped?add_a:add_c)(x->a0), (x->swapped?add_c:add_a)(x->a1), 'b'}); }
 | |
| */
 | |
| 
 | |
| /* ostream& operator << (ostream& os, prep x) {
 | |
|   if(x == &grig_I) return os << "I";
 | |
|   // else if(x == &grig_a) return os << "a";
 | |
|   else if(x == &grig_b) return os << "b";
 | |
|   else if(x == &grig_c) return os << "c";
 | |
|   else if(x == &grig_d) return os << "d";
 | |
|   else {
 | |
|     if(x->swapped) os << "a";
 | |
|     os << "(" << x->a0 << "," << x->a1 << ")";
 | |
|     return os;
 | |
|     }
 | |
|   } */
 | |
| 
 | |
| prep mul (prep x, prep y) {
 | |
|   if(x == &grig_I) return y;
 | |
|   if(y == &grig_I) return x;
 | |
|   if(x == &grig_a && y == &grig_a) return &grig_I;
 | |
|   if(x == &grig_b && y == &grig_b) return &grig_I;
 | |
|   if(x == &grig_c && y == &grig_c) return &grig_I;
 | |
|   if(x == &grig_d && y == &grig_d) return &grig_I;
 | |
|   if(x == &grig_b && y == &grig_c) return &grig_d;
 | |
|   if(x == &grig_c && y == &grig_b) return &grig_d;
 | |
|   if(x == &grig_b && y == &grig_d) return &grig_c;
 | |
|   if(x == &grig_d && y == &grig_b) return &grig_c;
 | |
|   if(x == &grig_c && y == &grig_d) return &grig_b;
 | |
|   if(x == &grig_d && y == &grig_c) return &grig_b;
 | |
|   if(!y->swapped) return lookup(rep{x->swapped, mul(x->a0, y->a0), mul(x->a1, y->a1), y->last});
 | |
|   else return lookup(rep{!x->swapped, mul(x->a1, y->a0), mul(x->a0, y->a1), y->last});
 | |
|   }
 | |
| 
 | |
| string encode(string s) {
 | |
|   if(s == "") return "I";
 | |
|   else if( s == "d") return "d";
 | |
|   else {
 | |
|     Split(s); // auto [swapped, s0, s1] = split(s);
 | |
|     return (swapped ? "a(" : "(") + encode(s0) + "," + encode(s1) + ")";
 | |
|     }
 | |
|   }
 | |
| 
 | |
| set<string> seen;
 | |
| 
 | |
| void addmore(const string& s, int more) {
 | |
|   if(more == 0) {
 | |
|     string sr = s;
 | |
|     reverse(sr.begin(), sr.end());
 | |
|     for(string q: seen) {
 | |
|       string qo = q;
 | |
|       for(char cr: sr) add(q, cr);
 | |
|       if(empt(q)) { 
 | |
|         // printf("%s = %s /%s\n", s.c_str(), qo.c_str(), sr.c_str()); 
 | |
|         return; 
 | |
|         }
 | |
|       }
 | |
|     seen.insert(s);
 | |
|     // printf("%s\n", s.c_str());
 | |
|     return;
 | |
|     }
 | |
|   for(char c: {'a', 'b', 'c', 'd'}) {
 | |
|     string s1 = s;
 | |
|     add(s1, c);
 | |
|     if(isize(s1) != isize(s)+1) continue;
 | |
|     addmore(s1, more-1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| string deform(prep x2) {
 | |
|   string t = "";
 | |
|   while(x2 != &grig_I) {
 | |
|     if(x2->last == 'a') t += 'a', x2 = mul(x2, &grig_a);
 | |
|     else if(x2->last == 'b') t += 'b', x2 = mul(x2, &grig_b);
 | |
|     else if(x2->last == 'c') t += 'c', x2 = mul(x2, &grig_c);
 | |
|     else if(x2->last == 'd') t += 'd', x2 = mul(x2, &grig_d);
 | |
|     else if(x2->last == 'A') t += "ca", x2 = mul(mul(x2, &grig_c), &grig_a);
 | |
|     else if(x2->last == 'C') t += "ac", x2 = mul(mul(x2, &grig_a), &grig_c);
 | |
|     else return "?" + t + "?";
 | |
|     }
 | |
|   reverse(t.begin(), t.end());
 | |
|   return t;
 | |
|   }
 | |
| 
 | |
| bool prepared = false;
 | |
| 
 | |
| prep ac, ca;
 | |
| 
 | |
| int grig_limit = 10000;
 | |
| 
 | |
| int prepared_dists = 0;
 | |
| int next;
 | |
| int length = 0;
 | |
| 
 | |
| vector<prep> all;
 | |
| 
 | |
| void visit(prep x, char l, int d) { 
 | |
|   if(!x->visited) x->visited = true, x->last = l, all.push_back(x), x->len = d;
 | |
|   }
 | |
| 
 | |
| void prepare_to_next(bool verbose) {
 | |
|   while(true) {
 | |
|     int i = prepared_dists++;
 | |
|     prep x = all[i];
 | |
|     if(!x->visited) println(hlog, "visited or not");
 | |
|     if(1) {
 | |
|       // printf("%s\n", deform(x).c_str());
 | |
|       }
 | |
|     visit(mul(x, &grig_b), 'b', x->len + 1);
 | |
|     visit(mul(x, ac), 'A', x->len + 1);
 | |
|     visit(mul(x, ca), 'C', x->len + 1);
 | |
| 
 | |
|     if(i == next) {
 | |
|       if(verbose)
 | |
|         addMessage("there are "+its(i)+" elements in distance up to "+its(length));
 | |
|       println(hlog, "Grigorchuk: ", tie(length, i));
 | |
|       next = all.size(), length++;
 | |
|       break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| void prepare() {
 | |
|   prepared = true;
 | |
|   
 | |
|   // rep* grigid = lookup(rep { false, NULL, NULL });
 | |
|   
 | |
|   ac = mul(&grig_a, &grig_c);
 | |
|   ca = mul(&grig_c, &grig_a);
 | |
|   // prep where = &grig_I;
 | |
|   
 | |
|   string s = "";
 | |
|   all.clear();
 | |
| 
 | |
|   
 | |
|   /*
 | |
|   for(int a=0; a<=32; a++) {
 | |
|     // printf("%p -> %d %p %p\n", where, where->swapped, where->a0, where->a1);
 | |
|     cout << where << " | " << encode(s) << "\n";
 | |
|     where = ((a&1) ? add_a : add_b) (where);
 | |
|     s += (a&1) ? 'a' : 'b';
 | |
|     }
 | |
|   
 | |
|   string test = "ba";
 | |
|   string pw = "";
 | |
|   for(int i=0; i<=16; i++) {
 | |
|     printf("%d: %d\n", i, empt(pw));
 | |
|     pw += test;
 | |
|     }
 | |
|   */
 | |
|   
 | |
|   // printf("TEST %s\n", encode("bcd").c_str());
 | |
| 
 | |
|   visit(&grig_I, 0, 0);
 | |
|   length = 0;
 | |
|   next = all.size();
 | |
|   
 | |
|   prepared_dists = 0;
 | |
| 
 | |
|   while(prepared_dists < grig_limit) prepare_to_next(false);
 | |
|   
 | |
|   prep test = &grig_b;
 | |
|   test = mul(test, &grig_a);
 | |
|   test = mul(test, &grig_d);
 | |
|   test = mul(test, &grig_a);
 | |
|   test = mul(test, &grig_d);
 | |
|   printf("badad = %s\n", deform(test).c_str());
 | |
|   }
 | |
| 
 | |
| bool view_labels = true, view_lines = true;
 | |
| 
 | |
|   }
 | |
| 
 | |
| namespace hr {
 | |
| 
 | |
| extern ccolor::data grigorchuk_coloring;
 | |
| 
 | |
| struct hrmap_grigorchuk : hrmap_standard {
 | |
| 
 | |
|   heptagon *origin;
 | |
|   heptagon *getOrigin() override { return origin; }
 | |
|   
 | |
|   map<heptagon*, grigorchuk::prep> dec;
 | |
|   map<grigorchuk::prep, heptagon*> enc;
 | |
|   
 | |
|   void gtie(heptagon* h, grigorchuk::prep p) {
 | |
|     dec[h] = p;
 | |
|     enc[p] = h;
 | |
|     }
 | |
| 
 | |
|   hrmap_grigorchuk() {
 | |
|     if(!grigorchuk::prepared) grigorchuk::prepare();
 | |
|     origin = tailored_alloc<heptagon> (S7);
 | |
|     origin->s = hsOrigin;
 | |
|     origin->emeraldval = 0;
 | |
|     origin->zebraval = 0;
 | |
|     origin->fiftyval = 0;
 | |
|     origin->fieldval = 0;
 | |
|     origin->rval0 = origin->rval1 = 0;
 | |
|     origin->cdata = NULL;
 | |
|     origin->alt = NULL;
 | |
|     origin->c7 = NULL;
 | |
|     origin->distance = 0;
 | |
|     origin->c7 = newCell(3, origin);
 | |
|     gtie(origin, &grigorchuk::grig_I);
 | |
|     }
 | |
| 
 | |
|   heptagon *create_step(heptagon *p, int d) override {
 | |
|     auto pr = dec[p];
 | |
|     // auto pr1 = pr;
 | |
| 
 | |
|     switch(d) {
 | |
|       using namespace grigorchuk;
 | |
|       case 0: pr = mul(mul(pr, &grig_a), &grig_c); break;
 | |
|       case 1: pr = mul(mul(pr, &grig_c), &grig_a); break;
 | |
|       case 2: pr = mul(pr, &grig_b); break;
 | |
|       }
 | |
|     
 | |
|     heptagon *h;
 | |
| 
 | |
|     if(enc.count(pr)) {
 | |
|       h = enc[pr];
 | |
|       // println(hlog, deform(pr), "*", "acd"[d], " = ", deform(pr1));
 | |
|       }
 | |
|     else {
 | |
|       if(!pr->visited) pr->last = "ACb" [d];
 | |
| 
 | |
|       h = tailored_alloc<heptagon> (S7);
 | |
|       h->s = hsOrigin;
 | |
|       h->emeraldval = 0;
 | |
|       h->zebraval = 0;
 | |
|       h->fiftyval = 0;
 | |
|       h->fieldval = 0;
 | |
|       h->rval0 = h->rval1 = 0;
 | |
|       h->cdata = NULL;
 | |
|       h->alt = NULL;
 | |
|       h->c7 = newCell(3, h);
 | |
|       h->distance = p->distance + 1;
 | |
|       gtie(h, pr);
 | |
|       }
 | |
|     
 | |
|     h->c.connect(d == 2 ? 2 : 1-d, p, d, false);;
 | |
|     return h;
 | |
|     }
 | |
|   
 | |
|   void draw_at(cell *at, const shiftmatrix& where) override {
 | |
|   
 | |
|     dq::clear_all();
 | |
|     dq::enqueue_by_matrix(at->master, where * currentmap->master_relative(centerover, true));
 | |
|     
 | |
|     while(!dq::drawqueue.empty()) {      
 | |
|       auto& p = dq::drawqueue.front();
 | |
|       heptagon *h = get<0>(p);
 | |
|       shiftmatrix V = get<1>(p);
 | |
|       dq::drawqueue.pop();
 | |
|             
 | |
|       cell *c = h->c7;
 | |
|       if(!do_draw(c, V)) continue;
 | |
|       
 | |
|       if(grigorchuk::view_lines) queueline(V * ddspin(c, 2) * xpush0(cgi.tessf/2), V * ddspin(c, 2) * xpush0(-cgi.tessf), 0xFF00FFFF, 2);
 | |
| 
 | |
|       if(grigorchuk::view_labels) queuestr(V, 0.3, grigorchuk::deform(dec[c->master]), 0xFFFFFF);
 | |
| 
 | |
|       if(ccolor::which == &grigorchuk_coloring && c->landparam == 0)
 | |
|         c->landparam = grigorchuk_coloring(c);
 | |
|       
 | |
|       drawcell(c, V * currentmap->master_relative(c, false));
 | |
|       
 | |
|       for(int i=0; i<3; i++) if(c->move(i))
 | |
|         dq::enqueue_by_matrix(h->cmove(i), optimized_shift(V * adj(h, i)));
 | |
|       }
 | |
|     }
 | |
|   
 | |
|   transmatrix relative_matrixh(heptagon *h2, heptagon *h1, const hyperpoint& hint) override {
 | |
|     if(gmatrix0.count(h2->c7) && gmatrix0.count(h1->c7))
 | |
|       return inverse_shift(gmatrix0[h1->c7], gmatrix0[h2->c7]);
 | |
|     return Id;
 | |
|     }
 | |
| 
 | |
|   transmatrix relative_matrixc(cell *c2, cell *c1, const struct hyperpoint& hint) override {
 | |
|     if(gmatrix0.count(c2) && gmatrix0.count(c1))
 | |
|       return inverse_shift(gmatrix0[c1], gmatrix0[c2]);
 | |
|     return Id;
 | |
|     }
 | |
|   };
 | |
| 
 | |
| eGeometry gGrigorchuk(eGeometry(-1));
 | |
| 
 | |
| ccolor::data grigorchuk_coloring = ccolor::data("Grigorchuk", [] { return geometry == gGrigorchuk; }, [] (cell *c, ccolor::data& cco) {
 | |
|   return 0x102008 * (1 + ((hrmap_grigorchuk*)currentmap)->dec[c->master]->len);
 | |
|   }, {});
 | |
| 
 | |
| void create_grigorchuk_geometry() {
 | |
|   if(gGrigorchuk != eGeometry(-1)) return;
 | |
|   ginf.push_back(ginf[gNormal]);
 | |
|   gGrigorchuk = eGeometry(isize(ginf) - 1);
 | |
|   auto& gi = ginf[gGrigorchuk];
 | |
|   gi.sides = 3;
 | |
|   gi.vertex = 8;
 | |
|   gi.flags = qANYQ | qEXPERIMENTAL;
 | |
|   gi.tiling_name = "{3,8}";
 | |
|   gi.quotient_name = "Grigorchuk";
 | |
|   gi.menu_displayed_name = "Grigorchuk group";
 | |
|   gi.shortname = "Grig";
 | |
|   gi.default_variation = eVariation::pure;
 | |
|   ccolor::all.push_back(&grigorchuk_coloring);
 | |
|   }
 | |
| 
 | |
| int readArgsG() {
 | |
|   using namespace arg;
 | |
|            
 | |
|   if(0) ;
 | |
|   else if(argis("-grig-limit")) {
 | |
|     shift(); grigorchuk::grig_limit = argi();
 | |
|     }
 | |
| 
 | |
|   else if(argis("-grigorchuk")) {
 | |
| 
 | |
|     PHASEFROM(3);
 | |
|     
 | |
|     stop_game();
 | |
|     create_grigorchuk_geometry();    
 | |
|     set_geometry(gGrigorchuk);
 | |
|     set_variation(eVariation::pure);
 | |
|     }
 | |
|   
 | |
|   else if(argis("-grig-nolines")) {
 | |
|     grigorchuk::view_lines = false;
 | |
|     }
 | |
| 
 | |
|   else if(argis("-grig-nolabels")) {
 | |
|     grigorchuk::view_labels = false;
 | |
|     }
 | |
| 
 | |
|   else return 1;
 | |
|   return 0;
 | |
|   }
 | |
| 
 | |
| auto hook = addHook(hooks_args, 100, readArgsG)
 | |
|   + addHook(hooks_newmap, 100, [] { return geometry == gGrigorchuk ? new hrmap_grigorchuk : nullptr; })
 | |
|   + addHook(dialog::hooks_display_dialog, 100, [] () {
 | |
|     if(current_screen_cfunction() == showEuclideanMenu && geometry == gGrigorchuk) {
 | |
|       dialog::addBoolItem_action(XLAT("Grigorchuk lines"), grigorchuk::view_lines, 'L'); 
 | |
|       dialog::addBoolItem_action(XLAT("Grigorchuk labels"), grigorchuk::view_labels, 'M'); 
 | |
|       }
 | |
|     })
 | |
|   + addHook(hooks_initialize, 100, create_grigorchuk_geometry)
 | |
|   + addHook_rvslides(140, [] (string s, vector<tour::slide>& v) {
 | |
|     if(s != "mixed") return;
 | |
|     using namespace rogueviz::pres;
 | |
|     v.push_back(tour::slide{
 | |
|       "Grigorchuk group", 10, tour::LEGAL::NONE,
 | |
| 
 | |
|       "This is a visualization of the Grigorchuk group. It is the first known group with "
 | |
|       "intermediate growth (i.e., superpolynomial and subexponential).\n\n"
 | |
|       "Each tile corresponds to two elements of the Grigorchuk group.\n\n"
 | |
|       "Every element of the Grigorchuk group has finite order. Therefore, if you choose a specific "
 | |
|       "way of travelling (e.g. turn left, go, turn right, go) you will always eventually reach the "
 | |
|       "starting point.\n\n"
 | |
|       "Cells are color-coded by the distance to the origin. Distance is only known for a given number of cells "
 | |
|       "(initially 10000); if you want to compute more distances, press '5'. Press 'o' to enable/disable lines.\n\n"
 | |
|       "See grigorchuk.cpp for more comments.",
 | |
| 
 | |
|       [] (tour::presmode mode) {
 | |
|         slide_url(mode, 'p', "a paper about Grigorchuk group", "https://arxiv.org/pdf/math/0607384.pdf");
 | |
|         if(mode == pmStart) {
 | |
|           grigorchuk::grig_limit = 10000;
 | |
|           gamestack::push();
 | |
|           slide_backup(ccolor::which, &grigorchuk_coloring);
 | |
|           slide_backup(firstland, laCanvas);
 | |
|           slide_backup(specialland, laCanvas);          
 | |
|           set_geometry(gGrigorchuk);
 | |
|           start_game();
 | |
|           resetview();
 | |
|           }
 | |
|         if(mode == pmKey) {
 | |
|           grigorchuk::prepare_to_next(true);
 | |
|           }
 | |
|         if(mode == pmStop) {
 | |
|           gamestack::pop();
 | |
|           slide_restore_all();
 | |
|           }
 | |
|         }}
 | |
|       );});
 | |
| }
 | |
| 
 | 
