1 // Hyperbolic Rogue -- patterns
2 // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file pattern2.cpp
5  *  \brief various functions computing the standings of individual cells with respect to various patterns
6  *
7  *  Patterns include simple ones (hexagon or heptagon, three-colorings, Chessboard)
8  *  as well as more complex ones (Emerald, Palace, Field Pattern)
9  */
10 
11 #include "hyper.h"
12 namespace hr {
13 
14 #if HDR
15 enum cpatterntype {
16   cpFootball, cpThree, cpChess, cpSingle, cpSingleSym, cpOddEven, cpLarge, cpZebra, cpUnknown
17   };
18 #endif
19 
enable_canvas()20 EX void enable_canvas() {
21   firstland = specialland = laCanvas;
22   randomPatternsMode = false;
23   land_structure = lsSingle;
24   }
25 
ctof(cell * c)26 EX int ctof(cell *c) {
27   #if CAP_IRR
28   if(IRREGULAR) return irr::ctof(c);
29   #endif
30   if(PURE) return 1;
31   // if(euclid) return 0;
32   if(!c) return 1;
33   if(bt::in()) return c->type == 7;
34   return ishept(c) ? 1 : 0;
35   // c->type == 6 ? 0 : 1;
36   }
37 
ctof012(cell * c)38 EX int ctof012(cell *c) {
39   return ishept(c)?1:ishex1(c)?0:2;
40   }
41 
gp_threecolor()42 int gp_threecolor() {
43   if(cgflags & qPORTALSPACE) return 0;
44   if(!GOLDBERG) return 0;
45   #if CAP_GP
46   if(S3 == 3 && (gp::param.first - gp::param.second) % 3 == 0) return 2;
47   #endif
48   return 1;
49   }
50 
eupattern(cell * c)51 int eupattern(cell *c) {
52   if(cgflags & qPORTALSPACE) return 0;
53   auto co = euc2_coordinates(c);
54   int x = co.first, y = co.second;
55   if(a4)
56     return ((x&1) + 2*(y&1)) % 3;
57   else
58     return gmod(y-x, 3);
59   }
60 
eupattern4(cell * c)61 int eupattern4(cell *c) {
62   auto co = euc2_coordinates(c);
63   int x = co.first, y = co.second;
64   return (x&1) + ((y&1)) * 2;
65   }
66 
ishept(cell * c)67 EX bool ishept(cell *c) {
68   if(cgflags & qPORTALSPACE) return 0;
69   // EUCLIDEAN
70   if(euc::in() && PURE) return eupattern(c) == 0;
71   else if(hybri) { cell *c1 = hybrid::get_where(c).first; return c1 == c1->master->c7; }
72   else return c == c->master->c7;
73   }
74 
ishex1(cell * c)75 EX bool ishex1(cell *c) {
76   if(cgflags & qPORTALSPACE) return 0;
77   // EUCLIDEAN
78   if(euc::in() && PURE) return eupattern(c) == 1;
79   #if CAP_GP
80   else if(GOLDBERG) return c->master->c7 != c && !pseudohept(c->move(0));
81   #endif
82   else return c->master->c7 != c;
83   }
84 
ishex2(cell * c)85 bool ishex2(cell *c) {
86   if(cgflags & qPORTALSPACE) return 0;
87   // EUCLIDEAN
88   if(euc::in() && PURE) return eupattern(c) == 1;
89   #if CAP_GP
90   else if(GOLDBERG) return c->master->c7 != c && gp::pseudohept_val(c) == 1;
91   #endif
92   else return c->master->c7 != c;
93   }
94 
chessvalue(cell * c)95 EX int chessvalue(cell *c) {
96   if(cgflags & qPORTALSPACE) return 0;
97   #if CAP_ARCM
98   if(arcm::in())
99     return arcm::chessvalue(c);
100   else
101   #endif
102   #if CAP_GP
103   if(WARPED)
104     return gp::untruncated_shift(c) == 2;
105   else if(UNRECTIFIED && a4) {
106     auto li = gp::get_local_info(c);
107     bool odd_a = gp::param.first & 1;
108     bool odd_b = gp::param.second & 1;
109     // odd-odd
110     if(odd_a && odd_b)
111       return (li.relative.second & 1) ^ (li.last_dir & 1) ^ (c->master->dm4 & 1) ^ ((c->master->emeraldval & 1) ? 1 : 0);
112     else if(odd_a || odd_b) {
113       /* int swapped = 0;
114       cellwalker cw(c, 0);
115       while((li.relative.first ^ li.relative.second) & 1) {
116         cw += wstep; cw += 2; swapped ^= 1;
117         } */
118       if((li.relative.first ^ li.relative.second) & 1)
119         return (li.relative.first & 1) ^ ((c->master->cmove(0)->emeraldval & 4) ? 1 : 0);
120       else
121         return (li.relative.first & 1) ^ ((c->master->emeraldval & 4) ? 1 : 0);
122       }
123     else
124       return (li.relative.second & 1) ^ (li.last_dir & 1) ^ ((c->master->emeraldval & 1) ? 1 : 0);
125     }
126   else
127   #endif
128     return celldist(c) & 1;
129   }
130 
emeraldval(heptagon * h)131 EX int emeraldval(heptagon *h) { return h->emeraldval >> 3; }
132 
emeraldval(cell * c)133 EX int emeraldval(cell *c) {
134   if(euclid) return eupattern(c);
135   if(sphere) return 0;
136   if(ctof(c))
137     return emeraldval(c->master);
138   else {
139     auto ar = gp::get_masters(c);
140     return emerald_hexagon(
141       emeraldval(ar[0]),
142       emeraldval(ar[1]),
143       emeraldval(ar[2])
144       );
145     }
146   }
147 
148 // === FIFTYVALS ===
149 
bitmajority(unsigned a,unsigned b,unsigned c)150 unsigned bitmajority(unsigned a, unsigned b, unsigned c) {
151   return (a&b) | ((a^b)&c);
152   }
153 
eufifty(cell * c)154 int eufifty(cell *c) {
155   auto co = euc2_coordinates(c);
156   int x = co.first, y = co.second;
157   int ix = x + 99999 + y;
158   int iy = y + 99999;
159   if(c->land == laWildWest)
160     return (ix + iy * 26 + 28) % 37;
161   else {
162     ix += (iy/3) * 3;
163     iy %= 3; ix %= 9;
164     return iy * 9 + ix;
165     }
166   }
167 
fiftyval(cell * c)168 int fiftyval(cell *c) {
169   if(euclid) return eufifty(c) * 32;
170   if(sphere || S7>7 || S6>6) return 0;
171   if(ctof(c))
172     return c->master->fiftyval;
173   else {
174     auto ar = gp::get_masters(c);
175     return bitmajority(
176       ar[0]->fiftyval,
177       ar[1]->fiftyval,
178       ar[2]->fiftyval) + 512;
179     }
180   }
181 
cdist50(cell * c)182 EX int cdist50(cell *c) {
183   if(euclid && S3 == 4) {
184     auto co = euc2_coordinates(c);
185     int x = co.first, y = co.second;
186     return abs(szgmod(x, 5)) + abs(zgmod(y, 5));
187     }
188   if(sphere || S7>7 || S6>6) return 0;
189   if(euclid) {
190     if(c->land == laWildWest)
191       return "0123333332112332223322233211233333322"[eufifty(c)] - '0';
192     else return "012333321112322232222321123"[eufifty(c)] - '0';
193     }
194   if(ctof(c)) return cdist50(c->master->fiftyval);
195   auto ar = gp::get_masters(c);
196   int a0 = cdist50(ar[0]->fiftyval);
197   int a1 = cdist50(ar[1]->fiftyval);
198   int a2 = cdist50(ar[2]->fiftyval);
199   if(a0 == 0 || a1 == 0 || a2 == 0) return 1;
200   return a0+a1+a2-5;
201   }
202 
land50(cell * c)203 int land50(cell *c) {
204   if(sphere || euclid) return 0;
205   else if(ctof(c)) return land50(fiftyval(c));
206   else {
207     auto ar = gp::get_masters(c);
208     for(int i=0; i<3; i++)
209       if(cdist50(ar[i]->fiftyval) < 3) return land50(ar[i]->fiftyval);
210     return 0;
211     }
212   }
213 
polara50(cell * c)214 EX bool polara50(cell *c) {
215   if(sphere || euclid || S7>7 || S6>6) return false;
216   else if(NONSTDVAR) return polara50(fiftyval(c->master->c7));
217   else if(ctof(c)) return polara50(fiftyval(c));
218   else {
219     auto ar = gp::get_masters(c);
220     for(int i=0; i<3; i++)
221       if(cdist50(ar[i]->fiftyval) < 3) return polara50(ar[i]->fiftyval);
222     return false;
223     }
224   }
225 
polarb50(cell * c)226 EX bool polarb50(cell *c) {
227   if(euclid) return true;
228   if(sphere || euclid || S7>7 || S6>6) return true;
229   else if(NONSTDVAR) return polarb50(fiftyval(c->master->c7));
230   else if(ctof(c)) return polarb50(fiftyval(c));
231   else {
232     auto ar = gp::get_masters(c);
233     for(int i=0; i<3; i++)
234       if(cdist50(ar[i]->fiftyval) < 3) return polarb50(ar[i]->fiftyval);
235     return false;
236     }
237   }
238 
239 int elhextable[28][3] = {
240   {0,1,2}, {1,2,9}, {1,9,-1}, {1,8,-1}, {1,-1,-1}
241   };
242 
fiftyval049(heptagon * h)243 EX int fiftyval049(heptagon *h) {
244   int i = h->fiftyval / 32;
245   if(i <= 7) return i;
246   if(quotient) return 0;
247   vector<int> allcodes;
248   for(int k=0; k<7; k++) {
249     heptagon *h2 = createStep(h, k);
250     if(polara50(h2->fiftyval) == polara50(h->fiftyval) && polarb50(h2->fiftyval) == polarb50(h->fiftyval))
251       allcodes.push_back(fiftyval049(h2));
252     }
253   int d = allcodes[1] - allcodes[0];
254   if(d == -1 || d == 6) swap(allcodes[0], allcodes[1]);
255   // printf("%d,%d: %d\n", allcodes[0], allcodes[1], allcodes[0] + 7);
256   return allcodes[0] + 7;
257   }
258 
fiftyval049(cell * c)259 EX int fiftyval049(cell *c) {
260   if(euclid) return fiftyval(c) / 32;
261   else if(ctof(c)) return fiftyval049(c->master);
262   else if(sphere) return 0;
263   else {
264     int a[3], qa=0;
265     bool pa = polara50(c);
266     bool pb = polarb50(c);
267     auto ar = gp::get_masters(c);
268     for(int i=0; i<3; i++)
269       if(polara50(ar[i]->fiftyval) == pa && polarb50(ar[i]->fiftyval) == pb)
270         a[qa++] = fiftyval049(ar[i]);
271     // 0-1-2
272     sort(a, a+qa);
273     if(qa == 1) return 43+a[0]-1;
274     if(qa == 2 && a[1] == a[0]+7) return 36+a[0]-1;
275     if(qa == 2 && a[1] != a[0]+7) return 29+a[0]-1;
276     // 3: zgodnie
277     // 1: zgodnie
278     // 0: przeciwnie
279     // 2: przeciwnie
280     // 168:
281     if(a[1] == 1 && a[2] == 7)
282       return 15 + 6; // (polarb50(c) ? 0 : 6);
283     if(a[2] >= 1 && a[2] <= 7) {
284       return 15 + a[1] - 1; // (polarb50(c) ? a[1]%7 : a[1]-1);
285       }
286     if(a[0] == 1 && a[1] == 7 && a[2] == 8)
287       return 22;
288     if(a[0] == 1 && a[1] == 7 && a[2] == 14)
289       return 22;
290     if(a[1] <= 7 && a[2] >= 8)
291       return 22 + a[1]-1;
292     return 0;
293     }
294   }
295 
fiftyval200(cell * c)296 EX int fiftyval200(cell *c) {
297   int i = fiftyval049(c);
298   i *= 4;
299   if(polara50(c)) i|=1;
300   if(polarb50(c)) i|=2;
301   return i;
302   }
303 
304 /*
305 {0,1,2} 15+0..15+6
306 {1,2,9},22+0..22+6
307 {1,9}   29+0..29+6
308 {1,8}   36+0..36+6
309 {1}     43+0..43+6
310 */
311 
312 // zebraval
313 
dir_bitrunc457(cell * c)314 int dir_bitrunc457(cell *c) {
315   if(GOLDBERG_INV) return c->master->zebraval / 10;
316   int wset = 0;
317   int has1 = 0;
318   for(int i=0; i<4; i++) {
319     int z = zebra40(createMov(c, i*2));
320     if(z&1) has1 = 1;
321     if(z&2) wset |= (1<<i);
322     }
323   if(wset == 0) return -8-has1;
324   if(wset == 15) return -10-has1;
325   if(wset == 3) return 1;
326   if(wset == 6) return 3;
327   if(wset == 12) return 5;
328   if(wset == 9) return 7;
329   return 0;
330   }
331 
332 int val46(cell *c);
333 
zebra40(cell * c)334 EX int zebra40(cell *c) {
335   if(euclid) return pattern_threecolor(c);
336   else if(IRREGULAR) return c->master->zebraval/10;
337   else if(INVERSE) {
338     cell *c1 = gp::get_mapped(c);
339     return UIU(zebra40(c1));
340     }
341   else if(a46) {
342     int v = val46(c);
343     if(v<4) return v;
344     else return 4+(v-4)/2;
345     }
346   else if(ctof(c)) return (c->master->zebraval/10);
347   else if(a4) {
348     if(GOLDBERG) return zebra40(c->master->c7);
349     int ws = dir_bitrunc457(c);
350     if(ws < 0) return -ws;
351     int tot = 0;
352     array<int, 4> zebras;
353     for(int i=0; i<4; i++) {
354       zebras[i] = zebra40(createMov(c, i*2));
355       tot += zebras[i];
356       }
357 
358     // break cycles
359     int cod = 0;
360     int mo = 0; for(int i=0; i<4; i++) if(zebras[i] < zebras[mo]) mo = i;
361     for(int i=0; i<4; i++) for(int j=1; j<i; j++)
362       if(zebras[(mo+i)&3] < zebras[(mo+j)&3]) cod ^= 4;
363 
364     if(tot == 0+2+4+6) return 16+cod;
365     if(tot == 1+3+5+7) return 19+cod;
366     if(tot == 0+1+2+3) return 18+cod;
367     if(tot == 4+5+6+7) return 17+cod;
368     return 24;
369     }
370   else if(sphere) return 0;
371   else if(S3 == 4 && S7 == 6) {
372     return 8 + ((c->master->zebraval / 10 + c->c.spin(0))%2) * 2;
373     }
374   else if(reg3::in()) return 0;
375   else {
376     int ii[3], z;
377     auto ar = gp::get_masters(c);
378     ii[0] = (ar[0]->zebraval/10);
379     ii[1] = (ar[1]->zebraval/10);
380     ii[2] = (ar[2]->zebraval/10);
381     for(int r=0; r<2; r++)
382       if(ii[1] < ii[0] || ii[2] < ii[0])
383         z = ii[0], ii[0] = ii[1], ii[1] = ii[2], ii[2] = z;
384     for(int i=0; i<28; i++)
385       if(zebratable6[i][0] == ii[0] && zebratable6[i][1] == ii[1] &&
386          zebratable6[i][2] == ii[2]) {
387            int ans = 16+i;
388            // if(ans >= 40) ans ^= 2;
389            // if(ans >= 4  && ans < 16) ans ^= 2;
390            return ans;
391            }
392     return 0;
393     }
394   }
395 
zebra3(cell * c)396 EX int zebra3(cell *c) {
397   if(euclid) return 0;
398   else if(ctof(c)) return (c->master->zebraval/10)/4;
399   else if(euclid || sphere || S7>7 || S6>6) return 0;
400   else {
401     int ii[3];
402     auto ar = gp::get_masters(c);
403     ii[0] = (ar[0]->zebraval/10)/4;
404     ii[1] = (ar[1]->zebraval/10)/4;
405     ii[2] = (ar[2]->zebraval/10)/4;
406     if(ii[0] == ii[1]) return ii[0];
407     if(ii[1] == ii[2]) return ii[1];
408     if(ii[2] == ii[0]) return ii[2];
409     return 0;
410     }
411   }
412 
413 #if CAP_FIELD
414 EX namespace fieldpattern {
415 
fieldval(cell * c)416 EX pair<int, bool> fieldval(cell *c) {
417   if(WDIM == 3) return make_pair(currfp.inverses[int(c->master->fieldval) * currfp.local_group], false);
418   else if(ctof(c)) return make_pair(int(c->master->fieldval), false);
419   else return make_pair(btspin(c->master->fieldval, c->c.spin(0)), true);
420   }
421 
fieldval_uniq(cell * c)422 EX int fieldval_uniq(cell *c) {
423   if(fake::in()) return FPIU(fieldval_uniq(c));
424   if(experimental) return 0;
425   if(reg3::in() && !PURE) return 0;
426   else if(arb::in()) return arb::id_of(c->master);
427   else if(hybri) {
428     auto c1 = hybrid::get_where(c).first;
429     return PIU ( fieldval_uniq(c1) );
430     }
431   else if(sphere) {
432     if(arcm::in()) return c->master->fiftyval;
433     #if CAP_IRR
434     else if(IRREGULAR) return irr::cellindex[c];
435     #endif
436     #if CAP_GP
437     else if(GOLDBERG_INV) return (get_code(gp::get_local_info(c)) << 8) | (c->master->fieldval / S7);
438     #endif
439     if(ctof(c)) return c->master->fieldval;
440     else return createMov(c, 0)->master->fieldval + 256 * createMov(c,2)->master->fieldval + (1<<16) * createMov(c,4)->master->fieldval;
441     }
442   else if(euc::in(2)) {
443     auto p = euc2_coordinates(c);
444     if(bounded) return p.first + p.second * (1 << 16);
445     return gmod(p.first - 22 * p.second, 3*127);
446     }
447   else if(euc::in(3)) {
448     auto co = euc::get_ispacemap()[c->master];
449     if(bounded) return co[0] + (co[1] << 10) + (co[2] << 20);
450     return gmod(co[0] + 3 * co[1] + 9 * co[2], 3*127);
451     }
452   else if(bt::in() || arcm::in() || nil || S3 >= OINF || (cgflags & qIDEAL)) return 0;
453   else if(&currfp == &fp_invalid) return 0;
454   else if(geometry == gSpace535) return 0;
455   else if(WDIM == 3) return c->master->fieldval;
456   else if(ctof(c) || NONSTDVAR) return c->master->fieldval/S7;
457   else {
458     int z = 0;
459     for(int u=0; u<S6; u+=2)
460       z = max(z, btspin(createMov(c, u)->master->fieldval, c->c.spin(u)));
461     return -1-z;
462     }
463   }
464 
fieldval_uniq_rand(cell * c,int randval)465 EX int fieldval_uniq_rand(cell *c, int randval) {
466   if(hybri) {
467     auto c1 = hybrid::get_where(c).first;
468     return PIU ( fieldval_uniq_rand(c1, randval) );
469     }
470   if(sphere || euclid || NONSTDVAR)
471     // we do not care in these cases
472     return fieldval_uniq(c);
473   if(ctof(c)) return currfp.gmul(c->master->fieldval, randval)/7;
474   else {
475     int z = 0;
476     for(int u=0; u<6; u+=2)
477       z = max(z, btspin(currfp.gmul(createMov(c, u)->master->fieldval, randval), c->c.spin(u)));
478     return -1-z;
479     }
480   }
481 
subval(cell * c,int _subpathid=subpathid,int _subpathorder=subpathorder)482 pair<int, int> subval(cell *c, int _subpathid = subpathid, int _subpathorder = subpathorder) {
483 
484   if(_subpathid == -1)
485     _subpathid = currfp.matcode[currfp.strtomatrix("RRRPRRRRRPRRRP")];
486   if(_subpathorder == -1)
487     _subpathorder = currfp.order(currfp.matrices[subpathid]);
488 
489   if(!ctof(c)) {
490     auto m = subval(createMov(c, 0));
491     for(int u=2; u<S6; u+=2)
492       m = min(m, subval(createMov(c, u)));
493     return m;
494     }
495   else {
496     pair<int, int> pbest, pcur;
497     pcur.first = c->master->fieldval;
498     pcur.second = 0;
499     pbest = pcur;
500     for(int i=0; i<_subpathorder; i++) {
501       pcur.first = currfp.gmul(pcur.first, _subpathid);
502       pcur.second++;
503       if(pcur < pbest) pbest = pcur;
504       }
505     return pbest;
506     }
507   }
508 
509 EX }
510 #endif
511 
getHemisphere(heptagon * h,int which)512 EX int getHemisphere(heptagon *h, int which) {
513   int id = h->fiftyval;
514   if(S7 == 5) {
515     int hemitable[3][12] = {
516       { 6, 3, 3, 3, 3, 3,-6,-3,-3,-3,-3,-3},
517       { 6, 3, 6, 3, 0, 0,-6,-3,-6,-3, 0, 0},
518       {-3, 0, 3, 0,-6,-6, 3, 0,-3, 0, 6, 6}
519       };
520     return hemitable[which][id];
521     }
522   else if(S7 == 4 && which < 6) {
523     int hemitable[3][6] = {
524       { 2, 2, 2,-1,-1,-1},
525       { 2,-1, 2, 2,-1,-1},
526       { 2,-1,-1, 2, 2,-1},
527       };
528     return hemitable[which][id];
529     }
530   else if(S7 == 3) {
531     int hemitable[3][4] = {
532       { 2, 2,-1,-1},
533       { 2,-1, 2,-1},
534       { 2,-1,-1, 2},
535       };
536     return hemitable[which][id];
537     }
538   else return 0;
539   }
540 
getHemisphere(cell * c,int which)541 EX int getHemisphere(cell *c, int which) {
542   if(euclid && quotient) return 0;
543   if(hybri) { auto d = hybrid::get_where(c); return PIU(getHemisphere(d.first, which)); }
544   if(WDIM == 3 && !hybri) {
545     hyperpoint p = tC0(calc_relative_matrix(c, currentmap->gamestart(), C0));
546     return int(p[which] * 6  + 10.5) - 10;
547     }
548   if(which == 0 && GOLDBERG && has_nice_dual()) {
549     set<cell*> visited;
550     vector<cell*> q;
551     vector<int> type;
552     auto visit = [&] (cell *c, int t) {
553       if(visited.count(c)) return;
554       visited.insert(c);
555       q.push_back(c);
556       type.push_back(t);
557       };
558 
559     cellwalker cw(currentmap->gamestart(), 0);
560     int ct = 1;
561     visit(cw.at, ct);
562     do {
563       cw = cw + wstep;
564       visit(cw.at, -ct);
565       cw = cw + (2*ct) + wstep + ct;
566       ct = -ct;
567       }
568     while(cw.at != currentmap->gamestart());
569     for(int i=0; i<isize(q); i++)
570       forCellCM(c2, q[i])
571         if(pseudohept(q[i]) || pseudohept(c2))
572           visit(c2, type[i]);
573     for(int i=0; i<isize(q); i++) if(q[i] == c) return type[i];
574     return 0;
575     }
576   if(ctof(c))
577     return getHemisphere(c->master, which);
578   else {
579     int score = 0;
580     if(0) ;
581     #if CAP_GP
582     else if(GOLDBERG) {
583       auto li = gp::get_local_info(c);
584       gp::be_in_triangle(li);
585       auto corner = cgi.gpdata->corners * gp::loctoh_ort(li.relative);
586       ld scored =
587         corner[0] * getHemisphere(c->master->c7, which)
588       + corner[1] * getHemisphere(c->master->move(li.last_dir)->c7, which)
589       + corner[2] * getHemisphere(c->master->modmove(li.last_dir+1)->c7, which);
590       int score = int(scored + 10.5) - 10;
591       ld error = scored - score;
592       if(score == 0 && error > .001) score++;
593       if(score == 0 && error < -.001) score--;
594       return score;
595       }
596     #endif
597     #if CAP_IRR
598     else if(IRREGULAR) {
599       auto m = irr::get_masters(c);
600       for(int i=0; i<3; i++)
601         score += getHemisphere(m[i], which);
602       return score / 3;
603       }
604     #endif
605     else {
606       for(int i=0; i<6; i+=2)
607         score += getHemisphere(c->move(i), which) * (c->c.mirror(i) ? -1 : 1);
608       return score/3;
609       }
610     }
611   }
612 
613 /** \brief Various functions related to map patterns. */
614 EX namespace patterns {
615 
616   #if HDR
617   enum ePattern : char {
618     PAT_NONE = 0,
619     PAT_TYPES = 'T',
620     PAT_ZEBRA = 'z',
621     PAT_EMERALD = 'f',
622     PAT_PALACE = 'p',
623     PAT_FIELD = 'F',
624     PAT_DOWN = 'H',
625     PAT_COLORING = 'C',
626     PAT_SIBLING = 'S',
627     PAT_CHESS = 'c',
628     PAT_SINGLETYPE = 't'
629     };
630 
631   static const int SPF_ROT = 1;
632   static const int SPF_SYM01 = 2;
633   static const int SPF_SYM02 = 4;
634   static const int SPF_SYM03 = 8;
635   static const int SPF_CHANGEROT = 16;
636   static const int SPF_TWOCOL = 32;
637   static const int SPF_EXTRASYM = 64;
638   static const int SPF_ALTERNATE = 128;
639   static const int SPF_FOOTBALL = 256;
640   static const int SPF_FULLSYM = 512;
641   static const int SPF_DOCKS = 1024;
642   static const int SPF_NO_SUBCODES = 2048;
643 
644   static const int SPF_SYM0123 = SPF_SYM01 | SPF_SYM02 | SPF_SYM03;
645 
646   struct patterninfo {
647     int id;
648     int dir;
649     bool reflect;
650     /** 1 if all symmetries, c->type if no symmetries */
651     int symmetries;
652     };
653   #endif
654 
valSibling(cell * c,patterninfo & si,int sub,int pat)655   void valSibling(cell *c, patterninfo& si, int sub, int pat) {
656     if(ctof(c)) {
657       int d = c->master->fieldval;
658       si.id = (d < siblings[d]) ? 0 : 1;
659       if(sub & SPF_ROT) si.id = 0;
660       for(int i=0; i<S7; i++) {
661         int di = c->master->move(i)->fieldval;
662         if(di == siblings[d]) si.dir = i;
663         }
664       si.reflect = false;
665       }
666     else {
667       int ids = 0, td = 0;
668       for(int i=0; i<S3; i++) {
669         int d = c->move(2*i)->master->fieldval;
670         ids |= (1<<d);
671         }
672       for(int i=0; i<S3; i++) {
673         int d = c->move(2*i)->master->fieldval;
674         if(ids & (1<<siblings[d])) td += d;
675         }
676       if(td) {
677         si.id = 4;
678         for(int i=0; i<S3; i++) {
679           int d = c->move(2*i)->master->fieldval;
680           if(!(ids & (1<<siblings[d]))) si.dir = 2*i;
681           }
682         /* if(!(sub & SPF_ROT)) {
683           int d0 = c->modmove(si.dir+2)->master->fieldval;
684           if(d0 < siblings[d0]) si.id += 8;
685           } */
686         si.reflect = false;
687         }
688       else {
689         si.id = 8;
690         si.dir = 0; // whatever
691         patterninfo si2;
692         valSibling(c->move(0), si2, sub, pat);
693         int di = si2.dir - c->c.spin(0);
694         di %= S7;
695         if(di<0) di += S7;
696         if(pat == PAT_SIBLING) si.reflect = di > S7/2;
697         if(sub & SPF_ROT) si.symmetries = 2;
698         }
699       }
700     }
701 
downdir(cell * c,const cellfunction & cf)702   EX int downdir(cell *c, const cellfunction& cf) {
703     return parent_id(c, 1, cf) + 1;
704     }
705 
applySym0123(int & i,int sub)706   void applySym0123(int& i, int sub) {
707     bool sym01 = sub & SPF_SYM01;
708     bool sym02 = sub & SPF_SYM02;
709     bool sym03 = sub & SPF_SYM03;
710     if((sym01?1:0)+(sym02?1:0)+(sym03?1:0) >= 2) i &= ~3;
711     if(sym01 && (i&1)) i ^= 1;
712     if(sym02 && (i&2)) i ^= 2;
713     if(sym03 && (i&2)) i ^= 3;
714     }
715 
applyAlt(patterninfo & si,int sub,int pat)716   void applyAlt(patterninfo& si, int sub, int pat) {
717     if(sub & SPF_ALTERNATE) {
718       si.id += 4;
719       si.id %= 12;
720       }
721     if(pat == PAT_COLORING && (sub & SPF_FOOTBALL)) {
722       if(si.id == 4) si.dir++;
723       si.id = !si.id;
724       if(si.id && (sub & SPF_EXTRASYM))
725         si.symmetries = si.id ? 1 : 2;
726       return;
727       }
728     }
729 
val46(cell * c,patterninfo & si,int sub,int pat)730   void val46(cell *c, patterninfo &si, int sub, int pat) {
731     if(ctof(c)) {
732       si.id = c->master->emeraldval >> 1;
733       applySym0123(si.id, sub);
734       if(sub & SPF_CHANGEROT)
735         si.dir = (c->master->emeraldval&1);
736       else
737         si.dir = (c->master->emeraldval&1) ^ (c->master->emeraldval>>1);
738       si.symmetries = 2;
739       applyAlt(si, sub, pat);
740       /* printf("[%3d] ", c->master->emeraldval);
741       for(int i=0; i<6; i++) printf("%2d", val46(createMov(c, i)));
742       printf("\n"); */
743       }
744     else {
745       si.id = ((c->master->emeraldval & 1) ^ ((c->master->emeraldval & 2)>>1) ^ (c->c.spin(0)&1)) ? 8 : 4;
746       si.dir = ((c->move(0)->master->emeraldval + c->c.spin(0)) & 1) ? 2 : 0;
747       if(createMov(c, si.dir)->master->emeraldval & 4)
748         si.dir += 4;
749 
750       if((sub & SPF_TWOCOL) && (pat == PAT_COLORING)) si.id = 4;
751       else if(pat == PAT_COLORING && si.id == 4) si.dir++;
752 
753       if(sub & SPF_SYM01) si.symmetries = 2;
754       else if(sub & SPF_SYM03) si.symmetries = 2;
755       else if(sub & SPF_SYM02) si.symmetries = 4;
756 
757       applyAlt(si, sub, pat);
758       }
759     }
760 
761   // if(a46) return patterndir46(c, w == PAT_ZEBRA ? 3 : w == PAT_PALACE ? 2 : 1);
762 
inr(int a,int b,int c)763   int inr(int a, int b, int c) { return a >= b && a < c; }
764 
val457(cell * c,patterninfo & si,int sub)765   void val457(cell *c, patterninfo &si, int sub) {
766     si.id = zebra40(c);
767     if(inr(si.id, 8, 12)) si.symmetries = 4;
768     applySym0123(si.id, sub);
769     if(sub & SPF_ROT) {
770       if(si.id >= 4 && si.id < 7) si.id -= 4;
771       if(si.id >= 20 && si.id < 23) si.id -= 4;
772       }
773     if(ctof(c)) {
774       for(int i=0; i<c->type; i++)
775         if((zebra40(createStep(c->master, i + S7/2)->c7)&2) == (zebra40(createStep(c->master, i + 1 + S7/2)->c7)&2))
776           si.dir = i;
777       }
778     else {
779       int d = dir_bitrunc457(c);
780       if(d >= 0) si.dir = d;
781       else si.dir = (zebra40(createMov(c, 0)) & 4) ? 2 : 0;
782       }
783     }
784 
val38(cell * c,patterninfo & si,int sub,int pat)785   EX void val38(cell *c, patterninfo &si, int sub, int pat) {
786     bool symRotation = sub & SPF_ROT;
787 
788     if(ctof(c)) {
789       if(!symRotation)
790         si.id = (c->master->fiftyval >> 1) & 3;
791       else
792         si.id = 0;
793       if(!BITRUNCATED && gp_threecolor() != 2)
794         si.id *= 4;
795       else
796         si.id += 4;
797       si.dir = (pat == PAT_COLORING && BITRUNCATED ? 1 : 0) + (c->master->fiftyval | (c->master->fiftyval & 8 ? 0 : 2));
798       si.symmetries = 2;
799       si.id += 8;
800       si.id %= 12;
801       applyAlt(si, sub, pat);
802       if((sub & SPF_DOCKS) && (c->master->fiftyval & 32))
803         si.id += 16, si.symmetries = 4;
804       }
805     else {
806       int sp = c->c.spin(0);
807       #if CAP_GP
808       if(GOLDBERG) {
809         sp = gp::last_dir(c);
810         sp ^= int(ishex2(c));
811         }
812       #endif
813       if(geometry == gBolza2 && (!GOLDBERG || gp_threecolor() == 2)) {
814         patterninfo si0;
815         patterninfo si1;
816         #if CAP_GP
817         if(GOLDBERG) {
818           auto li = gp::get_local_info(c);
819           val38(c->master->c7, si0, 0, PAT_COLORING);
820           val38(c->master->move(li.last_dir)->c7, si1, 0, PAT_COLORING);
821           }
822         else
823         #endif
824         {
825           val38(c->move(0), si0, 0, PAT_COLORING);
826           val38(c->move(2), si1, 0, PAT_COLORING);
827           }
828         if((si0.id+1) % 3 == (si1.id) % 3)
829           si.id = 8;
830         else
831           si.id = 0;
832         }
833       else
834         si.id = 8 * ((c->master->fiftyval & 1) ^ (sp & 1));
835       #if CAP_GP
836       if(GOLDBERG && pseudohept(c)) si.id = 4;
837       #endif
838       bool dock = false;
839       for(int i=0; i<c->type; i+=2) {
840         int fiv = createMov(c, i)->master->fiftyval;
841         int fv = (fiv >> 1) & 3;
842         if(fv == 0) {
843           si.dir = (si.id == 8 && pat == PAT_COLORING ? 1 : 0) + i;
844           if(fiv & 32) dock = true;
845           }
846         }
847       if(symRotation) si.symmetries = 2;
848       si.id += 8;
849       si.id %= 12;
850       #if CAP_GP
851       if(GOLDBERG && pat == PAT_COLORING)
852         for(int i=0; i<c->type; i++) {
853           cell *c2 = createMov(c, i);
854           int id2 = 4;
855           if(!pseudohept(c2)) {
856             int sp2 = c2->c.spin(0);
857             if(GOLDBERG) {
858               sp2 = gp::last_dir(c2);
859               sp2 ^= int(ishex2(c2));
860               }
861             id2 = 8 * ((c2->master->fiftyval & 1) ^ (sp2 & 1));
862             }
863 //          printf("%p %2d : %d %d\n", c, si.id, i, id2);
864           if((id2+4) % 12 == si.id) si.dir = i;
865           }
866       #endif
867       applyAlt(si, sub, pat);
868       if(dock && (sub & SPF_DOCKS)) si.id += 16;
869       }
870     }
871 
valEuclid6(cell * c,patterninfo & si,int sub)872   void valEuclid6(cell *c, patterninfo &si, int sub) {
873     bool symRotation = sub & SPF_ROT;
874     si.id = ishept(c) ? 4 : ishex1(c) ? 8 : 0;
875     if(sub & SPF_CHANGEROT) {
876       si.dir = (zebra40(c)*4) % 6;
877       }
878     if(symRotation) si.id = 1;
879     if(euc::in(2,6) && (sub & SPF_FULLSYM))
880       si.symmetries = 1;
881     }
882 
valEuclid4(cell * c,patterninfo & si,int sub)883   void valEuclid4(cell *c, patterninfo &si, int sub) {
884     si.id = eupattern4(c);
885     applySym0123(si.id, sub);
886     if(sub & SPF_CHANGEROT) {
887       int dirt[] = {0,1,3,2};
888       si.dir = dirt[si.id];
889       if(c->type == 8) si.dir *= 2;
890       }
891     if(sub & SPF_SYM03) {
892       si.id *= 4;
893       applyAlt(si, sub, PAT_COLORING);
894       }
895     else
896       si.symmetries = (sub & SPF_EXTRASYM) ? c->type/4 : c->type;
897     }
898 
val_all(cell * c,patterninfo & si,int sub,int pat)899   void val_all(cell *c, patterninfo &si, int sub, int pat) {
900     if(IRREGULAR || arcm::in() || bt::in() || arb::in() || WDIM == 3 || currentmap->strict_tree_rules()) si.symmetries = c->type;
901     else if(a46) val46(c, si, sub, pat);
902     else if(a38) val38(c, si, sub, pat);
903     else if(S7 < 6 && S3 == 3 && !INVERSE && !kite::in()) valSibling(c, si, sub, pat);
904     else if(euc::in(2,4)) valEuclid4(c, si, sub);
905     else if(euc::in(2,6)) valEuclid6(c, si, sub);
906     else if(a4) val457(c, si, sub);
907     else si.symmetries = ctof(c) ? 1 : 2;
908     }
909 
val_warped(cell * c,patterninfo & si)910   void val_warped(cell *c, patterninfo& si) {
911     int u = ishept(c)?1:0;
912     if(S3 != 3 || S7 != 7 || NONSTDVAR) {
913       si.id = u;
914       si.dir = 1;
915       return;
916       }
917     int qhex = 0;
918     for(int v=0; v<c->type; v++) if(c->move(v) && !isWarped(c->move(v))) {
919       u += 2;
920       if(!ishept(c->move(v))) qhex++;
921       }
922     if(u == 8 && qhex == 2) u = 12;
923     else if(u == 2 && qhex == 1) u = 8;
924     else if(u == 6 && qhex == 2) u = 10;
925     si.id = u;
926 
927     if(u == 6) {
928         for(int i=1; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
929             si.dir = i;
930         }
931 
932     else if(u == 2 || u == 3 || u == 8) {
933         for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,i)))
934             si.dir = i;
935         }
936 
937     else if(u == 4 || u == 10) {
938         for(int i=0; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
939             si.dir = i;
940         if(u == 4)
941           si.reflect = !isWarped(createMov(c, (si.dir+1)%S6));
942         }
943 
944     else if(u == 6) {
945         for(int i=1; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
946             si.dir = i;
947         }
948 
949     else if(u == 5) {
950         for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+S7/2)%S7)) && !isWarped(createMov(c,(i+(S7+1)/2)%S7)))
951             si.dir = i;
952         }
953 
954     else if(u == 9) {
955         for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+2)%S7)) && !isWarped(createMov(c,(i+S7-2)%S7)))
956             si.dir = i;
957         }
958 
959     else if(u == 11) {
960         for(int i=0; i<c->type; i++) if(isWarped(createMov(c,(i)%S7)) && isWarped(createMov(c,(i+1)%S7)))
961             si.dir = i;
962         }
963 
964     else if(u == 12) {
965         for(int i=0; i<c->type; i+=2) if(isWarped(createMov(c,i))) {
966             si.dir = i;
967             si.reflect = !isWarped(createMov(c, (i+1)%S6));
968             }
969         }
970 
971     else if(u == 7) {
972         for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+1)%S7)) && !isWarped(createMov(c,(i+S7-1)%S7)))
973             si.dir = i;
974         }
975     }
976 
val_nopattern(cell * c,patterninfo & si,int sub)977   void val_nopattern(cell *c, patterninfo& si, int sub) {
978     // use val_all for nicer rotation
979     val_all(c, si, 0, 0);
980 
981     // get id:
982     if((GOLDBERG? (S3==3) : !weirdhyperbolic) && isWarped(c))
983       val_warped(c, si);
984     else {
985       si.id = pseudohept(c) ? 1 : 0;
986       if(euc::in()) {
987         si.dir = ishex1(c) ? 0 : 3;
988         if(ctof(c)) si.symmetries = 3;
989         if(subpattern_flags & SPF_EXTRASYM)
990           si.symmetries /= 3;
991         if(subpattern_flags & SPF_FULLSYM)
992           si.symmetries = 1;
993         }
994       if(sphere && BITRUNCATED && !(S7 == 3))
995         si.symmetries = ctof(c) ? 1 : 2;
996       if(sphere && (sub & SPF_EXTRASYM)) {
997         si.symmetries = ctof(c) ? 1 : 2;
998         }
999       if(a38)
1000         si.symmetries = (ctof(c) && BITRUNCATED) ? 1 : 2;
1001       if(a457) {
1002         si.symmetries = ctof(c) ? 1 : 2;
1003         if(!ctof(c)) si.dir = 0;
1004         }
1005       if(a46) {
1006         si.symmetries = ctof(c) ? 1 : 2;
1007         }
1008       }
1009 
1010     if(GOLDBERG && has_nice_dual() && !ishept(c) && ishex1(c)) si.dir = gmod(si.dir+3, S6);
1011     }
1012 
1013   EX ePattern whichPattern = PAT_NONE;
1014 
1015   EX int subpattern_flags;
1016 
val_threecolors(cell * c,patterninfo & si,int sub)1017   void val_threecolors(cell *c, patterninfo& si, int sub) {
1018     int pcol = pattern_threecolor(c);
1019     si.id = pcol * 4;
1020     pcol = (pcol+1) % 3;
1021     si.dir = -1;
1022     for(int i=0; i<c->type; i++)
1023       if(pattern_threecolor(createMov(c, i)) == pcol) {
1024         if(si.dir == -1) si.dir = i;
1025         else {
1026           si.symmetries = i - si.dir;
1027           break;
1028           }
1029         }
1030     if(euc::in(2,6) && (sub & SPF_CHANGEROT)) {
1031       if(GOLDBERG) {
1032         auto li = gp::get_local_info(c);
1033         if(li.first_dir >= 0)
1034           si.dir = gmod(zebra40(c)*4 - li.total_dir - li.last_dir, 6);
1035         else
1036           si.dir = gmod(zebra40(c)*4, 6);
1037         }
1038       else if(c == c->master->c7)
1039         si.dir = (zebra40(c)*4) % 6;
1040       else
1041         si.dir = (zebra40(c)*4 + 9 - c->c.spin(0)) % 6;
1042       }
1043     if(sub & SPF_ROT) si.id = 1;
1044     if(euc::in(2,6) && !(sub & SPF_EXTRASYM)) {
1045       si.symmetries = 6;
1046       }
1047     if(euc::in(2,6) && (sub & SPF_FULLSYM))
1048       si.symmetries = 1;
1049     applyAlt(si, sub, PAT_COLORING);
1050     }
1051 
getpatterninfo(cell * c,ePattern pat,int sub)1052   EX patterninfo getpatterninfo(cell *c, ePattern pat, int sub) {
1053     if(fake::in()) return FPIU(getpatterninfo(c, pat, sub));
1054     if(!(sub & SPF_NO_SUBCODES)) {
1055       auto si = getpatterninfo(c, pat, sub | SPF_NO_SUBCODES);
1056       if(1) ;
1057       #if CAP_IRR
1058       else if(IRREGULAR)
1059         si.id += irr::cellindex[c] << 8;
1060       #endif
1061       #if CAP_ARCM
1062       else if(arcm::in())
1063         si.id += (arcm::id_of(c->master) << 8) + (arcm::parent_index_of(c->master) << 16);
1064       #endif
1065       #if CAP_GP
1066       else if(GOLDBERG) {
1067         if(c == c->master->c7) si.id += (c->c.fix(si.dir) << 8);
1068         else si.id += (get_code(gp::get_local_info(c)) << 16) | (c->c.fix(si.dir) << 8);
1069         }
1070       #endif
1071       return si;
1072       }
1073     bool symRotation = sub & SPF_ROT;
1074     // bool sym0 = sub & (SPF_SYM01 | SPF_SYM02 | SPF_SYM03);
1075 
1076     patterninfo si;
1077     si.dir = 0; si.reflect = false; si.id = ctof(c);
1078     si.symmetries = c->type;
1079 
1080     if(bt::in()) {
1081       if(pat == PAT_SINGLETYPE) si.id = 0;
1082       else si.id = c->type & 1;
1083       si.dir = 2;
1084       return si;
1085       }
1086 
1087     if(pat == PAT_SINGLETYPE) {
1088       si.id = 0; si.symmetries = 1;
1089       if(sub & SPF_TWOCOL) si.id = c->type & 1;
1090       if(sub & SPF_EXTRASYM) si.reflect = true;
1091       return si;
1092       }
1093 
1094     if(currentmap->strict_tree_rules()) return si;
1095 
1096     if(arb::in()) {
1097       si.id = arb::id_of(c->master);
1098       si.dir = 0;
1099       si.symmetries = c->type / arb::current.shapes[si.id].repeat_value;
1100       si.reflect = false;
1101       return si;
1102       }
1103 
1104     #if CAP_ARCM
1105     if(arcm::in() && pat == 0) {
1106       if(sub & SPF_FOOTBALL) {
1107         val_threecolors(c, si, sub);
1108         return si;
1109         }
1110       int id = arcm::id_of(c->master);
1111       auto& ca = arcm::current;
1112       si.id = ca.tilegroup[id];
1113       si.symmetries = ca.periods[si.id];
1114       si.dir = ca.groupoffset[id];
1115       if((sub & SPF_EXTRASYM) && ca.have_symmetry && ca.tilegroup[id^1] < ca.tilegroup[id])
1116         si.id = ca.tilegroup[id^1],
1117         si.reflect = true;
1118       return si;
1119       }
1120     #endif
1121 
1122     if(pat == PAT_ZEBRA && stdhyperbolic) {
1123 
1124       si.id = zebra40(c); // 4 to 43
1125       int t4 = si.id>>2, tcdir = 0;
1126 
1127       if(PURE) tcdir = si.id^1;
1128 
1129       else if(t4 == 10) tcdir = si.id-20;
1130       else if(t4 >= 4 && t4 < 7) tcdir = 40 + (si.id&3);
1131       else if(t4 >= 1 && t4 < 4) tcdir = si.id+12;
1132       else if(t4 >= 7 && t4 < 10) tcdir = si.id-24;
1133 
1134       for(int i=0; i<c->type; i++) if(c->move(i) && zebra40(c->move(i)) == tcdir)
1135         si.dir = i;
1136 
1137       applySym0123(si.id, sub);
1138 
1139       if(symRotation) {
1140         if(si.id >= 8 && si.id < 12) si.id -= 4;
1141         if(si.id >= 12 && si.id < 16) si.id -= 8;
1142         if(si.id >= 20 && si.id < 24) si.id -= 4;
1143         if(si.id >= 24 && si.id < 28) si.id -= 8;
1144         if(si.id >= 32 && si.id < 36) si.id -= 4;
1145         if(si.id >= 36 && si.id < 40) si.id -= 8;
1146         }
1147 
1148       if(si.id >= 40 && si.id < 44 && symRotation)
1149         si.symmetries = 2;
1150 
1151       if(si.id >= 40 && si.id < 44 && (sub & (SPF_SYM02 | SPF_SYM03)))
1152         si.symmetries = 2;
1153       }
1154 
1155     else if(pat == PAT_EMERALD && (stdhyperbolic || a38)) {
1156       si.id = emeraldval(c); // 44 to 99
1157       if(!euclid) {
1158         int tcdir = 0, tbest = (si.id&3);
1159         for(int i=0; i<c->type; i++) {
1160           cell *c2 = c->move(i);
1161           if(c2) {
1162             int t2 = emeraldval(c2);
1163             if((si.id&3) == (t2&3) && t2 > tbest)
1164               tbest = t2, tcdir = i;
1165             }
1166           }
1167         si.dir = tcdir;
1168         }
1169       applySym0123(si.id, sub);
1170       if(si.id >= 44 && si.id < 48)
1171         si.symmetries = 2;
1172       }
1173 
1174     else if(pat == PAT_PALACE && stdhyperbolic) {
1175       si.id = fiftyval200(c);
1176 
1177       si.reflect = polara50(c);
1178 
1179       int look_for = -1;
1180       int shft = 0;
1181       if(inr(si.id, 0, 4)) {
1182         look_for = si.id + (PURE ? 4 : 60);
1183         if(symRotation) si.symmetries = 1;
1184         }
1185       else if(inr(si.id, 4, 32)) look_for = si.id + (PURE ? 28 : 168);
1186       else if(inr(si.id, 32, 60)) look_for = si.id + (PURE ? -28 : 112);
1187       else if(inr(si.id, 60, 88)) look_for = si.id - 56, shft = si.reflect ? 1 : 5;
1188       else if(inr(si.id, 88, 116)) look_for = si.id - 84, shft = 3;
1189       else if(inr(si.id, 116, 144)) look_for = si.id + 56;
1190       else if(inr(si.id, 144, 172)) look_for = si.id + 28;
1191       else if(inr(si.id, 172, 200)) look_for = si.id - 28;
1192 
1193       si.dir = -1;
1194       for(int i=0; i<c->type; i++) {
1195           cell *c2 = createMov(c, i);
1196           if(fiftyval200(c2) == look_for)
1197             si.dir = (i + shft) % c->type;
1198           }
1199 
1200       if(si.dir == -1) {
1201         si.dir = 0;
1202         if(c->cpdist <= 1) printf("Not found for ID = %d (lf=%d)\n", si.id, look_for);
1203         c->item = itBuggy;
1204         }
1205       applySym0123(si.id, sub);
1206 
1207       if(symRotation && si.id >= 4)
1208         si.id -= ((si.id/4-1) % 7) * 4;
1209       }
1210 
1211     else if(pat == PAT_PALACE && euclid) {
1212       si.id = fiftyval049(c);
1213       si.symmetries = 6;
1214       }
1215 
1216     else if(pat == PAT_PALACE) {
1217       val_nopattern(c, si, sub);
1218       si.id = c->master->fiftyval;
1219       }
1220 
1221     else if(pat == PAT_DOWN) {
1222       si.id = towerval(c, coastvalEdge);
1223       si.dir = downdir(c, coastvalEdge);
1224       }
1225 
1226     #if CAP_FIELD
1227     else if(pat == PAT_FIELD) {
1228       if(euclid)
1229         // use the torus ID
1230         si.id = fieldpattern::fieldval_uniq(c);
1231       else if(PURE && standard_tiling())
1232         // use the actual field codes
1233         si.id = fieldpattern::fieldval(c).first;
1234       else
1235         // use the small numbers from windmap
1236         si.id = windmap::getId(c);
1237       // todo dir
1238       }
1239     #endif
1240 
1241     else if(sphere && pat == PAT_SIBLING) {
1242       val_all(c, si, sub, pat);
1243       }
1244 
1245     else if(a457 && pat == PAT_ZEBRA) {
1246       val_all(c, si, sub, pat);
1247       }
1248 
1249     else if(GOLDBERG) {
1250       bool football = (pat == PAT_COLORING && (sub & SPF_FOOTBALL)) || pat == 0;
1251       if(football) val_nopattern(c, si, sub);
1252       else val_threecolors(c, si, sub);
1253       }
1254 
1255     else if(pat == PAT_COLORING && (S7 == 4 || euclid || (a38 && gp_threecolor() == 1) || arcm::in())) {
1256       val_threecolors(c, si, sub);
1257       }
1258 
1259     else if(pat == PAT_COLORING && (a46 || a38)) {
1260       val_all(c, si, sub, pat);
1261       }
1262 
1263     else if(pat == PAT_CHESS) {
1264       val_nopattern(c, si, sub);
1265       si.id = chessvalue(c);
1266       }
1267 
1268     else
1269       val_nopattern(c, si, sub);
1270 
1271     return si;
1272     }
1273 
1274   #if HDR
getpatterninfo0(cell * c)1275   inline patterninfo getpatterninfo0(cell *c) {
1276     return getpatterninfo(c, whichPattern, subpattern_flags);
1277     }
1278   #endif
1279 
1280   EX }
1281 
geosupport_chessboard()1282 EX bool geosupport_chessboard() {
1283   return
1284 #if CAP_ARCM
1285     (arcm::in() && PURE) ? arcm::current.support_chessboard() :
1286     (arcm::in() && DUAL) ? arcm::current.support_threecolor_bitruncated() :
1287 #endif
1288     WARPED ? true :
1289     INVERSE ? false :
1290     (bt::in() || kite::in()) ? 0 :
1291     (S3 >= OINF) ? true :
1292     (valence() % 2 == 0);
1293   }
1294 
geosupport_threecolor()1295 EX int geosupport_threecolor() {
1296   if(IRREGULAR) return 0;
1297   if(kite::in() || bt::in()) return 0;
1298   #if CAP_ARCM
1299   if(arcm::in() && PURE) return arcm::current.support_threecolor();
1300   if(arcm::in() && BITRUNCATED) return arcm::current.support_threecolor_bitruncated();
1301   if(arcm::in() && DUAL) return 0; // it sometimes does support threecolor, but it can be obtained in other ways then
1302   #endif
1303   if(INVERSE) return 0;
1304   if(BITRUNCATED && S3 == 3) {
1305     if(S7 % 2) return 1;
1306     return 2;
1307     }
1308   if(S3 >= OINF) return 0;
1309   if((S7 % 2 == 0) && (S3 == 3))
1310     return 2;
1311   if(a46 && PURE)
1312     return 1;
1313   return 0;
1314   }
1315 
geosupport_football()1316 EX int geosupport_football() {
1317   // always works in bitrunc geometries
1318   if(BITRUNCATED) return 2;
1319   if(INVERSE) return 0;
1320   if(bt::in() || kite::in()) return 0;
1321 
1322 #if CAP_ARCM
1323   if(arcm::in() && DUAL) return false;
1324   // it sometimes does support football, but it can be obtained in other ways then
1325 
1326   if(arcm::in() /* PURE */) return arcm::current.support_football();
1327 #endif
1328 
1329   if(arb::in()) return arb::current.have_ph;
1330 
1331 #if CAP_IRR
1332   if(IRREGULAR) return irr::bitruncations_performed ? 2 : 1;
1333 #endif
1334 
1335   // always works in patterns supporting three-color
1336   int tc = max(geosupport_threecolor(), gp_threecolor());
1337   if(tc) return tc;
1338 
1339   if(S3 == 3 && S7 == 7) return 1;
1340   // nice chessboard pattern, but not the actual Graveyard
1341   if(S3 == 4 && !(S7&1)) return 1;
1342   if(S3 == 4 && GOLDBERG) return 1;
1343   return 0;
1344   }
1345 
pattern_threecolor(cell * c)1346 EX int pattern_threecolor(cell *c) {
1347   #if CAP_ARCM
1348   if(arcm::in()) {
1349     if(PURE)
1350       return arcm::threecolor(c);
1351     else /* if(BITRUNCATED) */
1352       return c->master->rval1;
1353     }
1354   #endif
1355   if(arb::in()) return arb::id_of(c->master) % 3;
1356   if(IRREGULAR || bt::in()) return !pseudohept(c);
1357   #if CAP_GP
1358   if(S3 == 3 && !(S7&1) && gp_threecolor() == 1 && c->master->c7 != c) {
1359     auto li = gp::get_local_info(c);
1360     int rel = gmod(li.relative.first - li.relative.second, 3);
1361     int par = gmod(gp::param.first - gp::param.second, 3);
1362     if(rel == 0)
1363       return pattern_threecolor(c->master->c7);
1364     else if(rel == par)
1365       return pattern_threecolor(c->master->cmove(li.last_dir)->c7);
1366     else
1367       return pattern_threecolor(c->master->cmodmove(li.last_dir+1)->c7);
1368     }
1369   #endif
1370   if(a38) {
1371     // if(GOLDBERG && gp_threecolor() == 2 && gp::pseudohept_val(c) == 0) return 0;
1372     patterns::patterninfo si;
1373     patterns::val38(c, si, !BITRUNCATED ? 0 : patterns::SPF_ROT, patterns::PAT_COLORING);
1374     return si.id >> 2;
1375     }
1376   if(euc::in(2,6) && gp_threecolor() == 2) {
1377     auto li = gp::get_local_info(c);
1378     int rel = gmod(li.relative.first - li.relative.second, 3);
1379     if(rel && (li.last_dir&1)) rel = 3 - rel;
1380     return rel;
1381     }
1382   #if CAP_GP
1383   if(a4 && GOLDBERG) {
1384     patterns::patterninfo si;
1385     auto li = gp::get_local_info(c);
1386     if(S7 & 1) return (li.relative.first&1) + (li.relative.second&1)*2;
1387     patterns::val46(c->master->c7, si, 0, patterns::PAT_COLORING);
1388     int i = si.id;
1389     if(i&2) i ^= 1;
1390     patterns::val46(createStep(c->master, li.last_dir)->c7, si, 0, patterns::PAT_COLORING);
1391     if(si.id&2) si.id ^= 1;
1392     int i2 = i ^ si.id;
1393     int i3 = 3 - i2;
1394     if(gp::param.first % 2 == 0 && gp::param.second % 2 == 0) i = 0;
1395     if(li.relative.first & 1) i ^= i3;
1396     if(li.relative.second & 1) i ^= i2;
1397     return i;
1398     }
1399   #endif
1400   if(a46 && BITRUNCATED) {
1401     patterns::patterninfo si;
1402     patterns::val46(c, si, 0, patterns::PAT_COLORING);
1403     int i = si.id;
1404     return i >> 2;
1405     }
1406   if(euclid) {
1407     if(a4 && PURE) return eupattern4(c);
1408     if(euc::in(2,6) && !BITRUNCATED) return eupattern(c) % 3;
1409     return c == c->master->c7 ? 0 : (c->c.spin(0)&1) ? 1 : 2;
1410     }
1411   if(S3 >= OINF) return c->master->distance % 3;
1412   if(S7 == 4 && S3 == 3) {
1413     int codesN[6] = {0,1,2,1,2,0};
1414     #if CAP_GP
1415     if(gp_threecolor() == 2) {
1416       auto li = gp::get_local_info(c);
1417       int sp = gmod(li.relative.first + 2 * li.relative.second, 3);
1418       if(sp != 0) {
1419         if(li.last_dir & 1)
1420           sp = 3 - sp;
1421         if(among(c->master->fiftyval, 1, 3, 5))
1422           sp = 3 - sp;
1423         }
1424       return sp;
1425       }
1426     #endif
1427     if(PURE)
1428       return codesN[c->master->fiftyval];
1429     if(ctof(c))
1430       return 0;
1431     else for(int i=0; i<3; i++) {
1432       cell *c2 = c->move(i);
1433       if(c2->master->fiftyval == 0)
1434         return 1 + (c->c.spin(i)&1);
1435       if(c2->master->fiftyval == 5)
1436         return 2 - (c->c.spin(i)&1);
1437       }
1438     }
1439   if(stdhyperbolic && PURE) {
1440     int z = zebra40(c);
1441     if(z == 5 || z == 8 || z == 15) return 0;
1442     if(c->land == laSnakeNest) {
1443       if(z == 10 || z == 12 || z == 7) return 2;
1444       if(z == 6 || z == 9) return 3;
1445       if(z == 14 || z == 11) return 4;
1446       }
1447     return 1;
1448     }
1449   if(a46 && !BITRUNCATED) {
1450     patterns::patterninfo si;
1451     patterns::val46(c, si, 0, patterns::PAT_COLORING);
1452     return si.id;
1453     }
1454   if(S7 == 5 && PURE && S3 == 3) {
1455     const int codes[12] = {1, 2, 0, 3, 2, 0, 0, 1, 3, 1, 2, 3};
1456     return codes[c->master->fiftyval];
1457     }
1458   if(S7 == 3 && PURE)
1459     return c->master->fiftyval;
1460   #if CAP_GP
1461   if(gp_threecolor() && (S7&1))
1462     return gp::pseudohept_val(c) > 0;
1463   #endif
1464   return !ishept(c);
1465   }
1466 
1467 // returns ishept in the normal tiling;
1468 // in the 'pure heptagonal' tiling, returns true for a set of cells
1469 // which roughly corresponds to the heptagons in the normal tiling
pseudohept(cell * c)1470 EX bool pseudohept(cell *c) {
1471   #if CAP_IRR
1472   if(IRREGULAR) return irr::pseudohept(c);
1473   #endif
1474   if(hybri) { auto d = hybrid::get_where(c); return ((!prod) || (d.second & 1)) && PIU(pseudohept(d.first)); }
1475   #if CAP_BT
1476   if(nil) return c->master->zebraval & c->master->emeraldval & c->master->fieldval & 1;
1477   if(sol) return (c->master->emeraldval % 3 == 2) && (c->master->zebraval % 3 == 2) && (c->master->distance % 2);
1478   if(nih) return c->master->zebraval % 3 == 2 && c->master->emeraldval % 2 == 1 && (c->master->distance % 2);
1479   if(kite::in()) return kite::getshape(c->master) == kite::pDart;
1480   if(bt::in()) return bt::pseudohept(c);
1481   #endif
1482   if(S3 >= OINF) return c->master->distance % 3 == 1;
1483   #if MAXMDIM == 4
1484   if(WDIM == 3) {
1485     if(geometry == gField435) return false;
1486     else if(euc::in()) return euc::pseudohept(c);
1487     else return reg3::pseudohept(c);
1488     }
1489   #endif
1490   #if CAP_ARCM
1491   if(arcm::in()) return arcm::pseudohept(c);
1492   #endif
1493   if(arb::in()) return arb::pseudohept(c);
1494   #if CAP_GP
1495   if(INVERSE) return false;
1496   if(GOLDBERG && gp_threecolor() == 2)
1497     return gp::pseudohept_val(c) == 0;
1498   if(GOLDBERG && gp_threecolor() == 1 && (S7&1) && (S3 == 3))
1499     return gp::pseudohept_val(c) == 0;
1500   #endif
1501   return pattern_threecolor(c) == 0;
1502   }
1503 
1504 // while Krakens movement is usually restricted to non-pseudohept cells,
1505 // there is one special case when this does not work (because non-pseudohept cells have varying degrees)
kraken_pseudohept(cell * c)1506 EX bool kraken_pseudohept(cell *c) {
1507   if(0);
1508   #if CAP_GP
1509   else if(!euclid && S3 == 4 && GOLDBERG && (gp::param.first % 2 || gp::param.second % 2 || S7 % 2))
1510     return ishept(c);
1511   #endif
1512   #if CAP_IRR
1513   else if(IRREGULAR)
1514     return c->type != 6;
1515   #endif
1516   #if CAP_ARCM
1517   else if(arcm::in() && PURE)
1518     return c->type != isize(arcm::current.triangles[0]);
1519   else if(arcm::in() && BITRUNCATED)
1520     return pseudohept(c);
1521   else if(arcm::in() && DUAL)
1522     return false;
1523   #endif
1524   else if(!euclid && S3 == 3 && !(S7&1) && gp_threecolor() == 1)
1525     return ishept(c);
1526   else
1527     return pseudohept(c);
1528   }
1529 
warptype(cell * c)1530 EX bool warptype(cell *c) {
1531   if(geosupport_chessboard())
1532     return chessvalue(c);
1533   else if(NONSTDVAR)
1534     return pseudohept(c);
1535   else
1536     return pattern_threecolor(c) == 0;
1537   }
1538 
1539 EX map<char, colortable> colortables = {
1540   {'A', {
1541     0xF04040, 0x40F040, 0x4040F0,
1542     0xD0D000, 0xD000D0, 0x00D0D0,
1543     0xC0C0C0, 0x404040, 0x808080,
1544     0xF08040, 0xF04080, 0x40F080,
1545     0x4080F0, 0x8040F0, 0x80F040,
1546     0xFFD500 }},
1547   {'B', {
1548     // trying to get colors as in Wikipedia [ https://en.wikipedia.org/wiki/Euclidean_tilings_by_convex_regular_polygons#k-uniform_tilings ]
1549     0, 0, 0xFFFFFF, 0xFFFF00,
1550     0xFF0000, 0xC000C0 /* unknown5 */, 0x00FF00, 0x00C0C0 /* unknown7 */, 0xFF8000,
1551     0xFFFF80, 0xC040C0, 0xFFD500, 0x000080,
1552     0x404040, 0x606060, 0x808080
1553     }},
1554   {'a', {0x800000, 0x503000, 0x206000, 0x007010, 0x004040, 0x001070, 0x200060, 0x500030}},
1555   {'e', {0x404040, 0x1800000, 0x1008000, 0x000080 }},
1556   {'b', {0x404040, 0x1800000, 0x1008000, 0x000080 }},
1557   {'z', {0x1C0C0C0, 0x1E0E0E0, 0x404040, 0x606060 }},
1558   {'x', {0xC0C0C0, 0x1800000, 0x1008000, 0x000080 }},
1559   {'t', {0x804040, 0x1408040, 0x404080, 0x1808040 }},
1560   {'c', {0x202020, 0x1C0C0C0}},
1561   {'F', {0x1C0C0C0, 0x202020}},
1562   {'w', {0x303030, 0x1C0C0C0}},
1563   {'v', {0xC00000, 0xC08000, 0xC0C000, 0x00C000, 0xC0C0, 0x00C0, 0xC000C0}},
1564   {'j', {0x100FFFF, 0x100FF00, 0x1FFFF00, 0x1FF8000, 0x1FF0000, 0x1FF00FF}},
1565   {'!', {0x1202020, 0x1000080, 0x1008000, 0x1008080, 0x1800000, 0x1800080, 0x1808000, 0x1C0C0C0,
1566          0x1808080, 0x10000FF, 0x100FF00, 0x100FFFF, 0x1FF0000, 0x1FF00FF, 0x1FFFF00, 0x1FFFFFF}},
1567   };
1568 
random_landscape(cell * c,int mul,int div,int step,color_t base)1569 color_t random_landscape(cell *c, int mul, int div, int step, color_t base) {
1570   int col[4];
1571   for(int j=0; j<4; j++) {
1572     col[j] = getCdata(c, j);
1573     col[j] *= mul;
1574     col[j] %= 240;
1575     if(col[j] > 120) col[j] = 240 - col[j];
1576     if(col[j] < -120) col[j] = -240 - col[j];
1577     }
1578   col[0] /= div;
1579   col[1] /= div;
1580   col[2] /= div;
1581   if(ISWEB) for(int a=0; a<3; a++) col[a] = (col[a] + step/2) / step * step;
1582   color_t res = base + col[0] + (col[1] << 8) + (col[2] << 16);
1583   if(WDIM == 3 && (getBits(c) & 1)) res |= 0x1000000;
1584   return res;
1585   }
1586 
1587 EX namespace patterns {
1588   EX int canvasback = linf[laCanvas].color >> 2;
1589   EX int subcanvas;
1590   EX bool displaycodes;
1591   EX char whichShape = 0;
1592   EX char whichCanvas = 0;
1593   EX bool innerwalls;
1594 
sevenval(cell * c)1595   int sevenval(cell *c) {
1596     if(!euclid) return 0;
1597     auto p = euc2_coordinates(c);
1598     return gmod(p.first - p.second * 2, 7);
1599     }
1600 
1601   EX string color_formula = "to01(rgb(x,y,z))";
1602 
1603   map<cell*, color_t> computed_nearer_map;
1604 
nearer_map(cell * c)1605   color_t nearer_map(cell *c) {
1606     if(computed_nearer_map.count(c)) return computed_nearer_map[c];
1607     if(!bounded) return 0;
1608 
1609     cell *sc = currentmap->gamestart();
1610     auto ac = currentmap->allcells();
1611     vector<int> bydist(100, 0);
1612     vector<int> bynei(FULL_EDGE+1, 0);
1613     int maxd = 0;
1614     for(cell *d: ac) {
1615       int di = celldistance(sc, d);
1616       if(di<100) bydist[di]++;
1617       maxd = max(maxd, di);
1618       }
1619 
1620     auto& cnm = computed_nearer_map;
1621 
1622     for(cell *d: ac) cnm[d] = 0x101010;
1623 
1624     for(cell *d: ac) if(celldistance(sc, d) == maxd) {
1625       for(cell *e: ac) if(celldistance(sc, e) > celldistance(d, e)) {
1626         cnm[e] = 0x1FF4040;
1627         int nei = 0;
1628         forCellEx(f, e) if(celldistance(sc, f) > celldistance(d, f)) nei++;
1629         bynei[nei]++;
1630         if(nei == 6) cnm[e] = 0x1FFFF80;
1631         if(nei == S7) cnm[e] = 0x404040;
1632         }
1633       if(0) for(cell *e: ac) if(celldistance(sc, e) == celldistance(d, e))
1634         cnm[e] = 0x140FF40;
1635       break;
1636       }
1637 
1638     println(hlog, "bydist = ", bydist, " bynei = ", bynei);
1639 
1640     return cnm[c];
1641     }
1642 
1643   map<cell*, color_t> computed_furthest_map;
1644 
furthest_map(cell * c,int reduce)1645   color_t furthest_map(cell *c, int reduce) {
1646     auto& cfm = computed_furthest_map;
1647     if(cfm.count(c)) return cfm[c];
1648     if(!bounded) return 0;
1649 
1650     cell *sc = currentmap->gamestart();
1651     auto ac = currentmap->allcells();
1652     vector<int> bydist(100, 0);
1653     vector<int> bynei(FULL_EDGE+1, 0);
1654     int maxd = 0;
1655     for(cell *d: ac) {
1656       int di = celldistance(sc, d);
1657       if(di<100) bydist[di]++;
1658       maxd = max(maxd, di);
1659       }
1660 
1661     for(cell *d: ac) cfm[d] = 0x101010;
1662 
1663     for(cell *d: ac) if(celldistance(sc, d) == maxd - reduce)
1664       cfm[d] = 0x1FFFF80;
1665 
1666     println(hlog, "bydist = ", bydist);
1667 
1668     return cfm[c];
1669     }
1670 
1671 
compute_map_function(cell * c,int p,const string & formula)1672   cld compute_map_function(cell *c, int p, const string& formula) {
1673     exp_parser ep;
1674     ep.extra_params["p"] = p;
1675 
1676     hyperpoint h = calc_relative_matrix(c, currentmap->gamestart(), C0) * C0;
1677     ep.extra_params["x"] = h[0];
1678     ep.extra_params["y"] = h[1];
1679     ep.extra_params["z"] = h[2];
1680     #if MAXMDIM >= 4
1681     ep.extra_params["w"] = h[3];
1682     #endif
1683     ep.extra_params["z40"] = zebra40(c);
1684     ep.extra_params["z3"] = zebra3(c);
1685     ep.extra_params["ev"] = emeraldval(c);
1686     ep.extra_params["fv50"] = fiftyval(c);
1687     ep.extra_params["pa"] = polara50(c);
1688     ep.extra_params["pb"] = polarb50(c);
1689     ep.extra_params["pd"] = cdist50(c);
1690     ep.extra_params["fu"] = fieldpattern::fieldval_uniq(c);
1691     ep.extra_params["threecolor"] = pattern_threecolor(c);
1692     ep.extra_params["chess"] = chessvalue(c);
1693     ep.extra_params["ph"] = pseudohept(c);
1694     ep.extra_params["kph"] = kraken_pseudohept(c);
1695     if(true) {
1696       ep.extra_params["md"] = c->master->distance;
1697       ep.extra_params["me"] = c->master->emeraldval;
1698       ep.extra_params["mf"] = c->master->fieldval;
1699       ep.extra_params["mz"] = c->master->zebraval;
1700       }
1701 
1702     if(sphere) {
1703       ep.extra_params["h0"] = getHemisphere(c, 0);
1704       ep.extra_params["h1"] = getHemisphere(c, 1);
1705       ep.extra_params["h2"] = getHemisphere(c, 2);
1706       }
1707     if(euclid) {
1708       auto co = euc2_coordinates(c);
1709       int x = co.first, y = co.second;
1710       ep.extra_params["ex"] = x;
1711       ep.extra_params["ey"] = y;
1712       if(S7 == 6) ep.extra_params["ez"] = -x-y;
1713       }
1714     #if CAP_CRYSTAL
1715     if(cryst) {
1716       crystal::ldcoord co = crystal::get_ldcoord(c);
1717       for(int i=0; i<crystal::MAXDIM; i++)
1718         ep.extra_params["x"+its(i)] = co[i];
1719       }
1720     #endif
1721     #if CAP_SOLV
1722     if(asonov::in()) {
1723       auto co = asonov::get_coord(c->master);
1724       ep.extra_params["ax"] = szgmod(co[0], asonov::period_xy);
1725       ep.extra_params["ay"] = szgmod(co[1], asonov::period_xy);
1726       ep.extra_params["az"] = szgmod(co[2], asonov::period_z);
1727       }
1728     #endif
1729     if(nil) {
1730       auto co = nilv::get_coord(c->master);
1731       ep.extra_params["nx"] = szgmod(co[0], nilv::nilperiod[0]);
1732       ep.extra_params["ny"] = szgmod(co[1], nilv::nilperiod[1]);
1733       ep.extra_params["nz"] = szgmod(co[2], nilv::nilperiod[2]);
1734       }
1735     if(hybri)
1736       ep.extra_params["level"] = hybrid::get_where(c).second;
1737 
1738     if(geometry_supports_cdata()) {
1739       ep.extra_params["d0"] = getCdata(c, 0);
1740       ep.extra_params["d1"] = getCdata(c, 1);
1741       ep.extra_params["d2"] = getCdata(c, 2);
1742       ep.extra_params["d3"] = getCdata(c, 3);
1743       }
1744 
1745     ep.s = formula;
1746     try {
1747       return ep.parse();
1748       }
1749     catch(hr_parse_exception&) {
1750       return 0;
1751       }
1752     }
1753 
1754   EX hookset<int(cell*)> hooks_generate_canvas;
1755 
1756   EX int jhole = 0;
1757   EX int jblock = 0;
1758   EX int rwalls = 50;
1759 
edit_rwalls()1760   EX void edit_rwalls() {
1761     if(WDIM == 2) return;
1762     dialog::editNumber(rwalls, 0, 100, 10, 50, XLAT("probability of a wall (%)"), "");
1763     dialog::reaction = [] { stop_game(); start_game(); };
1764     }
1765 
generateCanvas(cell * c)1766   EX int generateCanvas(cell *c) {
1767 
1768     int i = callhandlers(-1, hooks_generate_canvas, c);
1769     if(i != -1) return i;
1770     switch(whichCanvas) {
1771       #if CAP_CRYSTAL
1772       case 'K': case '#': case '=': case 'O': case '/': case '@':
1773         if(nil)
1774           return nilv::colorize(c, whichCanvas);
1775         else
1776           return crystal::colorize(c, whichCanvas);
1777       #endif
1778       case 'A':
1779         #if CAP_ARCM
1780         if(arcm::in()) return colortables['A'][arcm::current.tilegroup[arcm::id_of(c->master)]];
1781         #endif
1782         if(arb::in()) return colortables['A'][shvid(c) + c->master->emeraldval * isize(arb::current.shapes)];
1783         return colortables['A'][shvid(c)];
1784       case 'B':
1785         return colortables['B'][c->type & 15];
1786       #if CAP_FIELD
1787       case 'C': {
1788         if(!hyperbolic) return canvasback;
1789         using namespace fieldpattern;
1790         int z = currfp.getdist(fieldval(c), make_pair(0,false));
1791         if(z < currfp.circrad) return 0x00C000;
1792         int z2 = currfp.getdist(fieldval(c), make_pair(currfp.otherpole,false));
1793         if(z2 < currfp.disthep[currfp.otherpole] - currfp.circrad)
1794           return 0x3000;
1795         return 0x6000;
1796         }
1797       case 'D': {
1798         if(!hyperbolic) return canvasback;
1799         using namespace fieldpattern;
1800         int z = currfp.getdist(fieldval(c), make_pair(0,false));
1801         return 255 * (currfp.maxdist+1-z) / currfp.maxdist;
1802         }
1803       case 'N': {
1804         if(!hyperbolic) return canvasback;
1805         using namespace fieldpattern;
1806         int z = currfp.getdist(fieldval(c), make_pair(0,false));
1807         int z2 = currfp.getdist(fieldval(c), make_pair(currfp.otherpole,false));
1808         if(z < z2) return 0x00C000;
1809         if(z > z2) return 0xC00000;
1810         return 0xCCCC00;
1811         }
1812       #endif
1813       case 'M': {
1814         int d = celldist(c);
1815         color_t res = gradient(0, canvasback, 0, min(1.8/(1+d), 1.), 1);
1816         if(d > 3) res |= 0x1000000;
1817         return res;
1818         }
1819       case 'P': {
1820         cell *s = currentmap->gamestart()->move(0);
1821         if(exhaustive_distance_appropriate() && !keep_distances_from.count(s))
1822           permanent_long_distances(s);
1823         int d = celldistance(s, c);
1824         color_t res = distcolors[d];
1825         if(d > 3) res |= 0x1000000;
1826         return res;
1827         }
1828       #if CAP_FIELD
1829       case 'S':
1830         if(!hyperbolic) return canvasback;
1831         return 0x3F1F0F * fieldpattern::subval(c).second + 0x000080;
1832       #endif
1833       case 'g': {
1834         color_t r = canvasback;
1835         if(hrand(100) < rwalls) r |= 0x1000000;
1836         if(c == cwt.at) r &= 0xFFFFFF;
1837         return r;
1838         }
1839       case 'r': {
1840         color_t r = hrand(0xFFFFFF + 1);
1841         if(hrand(100) < rwalls) r |= 0x1000000;
1842         if(c == cwt.at && rwalls <= 100) r &= 0xFFFFFF;
1843         return r;
1844         }
1845       case 'I': {
1846         color_t r = 0xFFD500;
1847         if(c->type != (variation == eVariation::dual_subcubes ? 6 : 14)) r |= 0x1000000;
1848         return r;
1849         }
1850       case '^': {
1851         int x = c->master->fieldval & 4095;
1852         int y = (c->master->fieldval >> 12) & 4095;
1853         ignore(x);
1854         if(c->master->distance % 3) return 0;
1855         if(c->c.spin(bt::updir()) != 1) return 0;
1856         // if(c->master->distance % 2 == 0) return 0;
1857         if(hrand(100) == 0) return 0;
1858         return 0x1000000 | (0xFFFFFF & (0x671349 + y * 0x512369));
1859         if(hrand(100) >= 1) return 0;
1860         return 0x1000000 | hrand(0x1000000);
1861 //        if(c->master->distance == 1) return 0x1FF0000;
1862 //        if(c->master->distance == -1) return 0x100FF00;
1863 //        return 0;
1864         }
1865       case '!': {
1866         if(c == currentmap->gamestart()) return 0;
1867         for(int a=0; a<S7; a++) if(c == currentmap->gamestart()->move(a))
1868           {
1869           if(among(a,4,5,10,11)) return 0;
1870           return colortables['!'][a];
1871           }
1872         return hrand(0x1000000) | 0x1000000;
1873         }
1874 
1875       case 'e':
1876         return colortables['e'][emeraldval(c)];
1877       case 'a': {
1878         color_t col = colortables['a'][land50(c)];
1879         if(polara50(c)) col += 0x181818;
1880         return col;
1881         }
1882       case 'b':
1883         return colortables['b'][polara50(c) + 2 * polarb50(c)];
1884       case 'z':
1885         return colortables['z'][zebra40(c)];
1886       case 't': {
1887         int fv = zebra40(c);
1888         if(fv/4 == 4 || fv/4 == 6 || fv/4 == 5 || fv/4 == 10) fv ^= 2;
1889         return colortables['t'][fv];
1890         }
1891       case 'x':
1892         return colortables['x'][zebra3(c)];
1893       case 'w':
1894         return colortables['w'][randpattern(c, subcanvas) ? 1 : 0];
1895       case 'H':
1896         return colortables['c'][c->master->c7 == c ? 0 : 1];
1897       case 'l':
1898         return random_landscape(c, 3, 1, 17, 0x808080);
1899       case 'd':
1900         return random_landscape(c, 6, 8, 2, 0x101010);
1901       case 'h':
1902         return random_landscape(c, 6, 4, 4, 0x202020);
1903       case 'c':
1904         return colortables['c'][chessvalue(c)];
1905       case 'F':
1906         return colortables['F'][pseudohept(c)];
1907       case 'T':
1908         return nestcolors[pattern_threecolor(c)];
1909       case 'v':
1910         return colortables['v'][sevenval(c)];
1911       case 'j': {
1912         if(c == currentmap->gamestart()) return canvasback;
1913         int d = c->master->distance;
1914         if(geometry == gNil) d = c->master->zebraval;
1915         if(euc::in()) d = euc::get_ispacemap()[c->master][0];
1916         if(d % 2 == 0 || d < -5 || d > 5) return hrand(100) < jblock ? 0xFFFFFFFF : canvasback;
1917         return hrand(100) < jhole ? canvasback : colortables['j'][(d+5)/2];
1918         }
1919       case 'J': {
1920         if(c == currentmap->gamestart()) return canvasback;
1921         int d = c->master->distance;
1922         if(geometry == gNil) d = c->master->zebraval;
1923         if((d&3) != 2) return hrand(100) < jblock ? 0xFFFFFFFF : canvasback;
1924         return hrand(100) < jhole ? canvasback : colortables['j'][(d+10)/4];
1925         }
1926       case 'G': {
1927         color_t r = hrand(0xFFFFFF + 1);
1928         if(hrand(100) < rwalls && pseudohept(c) && c != cwt.at) r |= 0x1000000;
1929         return r;
1930         }
1931       case 'f': {
1932         color_t res;
1933         for(int i=0; i<4; i++) {
1934           ld v = real(compute_map_function(c, 1+i, color_formula));
1935           if(i == 3) part(res, i) = (v > 0);
1936           else if(v < 0) part(res, i) = 0;
1937           else if(v > 1) part(res, i) = 255;
1938           else part(res, i) = int(v * 255 + .5);
1939           }
1940         return res;
1941         }
1942       case 'k': {
1943         /* just keep the old color */
1944         return c->landparam;
1945         }
1946       case 'Z': {
1947         return nearer_map(c);
1948         }
1949       case 'Y': {
1950         return furthest_map(c, 0);
1951         }
1952       case 'X': {
1953         return furthest_map(c, 1);
1954         }
1955       case 'W': {
1956         return furthest_map(c, 2);
1957         }
1958       }
1959     return canvasback;
1960     }
1961 
showPrePatternP(bool instant)1962   void showPrePatternP(bool instant) {
1963     cmode = sm::SIDE | sm::MAYDARK;
1964     gamescreen(0);
1965 
1966     dialog::init("predesigned patterns");
1967     dialog::addItem(WDIM == 3 ? XLAT("empty") : XLAT("single color"), 'g');
1968     dialog::addItem(XLAT("random colors"), 'r');
1969 
1970     if(WDIM == 2) dialog::addItem(XLAT("distance from origin"), 'M');
1971     dialog::addSelItem(XLAT("rainbow by distance"), "binary/Solv", 'j');
1972 
1973     if(geometry_supports_cdata()) {
1974       dialog::addItem(XLAT("rainbow landscape"), 'l');
1975       dialog::addItem(XLAT("dark rainbow landscape"), 'd');
1976       }
1977 
1978     dialog::addItem(XLAT("football"), 'F');
1979     if(geosupport_chessboard())
1980       dialog::addItem(XLAT("chessboard"), 'c');
1981 
1982     dialog::addItem(XLAT("nice coloring"), 'T');
1983 
1984     if(euc::in(2,6))
1985       dialog::addItem(XLAT("seven-coloring"), 'v');
1986 
1987     if(stdhyperbolic) {
1988       dialog::addSelItem(XLAT("emerald pattern"), "emerald", 'e');
1989       dialog::addSelItem(XLAT("four elements"), "palace", 'b');
1990       dialog::addSelItem(XLAT("eight domains"), "palace", 'a');
1991       dialog::addSelItem(XLAT("zebra pattern"), "zebra", 'z');
1992       dialog::addSelItem(XLAT("four triangles"), "zebra", 't');
1993       dialog::addSelItem(XLAT("three stripes"), "zebra", 'x');
1994       }
1995 
1996     if(a4)
1997       dialog::addSelItem(XLAT("zebra pattern"), "coloring", 'z');
1998 
1999     dialog::addSelItem(XLAT("random black-and-white"), "current", 'w');
2000 
2001     #if CAP_FIELD
2002     if(!sphere) {
2003       dialog::addSelItem(XLAT("field pattern C"), "field", 'C');
2004       dialog::addSelItem(XLAT("field pattern D"), "field", 'D');
2005       dialog::addSelItem(XLAT("field pattern N"), "field", 'N');
2006       dialog::addSelItem(XLAT("field pattern S"), "field", 'S');
2007       }
2008     #endif
2009 
2010     if(arcm::in())
2011       dialog::addSelItem(XLAT("Archimedean"), "Archimedean", 'A');
2012 
2013     if(cryst)
2014       dialog::addSelItem(XLAT("Crystal coordinates"), "Crystal", 'K');
2015 
2016     if(cryst || geometry == gSpace344) {
2017       dialog::addSelItem(XLAT("Cage"), "Crystal", '#');
2018       dialog::addSelItem(XLAT("Hyperplanes"), "Crystal", '=');
2019       dialog::addSelItem(XLAT("Honeycomb"), "Crystal", 'O');
2020       dialog::addSelItem(XLAT("Diagonal"), "Crystal", '/');
2021       }
2022     if(nil) {
2023       dialog::addSelItem(XLAT("Penrose staircase"), "Nil", '/');
2024       }
2025 
2026     if(bounded) {
2027       dialog::addSelItem(XLAT("nearer end"), "bounded", 'Z');
2028       dialog::addSelItem(XLAT("furthest from start"), "bounded", 'Y');
2029       }
2030 
2031     dialog::addSelItem(XLAT("types"), "types", 'A');
2032     dialog::addSelItem(XLAT("sides"), "sides", 'B');
2033 
2034     if(!ISMOBILE)
2035       dialog::addSelItem(XLAT("formula"), "formula", 'f');
2036 
2037     dialog::addBreak(100);
2038 
2039     dialog::addBoolItem_action(XLAT("display the inner walls"), innerwalls, '5');
2040 
2041     if(geosupport_threecolor() == 2) {
2042       dialog::addBoolItem(XLAT("display only hexagons"), (whichShape == '6'), '6');
2043       dialog::addBoolItem(XLAT("display only heptagons"), (whichShape == '7'), '7');
2044       dialog::addBoolItem(XLAT("display the triheptagonal grid"), (whichShape == '8'), '8');
2045       }
2046 
2047     if(geosupport_chessboard()) {
2048       dialog::addBoolItem(XLAT("display only chessboard white"), (whichShape == '6'), '6');
2049       dialog::addBoolItem(XLAT("display only chessboard black"), (whichShape == '7'), '7');
2050       }
2051 
2052     dialog::addBoolItem(XLAT("display full floors"), (whichShape == '9'), '9');
2053     dialog::addSelItem(XLAT("floor type"), XLATN(winf[canvas_default_wall].name), 'i');
2054 
2055     dialog::addItem(XLAT("line patterns"), 'L');
2056 
2057     dialog::addBack();
2058     dialog::display();
2059 
2060     keyhandler = [instant] (int sym, int uni) {
2061       dialog::handleNavigation(sym, uni);
2062       if(uni == 'g') {
2063         static unsigned c = (canvasback << 8) | 0xFF;
2064         static unsigned canvasbacks[] = {
2065           6, 0xFFFFFFFF, 0x101010FF, 0x404040FF, 0x808080FF, 0x800000FF, unsigned(linf[laCanvas].color >> 2) << 8
2066           };
2067         dialog::openColorDialog(c, canvasbacks);
2068         dialog::reaction = [instant] () {
2069           if(instant) {
2070             stop_game();
2071             whichCanvas = 'g';
2072             canvasback = c >> 8;
2073             enable_canvas();
2074             start_game();
2075             }
2076           else {
2077             whichCanvas = 'g';
2078             canvasback = c >> 8;
2079             }
2080           };
2081         dialog::reaction_final = edit_rwalls;
2082         }
2083       else if(uni == 'i') {
2084         if(instant)
2085           stop_game();
2086 
2087         vector<eWall> choices = {waNone, waInvisibleFloor, waChasm, waEternalFire, waStone, waSea, waBarrier, waCavewall};
2088         for(int i=0; i<isize(choices); i++)
2089           if(canvas_default_wall == choices[i]) {
2090             canvas_default_wall = choices[(i+1) % isize(choices)];
2091             break;
2092             }
2093         if(instant) {
2094           enable_canvas();
2095           start_game();
2096           }
2097         }
2098 
2099       else if(uni == '6' || uni == '7' || uni == '8' || uni == '9') {
2100         if(whichShape == uni) whichShape = 0;
2101         else whichShape = uni;
2102         }
2103       else if(uni == 'L')
2104         pushScreen(linepatterns::showMenu);
2105 
2106       else if(uni == 'f') {
2107         string s = XLAT(
2108           "This lets you specify the color pattern as a function of the cell. "
2109           "Available parameters:\n\n"
2110           "x, y, z (hyperboloid/sphere/plane coordinates in non-crystal geometries)\n"
2111           "ex, ey, ez (in Euclidean geometries)\n"
2112           "x0, x1, x2... (crystal geometry only)\n"
2113           "0 is black, 1 is white, rgb(1,0,0) is red, ifp(p-2,1,0) is blue (p=1 for red, 2 for green, 3 for blue).");
2114 
2115         if(MDIM == 4) s += XLAT(
2116           "w (fourth coordinate)\n"
2117           "wallif(condition, color)\n"
2118           );
2119 
2120         s += XLAT("see compute_map_function in pattern2.cpp for more\n");
2121 
2122         dialog::edit_string(color_formula, "formula", s);
2123 
2124         dialog::extra_options = dialog::parser_help;
2125         dialog::reaction_final = [instant] () {
2126           if(instant) stop_game();
2127           whichCanvas = 'f';
2128           if(instant) {
2129             enable_canvas();
2130             start_game();
2131             }
2132           };
2133         }
2134 
2135       else if((uni >= 'a' && uni <= 'z') || (uni >= 'A' && uni <= 'Z') || among(uni, '#', '=', '/')) {
2136         if(instant)
2137           stop_game();
2138         whichCanvas = uni;
2139         subcanvas = rand();
2140         if(instant) {
2141           enable_canvas();
2142           start_game();
2143           }
2144         if(uni == 'r')
2145           edit_rwalls();
2146         if(uni == 'x' || uni == 'z' || uni == 't')
2147           whichPattern = PAT_ZEBRA, subpattern_flags = SPF_SYM0123 | SPF_ROT;
2148         if(uni == 'e')
2149           whichPattern = PAT_EMERALD, subpattern_flags = SPF_SYM0123 | SPF_ROT;
2150         if(uni == 'b')
2151           whichPattern = PAT_PALACE, subpattern_flags = SPF_SYM0123 | SPF_ROT;
2152         if(uni == 'z' && a46)
2153           whichPattern = PAT_COLORING, subpattern_flags = SPF_CHANGEROT | SPF_SYM0123;
2154         }
2155       else if(doexiton(sym, uni)) popScreen();
2156       };
2157     }
2158 
showPrePattern()2159   EX void showPrePattern() { showPrePatternP(true); }
showPrePatternNoninstant()2160   EX void showPrePatternNoninstant() { showPrePatternP(false); }
2161 
2162 
2163 #if CAP_TEXTURE
2164 #define REMAP_TEXTURE texture::config.remap()
2165 #else
2166 #define REMAP_TEXTURE (void)0
2167 #endif
2168 
showPattern()2169   EX void showPattern() {
2170     cmode = sm::SIDE | sm::MAYDARK;
2171     {
2172     dynamicval<bool> dc(displaycodes, whichPattern);
2173     gamescreen(0);
2174     }
2175     dialog::init();
2176 
2177     dialog::addBoolItem(XLAT("cell types"), (whichPattern == PAT_TYPES), PAT_TYPES);
2178 
2179     if(stdhyperbolic || a4)
2180       dialog::addBoolItem(XLAT("Zebra Pattern"), (whichPattern == PAT_ZEBRA), PAT_ZEBRA);
2181 
2182     if(stdhyperbolic)
2183       dialog::addBoolItem(XLAT("Emerald Pattern"), (whichPattern == PAT_EMERALD), PAT_EMERALD);
2184     else if(a38)
2185       dialog::addBoolItem(XLAT("broken Emerald Pattern"), (whichPattern == PAT_EMERALD), PAT_EMERALD);
2186 
2187     if(stdhyperbolic || euclid)
2188       dialog::addBoolItem(XLAT("Palace Pattern"), (whichPattern == PAT_PALACE), PAT_PALACE);
2189 
2190     if(geosupport_chessboard())
2191       dialog::addBoolItem(XLAT("chessboard"), (whichPattern == PAT_CHESS), PAT_CHESS);
2192 
2193     if(geosupport_threecolor() == 2)
2194       dialog::addBoolItem(XLAT("coloring"), (whichPattern == PAT_COLORING), PAT_COLORING);
2195 
2196     if(sphere_narcm)
2197       dialog::addBoolItem(XLAT("siblings"), (whichPattern == PAT_SIBLING), PAT_SIBLING);
2198 
2199     if(euclid)
2200       dialog::addBoolItem(XLAT("torus"), (whichPattern == PAT_FIELD), PAT_FIELD);
2201     else if(sphere)
2202       dialog::addBoolItem(XLAT("single cells"), (whichPattern == PAT_FIELD), PAT_FIELD);
2203     else
2204       dialog::addBoolItem(XLAT("field pattern"), (whichPattern == PAT_FIELD), PAT_FIELD);
2205 
2206     dialog::addBoolItem(XLAT("single type"), (whichPattern == PAT_SINGLETYPE), PAT_SINGLETYPE);
2207 
2208     dialog::addBreak(50);
2209 
2210     if(
2211       (whichPattern == PAT_EMERALD && (stdhyperbolic || a38)) ||
2212       (whichPattern == PAT_PALACE && stdhyperbolic) ||
2213       (whichPattern == PAT_ZEBRA && stdhyperbolic) ||
2214       (whichPattern == PAT_SIBLING && sphere) ||
2215       (whichPattern == PAT_ZEBRA && a457)) {
2216       dialog::addBoolItem(XLAT("rotational symmetry"), subpattern_flags & SPF_ROT, '0');
2217       }
2218 
2219     if((euclid && whichPattern == PAT_COLORING) ||
2220       (a38 && whichPattern == PAT_COLORING) ||
2221       (a4 && !BITRUNCATED && whichPattern == PAT_COLORING && !a46))
2222       dialog::addBoolItem(XLAT("edit all three colors"), subpattern_flags & SPF_ROT, '0');
2223 
2224     if(euclid && whichPattern == PAT_COLORING)
2225       dialog::addBoolItem(XLAT("rotate the color groups"), subpattern_flags & SPF_CHANGEROT, '4');
2226 
2227     if(a46 && whichPattern == PAT_COLORING)
2228       dialog::addBoolItem(XLAT("rotate the color groups"), subpattern_flags & SPF_CHANGEROT, '4');
2229 
2230     if(a46 && whichPattern == PAT_COLORING && BITRUNCATED)
2231       dialog::addBoolItem(XLAT("edit both bitrunc colors"), subpattern_flags & SPF_TWOCOL, '5');
2232 
2233     if(
2234       (whichPattern == PAT_EMERALD && (stdhyperbolic || a38)) ||
2235       (whichPattern == PAT_PALACE && stdhyperbolic) ||
2236       (whichPattern == PAT_ZEBRA && stdhyperbolic) ||
2237       (whichPattern == PAT_COLORING && a46) ||
2238       (whichPattern == PAT_ZEBRA && a457)
2239       ) {
2240       dialog::addBoolItem(XLAT("symmetry 0-1"), subpattern_flags & SPF_SYM01, '1');
2241       dialog::addBoolItem(XLAT("symmetry 0-2"), subpattern_flags & SPF_SYM02, '2');
2242       dialog::addBoolItem(XLAT("symmetry 0-3"), subpattern_flags & SPF_SYM03, '3');
2243       }
2244     if(euclid && among(whichPattern, PAT_COLORING, PAT_TYPES) && !arcm::in())
2245       dialog::addBoolItem(XLAT("extra symmetries"), subpattern_flags & SPF_EXTRASYM, '=');
2246 
2247     #if CAP_ARCM
2248     if(arcm::in() && arcm::current.have_symmetry && whichPattern == PAT_TYPES)
2249       dialog::addBoolItem(XLAT("extra symmetries"), subpattern_flags & SPF_EXTRASYM, '=');
2250     #endif
2251 
2252     if(whichPattern == PAT_SINGLETYPE) {
2253       dialog::addBoolItem(XLAT("odd/even"), subpattern_flags & SPF_TWOCOL, '5');
2254       dialog::addBoolItem(XLAT("extra symmetries"), subpattern_flags & SPF_EXTRASYM, '=');
2255       }
2256 
2257     if(euclid && among(whichPattern, PAT_COLORING, 0))
2258       dialog::addBoolItem(XLAT("full symmetry"), subpattern_flags & SPF_FULLSYM, '!');
2259 
2260     if(a38 && PURE && whichPattern == PAT_TYPES) {
2261       dialog::addBoolItem(XLAT("extra symmetries"), subpattern_flags & SPF_EXTRASYM, '=');
2262       }
2263 
2264     if(a46 && PURE && whichPattern == PAT_COLORING) {
2265       dialog::addBoolItem(XLAT("extra symmetries"), subpattern_flags & SPF_EXTRASYM, '=');
2266       }
2267 
2268     if((whichPattern == PAT_COLORING) || (whichPattern == PAT_TYPES && arcm::in())) {
2269       dialog::addBoolItem(XLAT("alternate coloring"), subpattern_flags & SPF_ALTERNATE, '\'');
2270       dialog::addBoolItem(XLAT("football"), subpattern_flags & SPF_FOOTBALL, '*');
2271       }
2272 
2273     if(a38 && whichPattern == PAT_COLORING)
2274       dialog::addBoolItem(XLAT("Docks pattern"), subpattern_flags & SPF_DOCKS, '@');
2275 
2276     if(whichPattern && (IRREGULAR || GOLDBERG || arcm::in()))
2277       dialog::addBoolItem(XLAT("remove complete classification"), subpattern_flags & SPF_NO_SUBCODES, '#');
2278 
2279     dialog::addBreak(50);
2280 
2281     dialog::addBoolItem(XLAT("display pattern codes (full)"), displaycodes, 'd');
2282 
2283     if(!needConfirmation()) dialog::addItem(XLAT("predesigned patterns"), 'r');
2284     else dialog::addInfo("start a new game to use predesigned patterns");
2285 
2286     dialog::display();
2287 
2288     keyhandler = [] (int sym, int uni) {
2289       dialog::handleNavigation(sym, uni);
2290 
2291       if(among(uni, PAT_EMERALD, PAT_PALACE, PAT_ZEBRA, PAT_DOWN, PAT_FIELD, PAT_COLORING, PAT_SIBLING, PAT_CHESS, PAT_SINGLETYPE, PAT_TYPES)) {
2292         if(whichPattern == uni) whichPattern = PAT_NONE;
2293         else whichPattern = ePattern(uni);
2294         #if CAP_EDIT
2295         mapeditor::modelcell.clear();
2296         #endif
2297         REMAP_TEXTURE;
2298         }
2299 
2300       else if(uni >= '0' && uni <= '5') {
2301         subpattern_flags ^= (1 << (uni - '0'));
2302         REMAP_TEXTURE;
2303         }
2304 
2305       else if(uni == '=') {
2306         subpattern_flags ^= SPF_EXTRASYM;
2307         REMAP_TEXTURE;
2308         }
2309 
2310       else if(uni == '\'') {
2311         subpattern_flags ^= SPF_ALTERNATE;
2312         // subpattern_flags &= ~SPF_FOOTBALL;
2313         REMAP_TEXTURE;
2314         }
2315 
2316       else if(uni == '*') {
2317         subpattern_flags ^= SPF_FOOTBALL;
2318         // subpattern_flags &= ~SPF_ALTERNATE;
2319         REMAP_TEXTURE;
2320         }
2321 
2322       else if(uni == '!') {
2323         subpattern_flags ^= SPF_FULLSYM;
2324         REMAP_TEXTURE;
2325         }
2326 
2327       else if(uni == '@') {
2328         subpattern_flags ^= SPF_DOCKS;
2329         REMAP_TEXTURE;
2330         }
2331 
2332       else if(uni == '#') {
2333         subpattern_flags ^= SPF_NO_SUBCODES;
2334         REMAP_TEXTURE;
2335         }
2336 
2337       else if(uni == 'd') displaycodes = !displaycodes;
2338 
2339       else if(uni == 'r' && !needConfirmation()) pushScreen(showPrePattern);
2340 
2341       else if(doexiton(sym, uni)) popScreen();
2342       };
2343     }
2344 
compatible(cpatterntype oldp,cpatterntype newp)2345   EX bool compatible(cpatterntype oldp, cpatterntype newp) {
2346     // larges are not incompatible between themselves
2347     if(newp == cpLarge || newp == cpZebra)
2348       return false;
2349     // other cps are compatible with themselves
2350     if(newp == oldp) return true;
2351     // Single can be upgraded to everything
2352     if(oldp == cpSingle) return true;
2353     // Football can be upgraded to Three colors
2354     if(oldp == cpFootball) return newp == cpThree;
2355     // incompatible otherwise
2356     return false;
2357     }
2358 
2359   struct changeable_pattern_geometry {
2360     eGeometry geo;
2361     eVariation var;
2362     ePattern whichPattern;
2363     int subpattern_flags;
2364     };
2365 
2366   struct changeable_pattern {
2367     string name;
2368     vector<changeable_pattern_geometry> geometries;
2369     };
2370 
2371   vector<changeable_pattern> cpatterns = {
2372     {"football", {
2373       {gNormal, eVariation::bitruncated, PAT_TYPES, 0},
2374       {gSphere, eVariation::bitruncated, PAT_TYPES, 0},
2375       {gEuclid, eVariation::bitruncated, PAT_TYPES, SPF_EXTRASYM},
2376       {gOctagon, eVariation::bitruncated, PAT_TYPES, 0},
2377       {gOctagon, eVariation::pure, PAT_COLORING, SPF_FOOTBALL | SPF_EXTRASYM},
2378       {g45, eVariation::bitruncated, PAT_TYPES, 0},
2379       {g46, eVariation::bitruncated, PAT_TYPES, SPF_EXTRASYM},
2380       {g47, eVariation::bitruncated, PAT_TYPES, 0},
2381       {gSmallSphere, eVariation::bitruncated, PAT_TYPES, 0},
2382       {gSmallSphere, eVariation::pure, PAT_COLORING, SPF_FOOTBALL | SPF_EXTRASYM},
2383       {gTinySphere, eVariation::bitruncated, PAT_TYPES, SPF_EXTRASYM},
2384       {gEuclidSquare, eVariation::bitruncated, PAT_TYPES, SPF_EXTRASYM},
2385       }},
2386     {"three colors", {
2387       {gEuclid, eVariation::bitruncated, PAT_COLORING, SPF_SYM0123 | SPF_EXTRASYM},
2388       {gSmallSphere, eVariation::bitruncated, PAT_COLORING, 0},
2389       {gSmallSphere, eVariation::bitruncated, PAT_COLORING, SPF_ALTERNATE},
2390       {gSmallSphere, eVariation::pure, PAT_COLORING, 0},
2391       {gOctagon, eVariation::bitruncated, PAT_COLORING, SPF_ROT | SPF_EXTRASYM},
2392       {gOctagon, eVariation::bitruncated, PAT_COLORING, SPF_ROT | SPF_EXTRASYM | SPF_ALTERNATE},
2393       {gOctagon, eVariation::pure, PAT_COLORING, 0},
2394       {gEuclidSquare, eVariation::bitruncated, PAT_COLORING, SPF_SYM03 | SPF_EXTRASYM},
2395       {gEuclidSquare, eVariation::bitruncated, PAT_COLORING, SPF_SYM03 | SPF_EXTRASYM | SPF_ALTERNATE},
2396       {g46, eVariation::bitruncated, PAT_COLORING, SPF_SYM0123},
2397       {g46, eVariation::bitruncated, PAT_COLORING, SPF_SYM0123 | SPF_EXTRASYM | SPF_ALTERNATE}
2398       }},
2399     {"chessboard", {
2400       {gEuclidSquare, eVariation::pure, PAT_CHESS, SPF_EXTRASYM},
2401       {g45, eVariation::pure, PAT_CHESS, 0},
2402       {g46, eVariation::pure, PAT_CHESS, 0},
2403       {g47, eVariation::pure, PAT_CHESS, 0}
2404       }},
2405     {"single type", {
2406       {gNormal, eVariation::pure, PAT_SINGLETYPE, 0},
2407       {gSphere, eVariation::pure, PAT_SINGLETYPE, 0},
2408       {gEuclid, eVariation::bitruncated, PAT_SINGLETYPE, 0},
2409       {gOctagon, eVariation::pure, PAT_SINGLETYPE, 0},
2410       {g45, eVariation::pure, PAT_SINGLETYPE, 0},
2411       {g46, eVariation::pure, PAT_SINGLETYPE, 0},
2412       {g47, eVariation::pure, PAT_SINGLETYPE, 0},
2413       {gSmallSphere, eVariation::pure, PAT_SINGLETYPE, 0},
2414       {gTinySphere, eVariation::pure, PAT_SINGLETYPE, 0},
2415       {gEuclidSquare, eVariation::pure, PAT_SINGLETYPE, 0},
2416       }},
2417     {"single type+symmetry", {
2418       {gNormal, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2419       {gSphere, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2420       {gEuclid, eVariation::bitruncated, PAT_SINGLETYPE, SPF_EXTRASYM},
2421       {gOctagon, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2422       {g45, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2423       {g46, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2424       {g47, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2425       {gSmallSphere, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2426       {gTinySphere, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2427       {gEuclidSquare, eVariation::pure, PAT_SINGLETYPE, SPF_EXTRASYM},
2428       }},
2429     {"odd/even", {
2430       {gNormal, eVariation::bitruncated, PAT_SINGLETYPE, SPF_TWOCOL},
2431       {gSphere, eVariation::bitruncated, PAT_SINGLETYPE, SPF_TWOCOL},
2432       {g45, eVariation::pure, PAT_SINGLETYPE, SPF_TWOCOL},
2433       {g47, eVariation::pure, PAT_SINGLETYPE, SPF_TWOCOL}
2434       }},
2435     {"large picture", {
2436       {gNormal, eVariation::bitruncated, PAT_PALACE, SPF_SYM0123},
2437       {gNormal, eVariation::pure, PAT_PALACE, SPF_SYM0123},
2438       {gSphere, eVariation::bitruncated, PAT_FIELD, 0},
2439       {gSphere, eVariation::pure, PAT_FIELD, 0},
2440       {gElliptic, eVariation::bitruncated, PAT_FIELD, 0},
2441       {gElliptic, eVariation::pure, PAT_FIELD, 0},
2442       {gEuclid, eVariation::bitruncated, PAT_PALACE, 0}
2443       }},
2444     {"periodic patterns", {
2445       {gNormal, eVariation::bitruncated, PAT_ZEBRA, SPF_SYM0123 | SPF_ROT},
2446       {gNormal, eVariation::bitruncated, PAT_PALACE, SPF_SYM0123 | SPF_ROT},
2447       {gNormal, eVariation::bitruncated, PAT_EMERALD, SPF_SYM0123 | SPF_ROT},
2448       {g46, eVariation::pure, PAT_COLORING, SPF_SYM0123 | SPF_CHANGEROT},
2449       {g45, eVariation::pure, PAT_ZEBRA, SPF_SYM0123 | SPF_ROT},
2450       {g47, eVariation::pure, PAT_ZEBRA, SPF_SYM0123 | SPF_ROT},
2451       {gOctagon, eVariation::pure, PAT_COLORING, SPF_DOCKS},
2452       }}
2453     };
2454 
2455   EX cpatterntype cgroup, old_cgroup;
2456 
showChangeablePatterns()2457   void showChangeablePatterns() {
2458     cmode = sm::SIDE | sm::MAYDARK;
2459     {
2460     dynamicval<bool> dc(displaycodes, true);
2461     gamescreen(0);
2462     }
2463     dialog::init();
2464     for(int i=0; i<isize(cpatterns); i++) {
2465       dialog::addBoolItem(XLAT(cpatterns[i].name), cgroup == i, '0'+i);
2466 #if CAP_TEXTURE
2467       if(texture::config.tstate == texture::tsActive && !compatible(texture::cgroup, (cpatterntype) i))
2468         dialog::lastItem().value = XLAT("BAD");
2469 #endif
2470       }
2471     dialog::addBreak(100);
2472     if(cgroup != cpUnknown && cgroup < isize(cpatterns))
2473       for(int j=0; j<isize(cpatterns[cgroup].geometries); j++) {
2474         auto &g = cpatterns[cgroup].geometries[j];
2475         string s = XLAT(ginf[g.geo].tiling_name);
2476         s += bitruncnames[int(g.var)];
2477         if(g.subpattern_flags & SPF_ALTERNATE) s += " (alt)";
2478         if(g.subpattern_flags & SPF_DOCKS) s += " (Docks)";
2479         if(cgroup == cpZebra) {
2480           if(g.whichPattern == PAT_PALACE) s += " (Palace)";
2481           else if(g.whichPattern == PAT_EMERALD) s += " (Emerald)";
2482           else s += " (Zebra)";
2483           }
2484         dialog::addBoolItem(s, geometry == g.geo && variation == g.var && whichPattern == g.whichPattern && subpattern_flags == g.subpattern_flags, 'a'+j);
2485         }
2486     bool have_goldberg = (S3 == 3 && among(cgroup, cpFootball, cpThree) && !euclid);
2487     bool have_variations = (among(cgroup, cpSingle, cpSingleSym) && !euclid);
2488     if(!(S7&1) && BITRUNCATED) have_goldberg = false; // always start from pure
2489     if(have_goldberg) {
2490       dialog::addBoolItem(XLAT("Goldberg"), GOLDBERG, 'G');
2491       dialog::lastItem().value = gp::operation_name();
2492       }
2493     if(have_variations) {
2494       dialog::addBoolItem(XLAT("variations"), GOLDBERG, 'G');
2495       dialog::lastItem().value = gp::operation_name();
2496       }
2497     else dialog::addBreak(100);
2498     dialog::addItem(XLAT("more tuning"), 'r');
2499     dialog::addBack();
2500     dialog::display();
2501 
2502     keyhandler = [have_goldberg, have_variations] (int sym, int uni) {
2503       if(uni == 'r')
2504         pushScreen(showPattern);
2505       else if(uni >= '0' && uni < '0' + isize(cpatterns))
2506         cgroup = cpatterntype(uni - '0');
2507       else if(cgroup != cpUnknown && uni >= 'a' && uni < 'a' + isize(cpatterns[cgroup].geometries)) {
2508         auto &g = cpatterns[cgroup].geometries[uni - 'a'];
2509         if(g.geo != geometry) set_geometry(g.geo);
2510         if(g.var != variation) set_variation(g.var);
2511         whichPattern = g.whichPattern;
2512         subpattern_flags = g.subpattern_flags;
2513         bool not_restarted = game_active;
2514         start_game();
2515         if(not_restarted) REMAP_TEXTURE;
2516         }
2517       #if CAP_GP
2518       else if(uni == 'G' && (have_goldberg || have_variations))
2519         gp::configure();
2520       #endif
2521       else if(doexiton(sym, uni))
2522         popScreen();
2523       };
2524     }
2525 
computeCgroup()2526   EX void computeCgroup() {
2527     if(fake::in()) { FPIU(computeCgroup()); return; }
2528     cgroup = cpUnknown;
2529     if(whichPattern == PAT_SINGLETYPE) {
2530       cgroup = cpSingle;
2531       return;
2532       }
2533     if(arcm::in()) {
2534       if(whichPattern == PAT_COLORING && geosupport_threecolor()) {
2535         if(subpattern_flags & SPF_FOOTBALL) cgroup = cpFootball;
2536         else cgroup = cpThree;
2537         }
2538       else if(whichPattern == PAT_CHESS && geosupport_chessboard()) cgroup = cpChess;
2539       else if(whichPattern == PAT_TYPES && (subpattern_flags & SPF_FOOTBALL) && geosupport_football()) cgroup = cpFootball;
2540       return;
2541       }
2542     for(int i=0; i<isize(cpatterns); i++)
2543       for(int j=0; j<isize(cpatterns[i].geometries); j++) {
2544         auto &g = cpatterns[i].geometries[j];
2545         eVariation xvar = variation;
2546         if(GOLDBERG && gp_threecolor()) xvar = eVariation::pure;
2547         if(geometry == g.geo && xvar == g.var && whichPattern == g.whichPattern && subpattern_flags == g.subpattern_flags)
2548           cgroup = cpatterntype(i);
2549         }
2550     old_cgroup = cgroup;
2551     }
2552 
pushChangeablePatterns()2553   EX void pushChangeablePatterns() {
2554     pushScreen(showChangeablePatterns);
2555     computeCgroup();
2556     }
2557   EX }
2558 
is_master(cell * c)2559 EX bool is_master(cell *c) {
2560   if(euclid) return pseudohept(c);
2561   else return c->master->c7 == c;
2562   }
2563 
2564 EX namespace linepatterns {
2565 
2566   #if HDR
2567   struct linepattern {
2568     string lpname;
2569     color_t color;
2570     ld multiplier;
2571     function<bool()> is_available;
2572     function<void(linepattern*)> renderer;
2573 
linepatternhr::linepatterns::linepattern2574     linepattern(string _lpname, color_t _color, function<bool()> _av, function<void(linepattern*)> _rend) :
2575       lpname(_lpname), color(_color), multiplier(1), is_available(_av), renderer(_rend) {}
2576     };
2577   #endif
2578 
always_available()2579   bool always_available() { return true; }
cheating()2580   bool cheating() { return cheater || autocheat || tour::on; }
stdhyp_only()2581   bool stdhyp_only() { return stdhyperbolic; }
2582 
lessalpha(color_t col,int m)2583   color_t lessalpha(color_t col, int m) {
2584     part(col, 0) /= m;
2585     return col;
2586     }
2587 
lessalphaif(color_t col,bool b)2588   color_t lessalphaif(color_t col, bool b) {
2589     return b?lessalpha(col, 4):col;
2590     }
2591 
lessalphaif(color_t col,bool b1,bool b2)2592   color_t lessalphaif(color_t col, bool b1, bool b2) {
2593     if(b1) col = lessalpha(col, 2);
2594     if(b2) col = lessalpha(col, 2);
2595     return col;
2596     }
2597 
gridlinef(const shiftmatrix & V1,const hyperpoint & h1,const shiftmatrix & V2,const hyperpoint & h2,color_t col,int par)2598   void gridlinef(const shiftmatrix& V1, const hyperpoint& h1, const shiftmatrix& V2, const hyperpoint& h2, color_t col, int par) {
2599     if(!elliptic)
2600       gridline(V1, h1, V2, h2, col, par);
2601     else {
2602       hyperpoint vh1 = V1.T * h1;
2603       hyperpoint vh2 = unshift(V2, V1.shift) * h2;
2604       ld cros = vh1[0]*vh2[0] + vh1[1]*vh2[1] + vh1[2]*vh2[2];
2605       if(cros > 0)
2606         gridline(V1, h1, V2, h2, col, par),
2607         gridline(V1, -1*h1, V2, -1*h2, col, par);
2608       else
2609         gridline(V1, h1, V2, -1*h2, col, par),
2610         gridline(V1, -1*h1, V2, h2, col, par);
2611       }
2612     }
2613 
gridlinef(const shiftmatrix & V,const hyperpoint & h1,const hyperpoint & h2,color_t col,int par)2614   void gridlinef(const shiftmatrix& V, const hyperpoint& h1, const hyperpoint& h2, color_t col, int par) { gridlinef(V, h1, V, h2, col, par); }
2615 
2616   #define ALLCELLS(R) \
2617     [] (linepattern *lp) { auto& col = lp->color; for(auto& p: current_display->all_drawn_copies) for(auto& V: p.second) { cell *c = p.first; R } }
2618 
2619   #define ATCENTER(T) \
2620     [] (linepattern *lp) { auto& col = lp->color; shiftmatrix V = gmatrix[cwt.at]; T}
2621 
2622   /** for functions drawing edges, ensure that the edge is drawn only in one direction */
way(T * c,int i)2623   template<class T> bool way(T*c, int i) {
2624     T* c2 = c->move(i);
2625     if(c == c2)
2626       return i <= c->c.spin(i);
2627     return c2 > c;
2628     }
2629 
2630   linepattern patDual("dual grid", 0xFFFFFF00, always_available,
2631     ALLCELLS(
2632       forCellIdEx(c2, i, c) if(way(c,i)) {
2633         gridlinef(V, C0, V * currentmap->adj(c, i), C0, col, 2 + vid.linequality);
2634         }
2635       )
2636     );
2637 
2638   linepattern patHepta("heptagonal grid", 0x0000C000, always_available,
2639     ALLCELLS(
2640       forCellIdEx(c2, i, c) if(way(c,i)) if(pseudohept(c) == pseudohept(c2))
2641         gridlinef(V, C0, V * currentmap->adj(c, i), C0, col, 2 + vid.linequality);
2642       )
2643     );
2644 
2645   linepattern patRhomb("rhombic tesselation", 0x0000C000, always_available,
2646     ALLCELLS(
2647       forCellIdEx(c2, i, c) if(way(c,i)) if(pseudohept(c) != pseudohept(c2))
2648         gridlinef(V, C0, V * currentmap->adj(c, i), C0, col, 2 + vid.linequality);
2649       )
2650     );
2651 
2652   linepattern patTrihepta("triheptagonal tesselation", 0x0000C000, always_available,
2653     ALLCELLS(
2654       if(pseudohept(c)) for(int t=0; t<c->type; t++)
2655       gridline(V, get_warp_corner(c, t%c->type),
2656                 get_warp_corner(c, (t+1)%c->type),
2657                 col, 1 + vid.linequality);
2658       )
2659     );
2660 
2661   linepattern patNormal("normal tesselation", 0x0000C000, always_available,
2662     ALLCELLS(
2663       for(int t=0; t<c->type; t++)
2664         if(c->move(t) && way(c,t))
2665         gridline(V, get_corner_position(c, t),
2666                   get_corner_position(c, (t+1)%c->type),
2667                   col, 1 + vid.linequality);
2668       )
2669     );
2670 
2671   linepattern patBigTriangles("big triangular grid", 0x00606000, always_available,
2672     ALLCELLS(
2673        if(is_master(c) && !euclid) for(int i=0; i<S7; i++)
2674           if(c->master->move(i) && c->master->move(i) < c->master) {
2675             gridlinef(V, C0, xspinpush0(-2*M_PI*i/S7 - master_to_c7_angle(), cgi.tessf), col, 2 + vid.linequality);
2676             }
2677        )
2678     );
2679 
2680   linepattern patBigRings("big triangles: rings", 0x00606000, cheating,
2681     ALLCELLS(
2682       if(is_master(c) && !euclid) for(int i=0; i<S7; i++)
2683         if(c->master->move(i) && way(c->master, i) && c->master->move(i)->dm4 == c->master->dm4)
2684           gridlinef(V, C0, xspinpush0(-2*M_PI*i/S7 - master_to_c7_angle(), cgi.tessf), col, 2 + vid.linequality);
2685       )
2686     );
2687 
2688   linepattern patTree("underlying tree", 0x00d0d000, cheating,
2689     ALLCELLS(
2690       if(is_master(c)) {
2691         int dir = updir(c->master);
2692         if(dir == -1) continue;
2693         hyperpoint end = currentmap->master_relative(c, true) * currentmap->adj(c->master, dir) * C0;
2694         hyperpoint start = mid(C0, mid(C0, mid(C0, end)));
2695         gridlinef(V, start, V, end, col, 2 + vid.linequality);
2696         }
2697       )
2698     );
2699   linepattern patAltTree("circle/horocycle tree", 0xd000d000, cheating,
2700     ALLCELLS(
2701       if(is_master(c)) {
2702         int dir = updir_alt(c->master);
2703         if(dir == -1) continue;
2704         hyperpoint end = currentmap->master_relative(c, true) * currentmap->adj(c->master, dir) * C0;
2705         hyperpoint start = mid(C0, mid(C0, mid(C0, end)));
2706         gridlinef(V, start, V, end, col, 2 + vid.linequality);
2707         }
2708       )
2709     );
2710 
__anon8b65abad0802null2711   linepattern patHeawood("seven-colorable torus", 0x40FF4000, [] { return euc::in(2, 6); },
2712     ALLCELLS(
2713       if(c != c->master->c7 || patterns::sevenval(c)) break;
2714       gridline(V, C0, tC0(euc::eumove(gp::loc(-1, +3))), col, 3 + vid.linequality);
2715       gridline(V, C0, tC0(euc::eumove(gp::loc(-3, +2))), col, 3 + vid.linequality);
2716       gridline(V, C0, tC0(euc::eumove(gp::loc(-2, -1))), col, 3 + vid.linequality);
2717       gridline(V, C0, tC0(euc::eumove(gp::loc(+1, -3))), col, 3 + vid.linequality);
2718       gridline(V, C0, tC0(euc::eumove(gp::loc(+3, -2))), col, 3 + vid.linequality);
2719       gridline(V, C0, tC0(euc::eumove(gp::loc(+2, +1))), col, 3 + vid.linequality);
2720       )
2721     );
2722 
2723   EX linepattern patZebraTriangles = linepattern("zebra triangles", 0x40FF4000, stdhyp_only,
2724     ALLCELLS(
2725       if(zebra40(c) / 4 == 10) {
2726         bool all = true;
2727         shiftmatrix tri[3];
2728         for(int i=0; i<3; i++)
2729           tri[i] = V * currentmap->adj(c, i*2);
2730 
2731         if(all) for(int i=0; i<3; i++)
2732           gridline(tri[i], C0, tri[(i+1)%3], C0, col, 3 + vid.linequality);
2733         }
2734       )
2735     );
2736   EX linepattern patZebraLines = linepattern("zebra lines", 0xFF000000, stdhyp_only,
2737     ALLCELLS(
2738       if(!pseudohept(c)) for(int i=0; i<c->type; i+=2) {
2739         cell *c2 = createMov(c, i);
2740         int fv1 = zebra40(c);
2741         if(fv1/4 == 4 || fv1/4 == 6 || fv1/4 == 5 || fv1/4 == 10) fv1 ^= 2;
2742         int fv2 = zebra40(c2);
2743         if(fv2/4 == 4 || fv2/4 == 6 || fv2/4 == 5 || fv2/4 == 10) fv2 ^= 2;
2744         if((fv1&1) == (fv2&1)) continue;
2745 
2746         double x = cgi.hexhexdist / 2; // sphere?.3651:euclid?.2611:.2849;
2747 
2748         gridlinef(V, ddspin(c,i,-M_PI/S3) * xpush0(x),
2749           ddspin(c,i,M_PI/S3) * xpush0(x),
2750           col, 1 + vid.linequality);
2751         }
2752       )
2753     );
__anon8b65abad0902null2754   linepattern patGoldbergTree("Goldberg tree", 0x8438A400, [] { return GOLDBERG || INVERSE; },
2755     ALLCELLS(
2756       if(c->master->c7 != c)
2757         gridlinef(V, C0, V*currentmap->adj(c,0), C0,
2758           darkena(backcolor ^ 0xFFFFFF, 0, col),
2759           2 + vid.linequality);
2760       )
2761     );
__anon8b65abad0a02null2762   linepattern patIrregularMaster("irregular master", 0x8438A400, [] { return IRREGULAR; },
2763     ALLCELLS(
2764       if(c->master->c7 != c) if(gmatrix.count(c->master->c7))
2765         gridlinef(V, C0, V*currentmap->master_relative(c, true), C0,
2766           darkena(backcolor ^ 0xFFFFFF, 0, col),
2767           2 + vid.linequality);
2768       )
2769     );
2770   linepattern patVine("vineyard pattern", 0x8438A400, stdhyp_only,
2771     ALLCELLS(
2772       int p = emeraldval(c);
2773       double hdist = hdist0(cgi.heptmove[0] * cgi.heptmove[2] * C0);
2774       if(pseudohept(c) && (p/4 == 10 || p/4 == 8))
2775       for(int i=0; i<S7; i++) if(c->move(i) && emeraldval(c->move(i)) == p-4) {
2776         gridlinef(V, C0, tC0(cgi.heptmove[i]), col, 2 + vid.linequality);
2777         gridlinef(V, C0, xspinpush0(-i * 2 * M_PI / S7, -hdist/2), col, 2 + vid.linequality);
2778         }
2779       )
2780     );
2781   linepattern patPalacelike("firewall lines", 0xFF400000, stdhyp_only,
2782     ALLCELLS(
2783       if(pseudohept(c)) for(int i=0; i<7; i++)
2784         gridlinef(V, ddspin(c,i,M_PI*5/7) * xpush0(cgi.tessf/2),
2785                      ddspin(c,i,M_PI*9/7) * xpush0(cgi.tessf/2),
2786                             col, 1 + vid.linequality);
2787       )
2788     );
2789   EX linepattern patPalace = linepattern("firewall lines: Palace", 0xFFD50000, stdhyp_only,
2790     ALLCELLS(
2791       bool a = polarb50(c);
2792       if(pseudohept(c)) for(int i=0; i<7; i++) {
2793           cell *c1 = createMov(c, (i+3) % 7);
2794           cell *c2 = createMov(c, (i+4) % 7);
2795           if(polarb50(c1) != a && polarb50(c2) != a)
2796               gridlinef(V, ddspin(c,i,M_PI*5/7) * xpush0(cgi.tessf/2),
2797                         ddspin(c,i,M_PI*9/7) * xpush0(cgi.tessf/2),
2798                                   col, 1 + vid.linequality);
2799           }
2800       )
2801     );
__anon8b65abad0b02null2802   linepattern patGoldbergSep("Goldberg", 0xFFFF0000, [] { return GOLDBERG || INVERSE; },
2803     ALLCELLS(
2804       forCellIdEx(c2, i, c) if(c2->master != c->master && way(c, i))
2805         gridlinef(V, C0, V*currentmap->adj(c, i), C0,
2806           col,
2807           1 + vid.linequality);
2808       )
2809     );
__anon8b65abad0c02null2810   linepattern patArcm("Archimedean", 0xFFFF0000, [] { return arcm::in(); },
2811     ALLCELLS(
2812       if(!pseudohept(c)) forCellIdEx(c2, i, c) if(way(c, i) && !pseudohept(c2))
2813         gridlinef(V, C0, V*currentmap->adj(c, i), C0,
2814           col,
2815           1 + vid.linequality);
2816       )
2817     );
2818 
2819   linepattern patPower("firewall lines: Power", 0xFFFF0000, stdhyp_only,
2820     ALLCELLS(
2821       int a = emeraldval(c);
2822       if(pseudohept(c) && a/4 == 8) for(int i=0; i<7; i++) {
2823           heptagon *h1 = c->master->modmove(i+1);
2824           heptagon *h2 = c->master->modmove(i-1);
2825           if(!h1 || !h2) continue;
2826           if(emeraldval(h1->c7)/4 == 8 && emeraldval(h2->c7)/4 == 8)
2827               gridlinef(V, ddspin(c,i,M_PI*5/7) * xpush0(cgi.tessf/2),
2828                         ddspin(c,i,M_PI*9/7) * xpush0(cgi.tessf/2),
2829                                   col, 1 + vid.linequality);
2830           }
2831       )
2832     );
2833   linepattern patHorocycles("horocycles", 0xd060d000, cheating,
2834     ALLCELLS(
2835       if(c->master->alt) {
2836         int d = celldistAlt(c);
2837         forCellIdEx(c2, i, c) if(c2 > c && c2->master->alt && celldistAlt(c2) == d)
2838           gridlinef(V, C0, V * currentmap->adj(c, i), C0,
2839             darkena(backcolor ^ 0xFFFFFF, 0, col),
2840             2 + vid.linequality);
2841         }
2842       )
2843     );
2844   linepattern patTriRings("triangle grid: rings", 0xFFFFFF00, always_available,
2845     ALLCELLS(
2846       forCellIdEx(c2, i, c) {
2847         if(S3 == 4) c2 = (cellwalker(c, i) + wstep + 1).cpeek();
2848         if(c2 > c) if(curr_dist(c) == curr_dist(c2))
2849           gridlinef(V, C0, V * currentmap->adj(c, i), C0, col, 2 + vid.linequality);
2850         }
2851       )
2852     );
2853   linepattern patTriTree("triangle grid: tree edges", 0xFFFFFF00, always_available,
2854     ALLCELLS(
2855       cell *parent = ts::right_parent(c, curr_dist);
2856       if(gmatrix.count(parent))
2857         gridlinef(V, C0, V * currentmap->adj(c, neighborId(c, parent)), C0, col, 2 + vid.linequality);
2858       )
2859     );
2860   linepattern patTriOther("triangle grid: other edges", 0xFFFFFF00, always_available,
2861     ALLCELLS(
2862       cell *parent = ts::right_parent(c, curr_dist);
2863       forCellIdEx(c2, i, c) if(curr_dist(c2) < curr_dist(c) && c2 != parent)
2864         gridlinef(V, C0, V * currentmap->adj(c, i), C0, col, 2 + vid.linequality);
2865       )
2866     );
2867 
2868   linepattern patCircles("circles", 0xFFFFFF00, always_available,
2869     ATCENTER(
2870       for(int i=15; i<=180; i+=15) {
2871         for(int j=0; j<360; j+=15) {
2872           for(int k=0; k<=15; k++)
2873             curvepoint(xspinpush0((j+k) * degree, i * degree));
2874           queuecurve(shiftless(Id), col, 0, PPR::LINE).V=V;
2875           }
2876         }
2877       )
2878     );
2879 
2880   linepattern patRadii("radii", 0xFFFFFF00, always_available,
2881     ATCENTER(
2882       for(int i=0; i<360; i+=15) {
2883         for(int j=0; j<180; j+=15) {
2884           for(int k=0; k<=15; k++)
2885             curvepoint(xspinpush0(i * degree, (j+k) * degree));
2886           queuecurve(shiftless(Id), col, 0, PPR::LINE).V=V;
2887           }
2888         }
2889       )
2890     );
2891   linepattern patMeridians("meridians", 0xFFFFFF00, always_available,
2892     ATCENTER(
2893       for(int j=-180; j<=180; j+=15) {
2894         for(int i=-90; i<90; i+=15) {
2895           for(int k=0; k<=15; k++)
2896             curvepoint(xpush(j * degree) * ypush0((i+k) * degree));
2897           queuecurve(V, col, 0, PPR::LINE).V=V;
2898           }
2899         }
2900       )
2901     );
2902   linepattern patParallels("parallels", 0xFFFFFF00, always_available,
2903     ATCENTER(
2904       for(int i=-90; i<=90; i += 15) {
2905         for(int j=-180; j<180; j+=15) {
2906           for(int k=0; k<=15; k++)
2907             curvepoint(xpush((j+k) * degree) * ypush0(i * degree));
2908           queuecurve(V, col, 0, PPR::LINE).V=V;
2909           }
2910         }
2911       )
2912     );
2913   linepattern patSublines("sub-lines", 0xFFFFFF00, arb::in,
2914     ALLCELLS(
2915       int i = arb::id_of(c->master);
2916       auto& sh = arb::current.shapes[i];
2917       for(auto p: sh.sublines)
2918         gridlinef(V, sh.vertices[p.first], sh.vertices[p.second], col, 2 + vid.linequality);
2919       )
2920     );
2921 
__anon8b65abad0d02null2922   linepattern patUltra("ultra-connection", 0xFFFF8000, [] { return cgflags & qULTRA; },
2923     ALLCELLS(
2924       ignore(c);
2925       color_t col2 = col;
2926       if(col2 == 0xFF)
2927         col2 = darkena(c->landparam, 0, 0xFF);
2928       for(auto U: cgi.ultra_mirrors)
2929         gridlinef(V, C0, V, mid(C0, U*C0), col2, 2 + vid.linequality);
2930       )
2931     );
2932 
2933   #if HDR
2934   extern linepattern patTriTree, patTriRings, patTriOther;
2935   #endif
2936 
2937   EX vector<linepattern*> patterns = {
2938     &patDual, &patHepta, &patRhomb, &patTrihepta, &patNormal, &patBigTriangles,
2939 
2940     &patTree, &patAltTree, &patZebraTriangles, &patZebraLines,
2941     &patVine, &patPalacelike, &patPalace, &patPower, &patHorocycles,
2942     &patTriRings, &patTriTree, &patTriOther,
2943     &patGoldbergTree, &patIrregularMaster, &patGoldbergSep, &patHeawood, &patArcm,
2944     &patCircles, &patRadii, &patMeridians, &patParallels, &patSublines, &patUltra
2945     };
2946 
clearAll()2947   EX void clearAll() {
2948     for(auto& lp: patterns) lp->color &= ~255;
2949     }
2950 
2951   EX ld width = 1;
2952 
drawAll()2953   EX void drawAll() {
2954 
2955     if(!width) return;
2956 
2957     vid.linewidth *= width;
2958 
2959     for(auto lp: patterns) if((lp->color & 255) && lp->multiplier && lp->is_available()) {
2960       vid.linewidth *= lp->multiplier;
2961       lp->renderer(lp);
2962       vid.linewidth /= lp->multiplier;
2963       }
2964 
2965     vid.linewidth /= width;
2966     }
2967 
2968   int numpat = 0;
2969 
2970   bool indiv;
2971 
showMenu()2972   EX void showMenu() {
2973     cmode = sm::SIDE | sm::MAYDARK;
2974     gamescreen(0);
2975 
2976     dialog::init(XLAT("line patterns"));
2977 
2978     int id = 0;
2979     for(auto lp: patterns) {
2980       string name = XLAT(lp->lpname);
2981       if(lp->is_available()) {
2982         if(!indiv) {
2983           dialog::addColorItem(name, lp->color, 'a'+(id++));
2984           dialog::add_action([lp] () {
2985             dialog::openColorDialog(lp->color, NULL);
2986             dialog::dialogflags |= sm::MAYDARK | sm::SIDE;
2987             });
2988           }
2989         else {
2990           dialog::addSelItem(name, fts(lp->multiplier), 'a'+(id++));
2991           dialog::add_action([lp] () { dialog::editNumber(lp->multiplier, 0.001, 10, 0.1, 1, XLAT("line width"), ""), dialog::scaleLog(); });
2992           }
2993         }
2994       else {
2995         cheater++;
2996         if(lp->is_available()) {
2997           dialog::addSelItem(name, XLAT("cheat"), 'a'+(id++));
2998           dialog::add_action(enable_cheat);
2999           }
3000         cheater--;
3001         }
3002       }
3003 
3004     dialog::addBreak(50);
3005     dialog::addBack();
3006 
3007     dialog::addSelItem("line width", fts(width), 'W');
3008     dialog::add_action([] () {
3009       dialog::editNumber(width, 0, 10, 0.1, 1, XLAT("line width"), "");
3010       });
3011 
3012     dialog::addBoolItem_action("edit widths individually", indiv, 'I');
3013 
3014     if(GDIM == 3)
3015       dialog::addBoolItem_action("fat edges", fat_edges, 'F');
3016 
3017     dialog::addBreak(50);
3018     dialog::addInfo("change the alpha parameter to show the lines");
3019 
3020     dialog::display();
3021     }
3022 
3023   }
3024 
val46(cell * c)3025 int val46(cell *c) {
3026   patterns::patterninfo si;
3027   patterns::val46(c, si, 0, 0);
3028   return si.id;
3029   }
3030 
3031 #if CAP_COMMANDLINE
3032 
read_pattern_args()3033 int read_pattern_args() {
3034   using namespace arg;
3035   if(argis("-pattern")) {
3036     PHASEFROM(2);
3037     shift();
3038     const char *c = argcs();
3039     using namespace patterns;
3040     subpattern_flags = 0;
3041     whichPattern = PAT_NONE;
3042     while(*c) {
3043       if(*c >= '0' && *c <= '9') subpattern_flags ^= 1 << (*c - '0');
3044       else if(*c == '@') subpattern_flags ^= 1 << 10;
3045       else if(*c == '-') subpattern_flags ^= 1 << 11;
3046       else if(*c == '~') subpattern_flags ^= 1 << 12;
3047       else whichPattern = ePattern(*c);
3048       c++;
3049       }
3050     }
3051 
3052   else if(argis("-wsh")) { start_game(); shift(); patterns::whichShape = args()[0]; }
3053 
3054   else if(argis("-pal")) {
3055     PHASEFROM(2); cheat();
3056     shift(); string ss = args();
3057     shift();
3058     for(auto& lp: linepatterns::patterns)
3059       if(appears(lp->lpname, ss))
3060         lp->color |= argi();
3061     }
3062   else if(argis("-palrgba")) {
3063     PHASEFROM(2); cheat();
3064     shift(); string ss = args();
3065     shift();
3066     for(auto& lp: linepatterns::patterns)
3067       if(appears(lp->lpname, ss))
3068         lp->color = arghex();
3069     }
3070 
3071   else if(argis("-fat-edges")) {
3072     PHASEFROM(2); shift(); fat_edges = argi();
3073     }
3074 
3075   else if(argis("-palw")) {
3076     PHASEFROM(2);
3077     shift(); string ss = args();
3078     for(auto& lp: linepatterns::patterns)
3079       if(appears(lp->lpname, ss)) {
3080         shift_arg_formula(lp->multiplier);
3081         return 0;
3082         }
3083     println(hlog, "linepattern not found in -palw: ", ss);
3084     shift();
3085     }
3086 
3087   else if(argis("-palgw")) shift_arg_formula(linepatterns::width);
3088 
3089   else if(argis("-noplayer")) mapeditor::drawplayer = !mapeditor::drawplayer;
3090   else if(argis("-pcol")) {
3091     shift();
3092     colortable *ct = &(colortables[patterns::whichCanvas]);
3093     if(args()[0] > '9') {
3094       char c = args()[0];
3095       if(c == 't') ct = &nestcolors;
3096       else if(c == 'd') ct = &distcolors;
3097       else if(c == 'm') ct = &minecolors;
3098       else if(c == 'E') { shift(); int d = argi(); shift(); expcolors[d] = arghex(); return 0; }
3099       else if(c == 'P') {
3100         shift(); int d = argi(); shift();
3101         color_t h = arghex();
3102         if(d >= 0 && d < 7)
3103           ((color_t*)(&vid.cs.skincolor)) [d] = h;
3104         return 0;
3105         }
3106       else ct = &(colortables[patterns::whichCanvas]);
3107       shift();
3108       }
3109     int d = argi();
3110     shift(); (*ct)[d] = arghex();
3111     }
3112   else if(argis("-canvas")) {
3113     PHASEFROM(2);
3114     stop_game();
3115     enable_canvas();
3116     shift();
3117     if(args() == "i") canvas_default_wall = waInvisibleFloor;
3118     else if(args().size() == 1) patterns::whichCanvas = args()[0];
3119     else patterns::canvasback = arghex();
3120     stop_game_and_switch_mode(rg::nothing);
3121     }
3122   else if(argis("-canvas-random")) {
3123     PHASEFROM(2);
3124     stop_game();
3125     enable_canvas();
3126     patterns::whichCanvas = 'r';
3127     shift(); patterns::rwalls = argi();
3128     }
3129   else if(argis("-cformula")) {
3130     PHASEFROM(2);
3131     stop_game();
3132     enable_canvas();
3133     patterns::whichCanvas = 'f';
3134     shift(); patterns::color_formula = args();
3135     }
3136   else if(argis("-innerwall")) {
3137     PHASEFROM(2);
3138     patterns::innerwalls = true;
3139     }
3140   else if(argis("-noinnerwall")) {
3141     PHASEFROM(2);
3142     patterns::innerwalls = false;
3143     }
3144   else if(argis("-d:line"))
3145     launch_dialog(linepatterns::showMenu);
3146 
3147   else if(argis("-d:reg"))
3148     launch_dialog(patterns::showPrePattern);
3149 
3150   else if(argis("-d:pattern"))
3151     launch_dialog(patterns::showPattern);
3152 
3153   else return 1;
3154   return 0;
3155   }
3156 
__anon8b65abad1102null3157 auto ah_pattern = addHook(hooks_args, 0, read_pattern_args) + addHook(hooks_clearmemory, 100, [] { patterns::computed_nearer_map.clear(); patterns::computed_furthest_map.clear(); });
3158 #endif
3159 
3160 }
3161