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