1 // Hyperbolic Rogue -- infinite-order tessellations
2 // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file inforder3.cpp
5  *  \brief infinite-order tessellations
6  *
7  *  very simple
8  */
9 
10 #include "../hyper.h"
11 
12 namespace hr {
13 
14 namespace rewriting {
15 
16 bool symmetric;
17 
18 string start;
19 vector<pair<string, string> > rules;
20 set<pair<string, string> > ruleset;
21 
find_matches(string s)22 map<string, int> find_matches(string s) {
23   map<string, int> res;
24   for(auto& p: rules) {
25     size_t next = s.find(p.first);
26     while(next != string::npos) {
27       string t = s.substr(0, next) + p.second + s.substr(next+isize(p.first));
28       auto& r = res[t];
29       if(ruleset.count({p.second, p.first})) r = 2;
30       else r = max(r, 1);
31       next = s.find(p.first, next+1);
32       }
33     }
34   return res;
35   }
36 
37 struct hrmap_rewrite : hrmap_hyperbolic {
38 
39   map<heptagon*, pair<heptagon*, string> > asg;
40   map<pair<heptagon*, string>, heptagon*> asg_rev;
41 
create_stephr::rewriting::hrmap_rewrite42   heptagon *create_step(heptagon *h, int direction) {
43     if(h->move(direction)) return h->move(direction);
44     if(asg.empty()) { asg[h] = {h, start}; h->zebraval = 0; }
45 
46     auto s = asg[h].second;
47     auto root = asg[h].first;
48 
49     auto matches = find_matches(s);
50 
51     int next = h->zebraval;
52 
53     if(matches.empty() && next == 0) {
54       h->c.connect(0, h, 0, false);
55       return h;
56       }
57 
58     for(auto& match: matches) {
59       if(h->move(next)) { next++; continue; }
60       bool symmetric = match.second == 2;
61       const string& m = match.first;
62       if(symmetric) {
63         auto matches1 = find_matches(m);
64         heptagon *h1;
65         if(asg_rev[{root, m}]) h1 = asg_rev[{root, m}];
66         else {
67           h1 = tailored_alloc<heptagon> (isize(matches1));
68           h1->alt = NULL;
69           h1->s = hsA;
70           h1->cdata = NULL;
71           h1->distance = h->distance;
72           h1->zebraval = 0;
73           h1->c7 = newCell(isize(matches1), h1);
74           asg[h1] = {root, m};
75           asg_rev[{root, m}] = h1;
76           }
77         int next1 = 0;
78         for(auto& match2: matches1) { if(match2.first == s) break; next1++; }
79         h->c.connect(next, h1, next1, false);
80         }
81       else {
82         int deg = 1 + isize(find_matches(m));
83         auto h1 = tailored_alloc<heptagon> (deg);
84         h->c.connect(next, h1, 0, false);
85 
86         h1->alt = NULL;
87         h1->s = hsA;
88         h1->cdata = NULL;
89         h1->distance = h->distance + 1;
90         h1->zebraval = 1;
91         h1->c7 = newCell(deg, h1);
92 
93         asg[h1] = {h1, m};
94         asg_rev[{h1, m}] = h1;
95         }
96       next++;
97       }
98 
99     if(next != h->type) { println(hlog, "degree error"); exit(1); }
100 
101     return h->move(direction);
102     }
103 
104   };
105 
labeller(cell * c,const shiftmatrix & V)106 bool labeller(cell* c, const shiftmatrix& V) {
107   auto m = dynamic_cast<hrmap_rewrite*> (currentmap);
108   if(m) {
109     string s = m->asg[c->master].second;
110     cgi.scalefactor = 1;
111     queuestr(V, 0.5, s, colortables['j'][c->master->distance+1]);
112     }
113   return false;
114   }
115 
load_rules(vector<string> vs)116 void load_rules(vector<string> vs) {
117 
118   stop_game();
119   set_geometry(gInfOrderMixed);
120   ginf[gInfOrderMixed].distlimit = {{1, 1}};
121   ginf[gInfOrderMixed].flags |= qEXPERIMENTAL;
122 
123   start = "";
124   rules.clear();
125   ruleset.clear();
126   for(string line: vs) {
127     if(line == "") continue;
128 
129     auto i = line.find("->");
130     if(i != string::npos) {
131       rules.emplace_back(line.substr(0, i), line.substr(i+2));
132       ruleset.emplace(line.substr(0, i), line.substr(i+2));
133       }
134     else start = line;
135     }
136 
137   ginf[gInfOrderMixed].sides = isize(find_matches(start));
138   if(!ginf[gInfOrderMixed].sides) ginf[gInfOrderMixed].sides = 1;
139   ginf[gInfOrderMixed].flags |= qANYQ;
140   }
141 
142 #if ISWEB
143 extern "C" {
load_web_rules()144   void load_web_rules() {
145     string s = get_value("rules") + '\n';
146     vector<string> split;
147     string cc = "";
148     for(char c: s) if(c == '\n' || c == '\r') split.push_back(cc), cc = ""; else cc += c;
149     load_rules(split);
150     start_game();
151     clearMessages();
152 
153     bfs();
154     resetview();
155     drawthemap();
156     centerpc(INF);
157     centerover = cwt.at;
158     }
159   }
160 #endif
161 
162 auto hooks =
__anon24d438020102null163   addHook(hooks_args, 100, [] {
164   using namespace arg;
165 
166   if(0) ;
167   #if ISWEB
168   else if(argis("-rww")) {
169     load_web_rules();
170     }
171   #endif
172   else if(argis("-rwr")) {
173     shift();
174     fhstream ss(argcs(), "rt");
175     vector<string> vs;
176     string line;
177     while(scan(ss, line)) vs.push_back(line);
178     load_rules(vs);
179     }
180   else return 1;
181   return 0;
182   }) +
183 
__anon24d438020202null184   addHook(hooks_newmap, 100, [] {
185     if(geometry == gInfOrderMixed && !rules.empty()) return (hrmap*) new hrmap_rewrite;
186     return (hrmap*) nullptr;
187     })
188 
189   + addHook(hooks_drawcell, 100, labeller);
190 
191 }
192 }
193