1 // Hyperbolic Rogue -- Barriers
2 // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file barriers.cpp
5  *  \brief This file implements routines related to barriers (Great Walls and similar).
6  */
7 
8 #include "hyper.h"
9 namespace hr {
10 
11 EX bool checkBarriersFront(cellwalker bb, int q IS(5), bool cross IS(false)) {
12 
13   if(!ctof(bb.at))
14     return false;
15 
16   if(bb.at->mpdist < BARLEV) return false;
17   if(bb.at->mpdist == BUGLEV) return false;
18   if(bb.at->bardir != NODIR) return false;
19   if(bb.spin == (PURE ? 3 : 0)) {q--; if(!q) return true; }
20 
21   if(!cross) for(int i=0; i<7; i++) {
22     cellwalker bb2 = bb + i + wstep;
23     if(bb2.at->bardir != NODIR) return false;
24     if(!PURE) {
25       bb2 = bb2 + 4 + wstep;
26       if(bb2.at->bardir != NODIR) return false;
27       }
28     }
29 
30   bb += wstep;
31   if(!PURE) { bb = bb + 3 + wstep + 3 + wstep; }
32   return checkBarriersBack(bb, q);
33   }
34 
35 /** return true if the cell c is not allowed to generate barriers because of other large things already existing nearby. */
hasbardir(cell * c)36 EX bool hasbardir(cell *c) {
37   return c->bardir != NODIR && c->bardir != NOBARRIERS;
38   }
39 
preventbarriers(cell * c)40 EX void preventbarriers(cell *c) {
41   if(hybri) c = hybrid::get_where(c).first;
42   if(c && c->bardir == NODIR) c->bardir = NOBARRIERS;
43   }
44 
45 EX bool checkBarriersBack(cellwalker bb, int q IS(5), bool cross IS(false)) {
46   // printf("back, %p, s%d\n", hr::voidp(bb.at), bb.spin);
47 
48   // if(mark) { printf("mpdist = %d [%d] bardir = %d spin=%d q=%d cross=%d\n", bb.at->mpdist, BARLEV, bb.at->bardir, bb.spin, q, cross); }
49 
50   if(bb.at->mpdist < BARLEV) return false;
51   if(bb.at->mpdist == BUGLEV) return false;
52   if(bb.at->bardir != NODIR) return false;
53 
54   // if(bb.spin == 0 && bb.at->mpdist == INFD) return true;
55 
56   if(!cross) for(int i=0; i<7; i++) {
57     cellwalker bb2 = bb + i + wstep;
58     if(bb2.at->bardir != NODIR) return false;
59     if(!PURE) {
60       bb2 = bb2 + 4 + wstep;
61       if(bb2.at->bardir != NODIR) return false;
62       }
63     }
64 
65   bb = bb + 3 + wstep + (PURE ? 5 : 4) + wstep + 3;
66   return checkBarriersFront(bb, q);
67   }
68 
69 /** warp coasts use a different algorithm for nowall barriers when has_nice_dual() is on. Check whether we should use this different algorithm when the lands are l1 and l2 */
warped_version(eLand l1,eLand l2)70 EX bool warped_version(eLand l1, eLand l2) {
71   return (has_nice_dual() && (l1 == laWarpCoast || l1 == laWarpSea || l2 == laWarpSea || l2 == laWarpCoast));
72   }
73 
get_valence(cellwalker bb,int dir,bool & ok)74 EX int get_valence(cellwalker bb, int dir, bool& ok) {
75   int steps = 0;
76   cellwalker bb1 = bb;
77   while(bb1 != bb || !steps) {
78     bb1 += dir;
79     bb1 += wstep;
80     steps++;
81     }
82   return steps;
83   }
84 
set_and_wall(cell * c,eLand l)85 EX void set_and_wall(cell *c, eLand l) {
86   setland(c, l);
87   if(c->bardir == NODIR) {
88     c->barleft = NOWALLSEP_USED;
89     c->bardir = NOBARRIERS;
90     }
91   }
92 
surround_by(bool setit,cellwalker bb,int dir,int a,int b,eLand which,bool swapped,bool & ok)93 EX void surround_by(bool setit, cellwalker bb, int dir, int a, int b, eLand which, bool swapped, bool& ok) {
94   for(int i=0; i<a; i++) {
95     bb += dir;
96     bb += wstep;
97     }
98   for(int i=a; i<b; i++) {
99     if(setit) set_and_wall(bb.at, which);
100     else {
101       if(bb.at->bardir != NODIR) ok = false;
102       if(swapped && bb.at->mpdist < BARLEV) ok = false;
103       }
104     bb += dir;
105     bb += wstep;
106     }
107   }
108 
spin_around_by(cellwalker & bb,int dir,int q)109 EX void spin_around_by(cellwalker& bb, int dir, int q) {
110   for(int i=0; i<q; i++) {
111     bb += dir;
112     bb += wstep;
113     }
114   }
115 
on_wall(eLand ws)116 EX bool on_wall(eLand ws) {
117   return among(ws, NOWALLSEP_WALL_CPOS, NOWALLSEP_WALL_CNEG, NOWALLSEP_WALL_EPOS, NOWALLSEP_WALL_ENEG);
118   }
119 
wsname(eLand ws)120 string wsname(eLand ws) {
121   if(ws == NOWALLSEP) return "NO";
122   if(ws == NOWALLSEP_SWAP) return "SWAP";
123   if(ws == NOWALLSEP_USED) return "USED";
124   if(ws == NOWALLSEP_WALL) return "WALL";
125   if(ws == NOWALLSEP_WALL_CPOS) return "CPOS";
126   if(ws == NOWALLSEP_WALL_CNEG) return "CNEG";
127   if(ws == NOWALLSEP_WALL_EPOS) return "EPOS";
128   if(ws == NOWALLSEP_WALL_ENEG) return "ENEG";
129   return dnameof(ws);
130   }
131 
general_barrier_advance(cellwalker & bb,int & dir,eLand & l1,eLand & l2,eLand & ws,bool setit)132 EX bool general_barrier_advance(cellwalker& bb, int& dir, eLand& l1, eLand& l2, eLand& ws, bool setit) {
133   bool ok = true;
134   if(ws == NOWALLSEP_WALL) {
135 
136 
137     /*
138     if(setit) bb.at->monst = moBug0;
139     if(setit) bb.cpeek()->monst = moBug2;
140     */
141 
142     int steps = get_valence(bb, dir, ok);
143     if(steps % 2 == 0) goto again;
144 
145     int s = (steps - 1) / 2;
146     surround_by(setit, bb, dir, 1, s, l1, false, ok);
147     surround_by(setit, bb, dir, s+1, steps-1, l2, true, ok);
148     spin_around_by(bb, dir, s);
149 
150     ws = dir > 0 ? NOWALLSEP_WALL_CPOS : NOWALLSEP_WALL_CNEG;
151     // goto tile;
152     return ok;
153     }
154 
155   if(on_wall(ws)) {
156 
157     bool at_corner = among(ws, NOWALLSEP_WALL_CPOS, NOWALLSEP_WALL_CNEG);
158 
159     cell *current = bb.at;
160 
161     // if at_corner: bb is facing the tile 1 before the first inside
162     int t = bb.at->type;
163 
164     int q = at_corner ? t/2 : (t-1) / 2;
165 
166     if(1) {
167       auto bb1 = bb;
168       if(at_corner) bb1 += dir;
169       bb1 -= dir;
170       for(int i=1; i<q; i++) {
171         int d = get_valence(bb1, -dir, ok);
172         surround_by(setit, bb1, -dir, 1, d, l1, false, ok);
173         bb1-=dir;
174         }
175       }
176 
177     bb += dir;
178     for(int i=1; i<q; i++) {
179       int d = get_valence(bb, dir, ok);
180       surround_by(setit, bb, dir, 1, d, l2, true, ok);
181       bb+=dir;
182       }
183 
184     // bb is now facing the last neighbor inside
185 
186     if(t % 2 == (at_corner ? 1 : 0)) {
187       if(setit) setbarrier(current, l1, l2, at_corner);
188       int d = get_valence(bb, dir, ok);
189       surround_by(setit, bb, dir, 2, d, l2, true, ok);
190 
191       bb += dir;
192       bb += wstep;
193 
194       d = get_valence(bb, -dir, ok);
195       surround_by(setit, bb, -dir, 2, d-1, l1, false, ok);
196 
197       ws = dir > 0 ? NOWALLSEP_WALL_EPOS : NOWALLSEP_WALL_ENEG;
198       return ok;
199       }
200 
201     int steps1 = get_valence(bb, dir, ok);
202     if(setit) setbarrier(current, l1, l2, true);
203 
204     if(steps1 % 2 == 0) {
205       int s1 = steps1 / 2;
206       surround_by(setit, bb, dir, 1, s1, l1, false, ok);
207       surround_by(setit, bb, dir, s1+1, steps1-1, l2, true, ok);
208       spin_around_by(bb, dir, s1);
209       ws = dir > 0 ? NOWALLSEP_WALL_CPOS : NOWALLSEP_WALL_CNEG;
210       return ok;
211       }
212 
213     int s1 = (steps1 - 1) / 2;
214     surround_by(setit, bb, dir, 1, s1, l1, false, ok);
215     surround_by(setit, bb, dir, s1+2, steps1-1, l2, true, ok);
216     spin_around_by(bb, dir, s1);
217     bb += dir;
218     ws = NOWALLSEP_WALL;
219     }
220   else if(warped_version(l1, l2)) {
221     bb = bb + wstep + (2*dir) + wstep + dir;
222     dir = -dir;
223     swap(l1, l2);
224     }
225   else {
226     again:
227     cellwalker bb1 = bb;
228     int steps = get_valence(bb, dir, ok);
229     int s = 2;
230     if(ws == NOWALLSEP_SWAP) s = 5 - s;
231     if(dir == -1) s = 5 - s;
232     s = (1 + steps - s) / 2;
233     surround_by(setit, bb, dir, 1, s, l1, false, ok);
234     surround_by(setit, bb, dir, s+2, steps-1, l2, true, ok);
235     spin_around_by(bb, dir, s);
236     bb += dir;
237     if(steps & 1) ws = (ws == NOWALLSEP ? NOWALLSEP_SWAP : NOWALLSEP);
238     if(bb.at == bb1.at) goto again;
239     }
240   return ok;
241   }
242 
general_barrier_check(cellwalker bb,int q,int dir,eLand ws,eLand l1 IS (laNone),eLand l2 IS (laNone))243 EX bool general_barrier_check(cellwalker bb, int q, int dir, eLand ws, eLand l1 IS(laNone), eLand l2 IS(laNone)) {
244 
245   if(l1 == l2) {
246     if(bb.at->mpdist < BARLEV || bb.cpeek()->mpdist < BARLEV || bb.cpeek()->bardir != NODIR || bb.at->bardir != NODIR)
247       return false;
248     for(int i=0; i<bb.at->type; i++) {
249       cell *c1 = bb.at->move(i);
250       if(!c1) continue;
251       if(c1->bardir != NODIR) return false;
252       }
253     }
254 
255   if(l1 != l2 && bb.at->barleft != NOWALLSEP_USED) {
256     bb.at->bardir = bb.spin; bb.at->barright = l2; bb.at->barleft = ws;
257     setland(bb.at, l1);
258     }
259   if(q <= 0) return true;
260 
261   bool b = general_barrier_advance(bb, dir, l1, l2, ws, l1 != l2);
262   if(l1 == l2 && !b) return false;
263   return general_barrier_check(bb, q-1, dir, ws, l1, l2);
264   }
265 
general_barrier_check_after(cellwalker bb,int steps,int q,int dir,eLand ws,eLand l1 IS (laNone),eLand l2 IS (laNone))266 EX bool general_barrier_check_after(cellwalker bb, int steps, int q, int dir, eLand ws, eLand l1 IS(laNone), eLand l2 IS(laNone)) {
267   for(int i=0; i<steps; i++) general_barrier_advance(bb, dir, l1, l2, ws, l1 != l2);
268   return general_barrier_check(bb, q, dir, ws, l1, l2);
269   }
270 
getElementalWall(eLand l)271 EX eWall getElementalWall(eLand l) {
272   if(l == laEAir) return waChasm;
273   if(l == laEEarth) return waStone;
274   if(l == laEFire) return waEternalFire;
275   if(l == laEWater) return waSea;
276   return waNone;
277   }
278 
setbarrier(cell * c,eLand l1,eLand l2,bool setbar)279 EX void setbarrier(cell *c, eLand l1, eLand l2, bool setbar) {
280   if(isSealand(l1) && isSealand(l2)) {
281     if(l1 == laKraken || l2 == laKraken)
282     if(l1 != laWarpSea && l2 != laWarpSea)
283       setbar = !setbar;
284     c->wall = setbar ? waBarrier : waSea;
285     c->land = laOceanWall;
286     }
287   else if(isElemental(l1) && isElemental(l2)) {
288     c->land = laElementalWall;
289     c->wall = getElementalWall(l1);
290     }
291   else if(l1 == laHaunted || l2 == laHaunted) {
292     c->land = laHauntedWall;
293     }
294   else if(l1 == laMirrored2 || l2 == laMirrored2)
295     c->land = laMirrorWall2;
296   else if(l1 == laMirrored || l2 == laMirrored)
297     c->land = laMirrorWall;
298   else if(l1 == laTerracotta && l2 == laTerracotta) {
299     c->land = laMercuryRiver;
300     c->wall = waMercury;
301     }
302   else {
303     c->wall = waBarrier;
304     c->land = laBarrier;
305     }
306   }
307 
setbarrier(cell * c)308 EX void setbarrier(cell *c) {
309   setbarrier(c, c->barleft, c->barright, ctof(c));
310   }
311 
setland(cell * c,eLand l)312 EX void setland(cell *c, eLand l) {
313   if(c->land != l)  {
314     c->landparam = 0;
315     }
316   if(l == laNone) {
317     printf("setland\n"); // NONEDEBUG
318     }
319   c->land = l;
320   }
321 
extendcheck(cell * c)322 EX void extendcheck(cell *c) {
323   return;
324   if(BITRUNCATED && c->landparam == 0 && c->barleft != NOWALLSEP) {
325     raiseBuggyGeneration(c, "extend error");
326     }
327   }
328 
mirrorwall(cell * c)329 EX bool mirrorwall(cell *c) {
330   return c->barleft == laMirrored || c->barright == laMirrored;
331   }
332 
extendBarrierFront(cell * c)333 EX void extendBarrierFront(cell *c) {
334   limitgen("extend front %p\n", hr::voidp(c));
335   if(buggyGeneration) return;
336   int ht = c->landparam;
337   extendcheck(c);
338 
339   cellwalker bb(c, c->bardir); setbarrier(bb.at);
340   bb += wstep;
341 
342   if(BITRUNCATED) {
343     bb.at->barleft = c->barleft;
344     bb.at->barright = c->barright;
345     setbarrier(bb.at);
346     if(!mirrorwall(bb.at))
347       bb.at->landparam = (ht-4);
348   //printf("[A heat %d]\n", ht-4);
349 
350     setland((bb + 2).cpeek(), c->barleft);
351     setland((bb + 4).cpeek(), c->barright);
352 
353     bb = bb + 3 + wstep;
354     bb.at->barleft = c->barright;
355     bb.at->barright = c->barleft;
356     setbarrier(bb.at);
357     if(!mirrorwall(bb.at))
358       bb.at->landparam = (ht-4)^2;
359   //printf("[B heat %d]\n", (ht-4)^2);
360 
361     bb = bb + 3 + wstep;
362 
363     bb.at->barleft = c->barleft;
364     bb.at->barright = c->barright;
365     if(!mirrorwall(bb.at))
366       bb.at->landparam = ht ^ 2;
367     }
368 
369 //printf("[C heat %d]\n", (ht)^2);
370   bb.at->bardir = bb.spin;
371   bb.at->barleft = c->barright;
372   bb.at->barright = c->barleft;
373   // printf("#1\n");
374   extendcheck(bb.at);
375   extendBarrier(bb.at);
376 
377   for(int a=-3; a<=3; a++) if(a) {
378     bb.at = c; bb.spin = c->bardir; bb += (PURE?-a:a); bb += wstep;
379     setland(bb.at, a > 0 ? c->barright : c->barleft);
380     }
381   }
382 
extendBarrierBack(cell * c)383 EX void extendBarrierBack(cell *c) {
384   limitgen("extend back %p\n", hr::voidp(c));
385   if(buggyGeneration) return;
386   int ht = c->landparam;
387   extendcheck(c);
388 
389   cellwalker bb(c, c->bardir); setbarrier(bb.at);
390   bb = bb + 3 + wstep + (PURE?5:4);
391   setland(bb.at, PURE ? c->barleft : c->barright);
392   bb = bb + wstep + 3;
393   bb.at->bardir = bb.spin;
394   bb.at->barleft = c->barright;
395   bb.at->barright = c->barleft;
396   if(!mirrorwall(bb.at))
397     bb.at->landparam = ht ^ 11;
398   extendcheck(bb.at);
399 //printf("[D heat %d]\n", (ht^11));
400 
401   // needed for CR2 to work
402   if(BITRUNCATED) {
403     auto bb2 = bb + wstep;
404     bb2.at->barleft = c->barright;
405     bb2.at->barright = c->barleft;
406     if(!mirrorwall(bb2.at))
407       bb2.at->landparam = (ht^11)-4;
408     }
409 //printf("[E heat %d]\n", (ht^11));
410 
411   // printf("#2\n");
412   extendBarrier(bb.at);
413   }
414 
general_barrier_extend(cell * c)415 EX void general_barrier_extend(cell *c) {
416 
417   eLand ws = c->barleft;
418   cellwalker cw(c, c->bardir);
419 
420   c->barleft = NOWALLSEP_USED;
421   eLand l1 = c->land;
422   eLand l2 = c->barright;
423 
424   if(!on_wall(ws)) {
425     if(c->bardir == NODIR) {
426       println(hlog, "error: NODIR barrier at ", c);
427       return;
428       }
429     setland(cw.cpeek(), l2);
430     cw.cpeek()->barleft = NOWALLSEP_USED;
431     }
432 
433   if(ws == NOWALLSEP_WALL && barrier_cross(l1, l2)) {
434 
435     cellwalker p_cw = cw;
436     eLand p_l1 = l1, p_l2 = l2;
437     eLand p_ws = ws;
438     int i = 1;
439     general_barrier_advance(p_cw, i, p_l1, p_l2, p_ws, false);
440 
441     cellwalker n_cw = cw;
442     eLand n_l1 = l1, n_l2 = l2;
443     eLand n_ws = ws;
444     i = -1;
445     general_barrier_advance(n_cw, i, n_l1, n_l2, n_ws, false);
446 
447     int dir = 0;
448 
449     println(hlog, "left ", n_cw, " = ", n_cw.at->barleft, " right ", p_cw, " = ", p_cw.at->barleft, " USED = ", NOWALLSEP_USED);
450 
451     if(n_cw.at->barleft == NOWALLSEP_USED && p_cw.at->barleft != NOWALLSEP_USED) dir = 1;
452     if(p_cw.at->barleft == NOWALLSEP_USED && n_cw.at->barleft != NOWALLSEP_USED) dir = -1;
453 
454     if(dir) {
455       if(!general_barrier_check_after(cw, 2, 10, 1, NOWALLSEP_WALL_EPOS, l1, l1)) {
456         println(hlog, "failed to check 1");
457         dir = 0;
458         }
459       if(!general_barrier_check_after(cw+wstep, 2, 10, 1, NOWALLSEP_WALL_EPOS, l1, l1)) {
460         println(hlog, "failed to check 2");
461         dir = 0;
462         }
463       }
464 
465     if(dir) {
466       eLand xl1 = oppositeElement(l1, l2);
467       eLand xl2 = oppositeElement(l2, l1);
468 
469       if(dir == 1) {
470         general_barrier_check_after(cw, 0, 10, 1, NOWALLSEP_WALL_EPOS, l1, xl2);
471         general_barrier_check_after(cw+wstep, 0, 10, 1, NOWALLSEP_WALL_EPOS, xl1, l2);
472         }
473       else {
474         general_barrier_check_after(cw, 0, 10, 1, NOWALLSEP_WALL_EPOS, xl2, l1);
475         general_barrier_check_after(cw+wstep, 0, 10, 1, NOWALLSEP_WALL_EPOS, l2, xl1);
476         }
477 
478       general_barrier_check(cw, 10, dir, ws, xl2, xl1);
479       return;
480       }
481     }
482 
483   for(int i: {-1, 1}) {
484 
485     if(i == -1 && among(ws, NOWALLSEP_WALL_CPOS, NOWALLSEP_WALL_EPOS)) continue;
486     if(i == +1 && among(ws, NOWALLSEP_WALL_CNEG, NOWALLSEP_WALL_ENEG)) continue;
487 
488     // general_barrier_check((cw, 10, i, ws, l1, l2);
489 
490     cellwalker cw0 = cw;
491     eLand xl1 = l1, xl2 = l2;
492     eLand ws1 = ws;
493     int i1 = i;
494     general_barrier_advance(cw0, i1, xl1, xl2, ws1, true);
495 
496     if(cw0.at->barleft != NOWALLSEP_USED) {
497       setland(cw0.at, xl1);
498       cw0.at->barleft = ws1;
499       cw0.at->barright = xl2;
500       cw0.at->bardir = cw0.spin;
501       extendcheck(cw0.at);
502       extendBarrier(cw0.at);
503       }
504     }
505   }
506 
507 bool gotit = false;
508 
extendCR5(cell * c)509 EX void extendCR5(cell *c) {
510   if(!BITRUNCATED) return;
511 // if(c->barright == laCrossroads5) extendCR5(c);
512   eLand forbidden = c->barleft;
513   eLand forbidden2 = laNone;
514   cellwalker cw(c, c->bardir);
515   for(int u=0; u<2; u++) {
516     // if(gotit) break;
517     cw = cw + 2 + wstep + 2 + wstep + 5;
518     if(cw.at->bardir == NODIR) {
519       cw.at->landparam = 40;
520       cw.at->bardir = cw.spin;
521       cw.at->barright = laCrossroads5;
522       eLand nland = forbidden;
523       for(int i=0; i<10 && (nland == forbidden || nland == forbidden2); i++)
524         nland = getNewLand(laCrossroads5);
525       if(ls::single() && specialland == laCrossroads5)
526         nland = laCrossroads5;
527       cw.at->barleft = forbidden2 = nland;
528       landcount[nland]++;
529       extendBarrier(cw.at);
530       gotit = true;
531       }
532     else forbidden2 = cw.at->barleft;
533     }
534   }
535 
isbar4(cell * c)536 EX bool isbar4(cell *c) {
537   return
538     c->wall == waBarrier || c->land == laElementalWall ||
539     c->land == laMirrorWall || c->land == laMirrorWall2 ||
540     c->land == laMercuryRiver;
541   }
542 
barrier_cross(eLand l,eLand r)543 EX bool barrier_cross(eLand l, eLand r) {
544   if(l == laCrossroads3 || r == laCrossroads3) return hrand(100) < 66;
545   if(isElemental(l) && isElemental(r)) return hrand(100) < 75;
546   return false;
547   }
548 
extendBarrier(cell * c)549 EX void extendBarrier(cell *c) {
550   limitgen("extend barrier %p\n", hr::voidp(c));
551   if(buggyGeneration) return;
552 
553   if(c->barleft == NOWALLSEP_USED) return;
554 
555   extendcheck(c);
556 
557   // printf("build barrier at %p", hr::voidp(c));
558   if(c->land == laBarrier || c->land == laElementalWall || c->land == laHauntedWall || c->land == laOceanWall ||
559     c->land == laMirrorWall || c->land == laMirrorWall2 || c->land == laMercuryRiver) {
560     // printf("-> ready\n");
561     return;
562     }
563 //  if(c->wall == waWaxWall) return;
564   if(c->mpdist > BARLEV) {
565     // printf("-> too far\n");
566     return; // == INFD) return;
567     }
568 
569   if(c->barleft == NOWALLSEP || c->barleft == NOWALLSEP_SWAP || c->barleft == NOWALLSEP_WALL || on_wall(c->barleft)) {
570     #if MAXMDIM >= 4
571     if(WDIM == 3) extend3D(c);
572     else
573     #endif
574     general_barrier_extend(c);
575     return;
576     }
577 
578   bool firstmirror =
579     (c->barleft == laMirrored || c->barright == laMirrored) &&
580       c->barleft != laMirrored2 && c->barright != laMirrored2;
581 
582   if(firstmirror && c->barleft == laMirror && hrand(100) < 60) {
583     cellwalker cw(c, c->bardir);
584     if(BITRUNCATED) cw += wstep;
585     if(cw.at->land != laMirrorWall)
586       if(buildBarrier6(cw, 1)) return;
587     }
588 
589   if(firstmirror && (PURE?c->barleft == laMirror : c->barright == laMirror) && hrand(100) < 60) {
590     cellwalker cw(c, c->bardir);
591     if(PURE) {
592       cw = cw - 3 + wstep - 3;
593       }
594     else {
595       cw = cw + wstep + 3 + wstep - 1; // check this
596       }
597     if(buildBarrier6(cw, 2)) return;
598     }
599 
600   if(barrier_cross(c->barleft, c->barright) || (firstmirror && hrand(100) < 60)) {
601 
602     cellwalker cw(c, c->bardir);
603     if(PURE) {
604       cw += wstep;
605       if(isbar4(cw.at)) {
606         cw = cw + wstep + 3 + wstep - 1 + wstep;
607         bool b = buildBarrier4(cw.at, cw.spin, 2, oppositeElement(c->barleft, c->barright), c->barright);
608         if(b) return;
609         }
610       else {
611         bool b = buildBarrier4(c, c->bardir, 1, c->barleft, c->barright);
612         if(b) return;
613         }
614       }
615     else {
616       cw = cw + 3 + wstep;
617       cell *cp = (cw + 4 + wstep).at;
618       if(!isbar4(cp)) {
619         cw = cw + 2 + wstep;
620         bool b = buildBarrier4(cw.at, cw.spin, 2, oppositeElement(c->barleft, c->barright), c->barright);
621         if(b) return;
622         }
623       else {
624         bool b = buildBarrier4(c, c->bardir, 1, c->barleft, c->barright);
625         if(b) return;
626         }
627       }
628     }
629 
630   extendBarrierFront(c);
631   extendBarrierBack(c);
632 
633   if(c->barright == laCrossroads5) extendCR5(c);
634   }
635 
buildBarrierForce(cell * c,int d,eLand l)636 EX void buildBarrierForce(cell *c, int d, eLand l) {
637   c->bardir = d;
638   eLand oldland = c->land;
639   if(oldland == laNone) {
640     raiseBuggyGeneration(c, "oldland is NONE");
641     return;
642     }
643   eLand newland = l ? l : getNewLand(oldland);
644   landcount[newland]++;
645   if(d == 4 || d == 5 || d == 6) c->barleft = oldland, c->barright = newland;
646   else c->barleft = newland, c->barright = oldland;
647   if(!mirrorwall(c)) c->landparam = 40;
648   extendcheck(c);
649   }
650 
buildBarrier(cell * c,int d,eLand l IS (laNone))651 EX void buildBarrier(cell *c, int d, eLand l IS(laNone)) {
652 
653   if(!old_nice_walls()) {
654     general_barrier_build(NOWALLSEP_WALL, c, l ? l : getNewLand(c->land), NODIR);
655     return;
656     }
657 
658   d %= 7;
659   cellwalker bb(c, d);
660 
661   if(checkBarriersFront(bb) && checkBarriersBack(bb))
662     buildBarrierForce(c, d, l);
663   }
664 
buildBarrier6(cellwalker cw,int type)665 EX bool buildBarrier6(cellwalker cw, int type) {
666   limitgen("build6 %p/%d (%d)\n", hr::voidp(cw.at), cw.spin, type);
667 
668   cellwalker b[4];
669 
670   if(buggyGeneration) return true;
671 
672   if(BITRUNCATED) {
673     b[0] = cw + wstep;
674     b[1] = cw + 1 + wstep + 3 + wstep;
675     b[2] = cw + 4 + wstep;
676     b[3] = cw + 3 + wstep + 3 + wstep;
677     }
678   else {
679     b[0] = cw;
680     b[1] = cw + 3 + wstep + 3;
681     b[2] = cw - 2 + wstep - 3;
682     b[3] = cw - 3 + wstep + 2 + wstep - 3;
683 
684     if(type == 1 && b[3].at->land != laMirrorWall) return false;
685     if(type == 2 && (b[1] + wstep).at->land != laMirrorWall) return false;
686     // if(type == 2 && b[2].at->land != laMirrorWall) return false;
687     }
688 
689   if(false) {
690     for(int z=0; z<4; z++) {
691       printf("%p/%d\n", hr::voidp(b[z].at), b[z].spin);
692       b[z].at->wall = waStrandedBoat; b[z].at->land = laAlchemist;
693       b[z].at->mondir = b[z].spin;
694       b[z].at->mpdist = 7;
695       b[z].at->item = eItem(1+z);
696       buggyGeneration = true;
697       }
698     return true;
699     }
700 
701   if(type == 1) {
702     if(!(PURE?checkBarriersFront:checkBarriersBack)(b[1], 6, true)) return false;
703     if(!(PURE?checkBarriersFront:checkBarriersBack)(b[2], 6, true)) return false;
704     }
705   else {
706     if(!(PURE?checkBarriersFront:checkBarriersBack)(b[0], 6, true)) return false;
707     if(!(PURE?checkBarriersFront:checkBarriersBack)(b[3], 6, true)) return false;
708     }
709 
710   for(int d=0; d<4; d++) {
711     b[d].at->bardir = b[d].spin;
712 
713     if(PURE) {
714       b[0].at->barleft = laMirrored, b[0].at->barright = laMirrored2;
715       b[1].at->barleft = laMirror, b[1].at->barright = laMirrored;
716       b[2].at->barleft = laMirrored2, b[2].at->barright = laMirrored;
717       b[3].at->barleft = laMirrored, b[3].at->barright = laMirror;
718       }
719     else {
720       b[0].at->barleft = laMirror, b[0].at->barright = laMirrored;
721       b[1].at->barleft = laMirrored, b[1].at->barright = laMirror;
722       b[2].at->barleft = laMirrored, b[2].at->barright = laMirrored2;
723       b[3].at->barleft = laMirrored2, b[3].at->barright = laMirrored;
724       }
725 
726     (PURE?extendBarrierFront:extendBarrierBack)(b[d].at);
727     }
728 
729   if(PURE && false) {
730     for(int z=0; z<4; z++)
731       b[z].at->item = eItem(1+z+4*type);
732     for(int a=0; a<4; a++)
733       extendBarrierBack((b[a]+wstep).at);
734     }
735 
736   if(BITRUNCATED) {
737     setland((cw+1).cpeek(), laMirrorWall);
738     setland((cw+2).cpeek(), laMirrored);
739     setland((cw+3).cpeek(), laMirrorWall2);
740     setland((cw+4).cpeek(), laMirrorWall2);
741     setland((cw+5).cpeek(), laMirrored);
742     setland((cw+0).cpeek(), laMirrorWall);
743     setland((b[0]+2).cpeek(), laMirrored);
744     setland((b[3]+6).cpeek(), laMirrored2);
745     setland((b[3]+5).cpeek(), laMirrored2);
746     setland((b[1]-1).cpeek(), laMirrored);
747     setland((b[2]-2).cpeek(), laMirrored);
748     setland((b[1]-2).cpeek(), laMirrored);
749     setland((b[0]-2).cpeek(), laMirror);
750     cw.at->land = laMirrorWall;
751     cw.at->wall = waMirrorWall;
752     cw.at->landparam = 1;
753     }
754   else {
755     setland(cw.at, laMirrorWall2);
756     setland((cw+0).cpeek(), laMirrorWall2);
757     setland((cw+1).cpeek(), laMirrored);
758     setland((cw+2).cpeek(), laMirrored);
759     setland((cw+3).cpeek(), laMirrorWall);
760     setland((cw+4).cpeek(), laMirrored);
761     setland((cw+5).cpeek(), laMirrorWall2);
762     setland((cw+6).cpeek(), laMirrored2);
763 
764     setland((b[1]).cpeek(), laMirrorWall);
765     setland((b[1]+1).cpeek(), laMirror);
766     setland((b[1]+2).cpeek(), laMirrorWall);
767     setland((b[1]+6).cpeek(), laMirrored);
768 
769     setland((b[0] + wstep - 2).cpeek(), laMirrored);
770     setland((b[3] + wstep - 2).cpeek(), laMirrored);
771     }
772 
773   return true;
774   }
775 
buildBarrier4(cell * c,int d,int mode,eLand ll,eLand lr)776 EX bool buildBarrier4(cell *c, int d, int mode, eLand ll, eLand lr) {
777   limitgen("build4 %p\n", hr::voidp(c));
778   if(buggyGeneration) return true;
779   d %= 7;
780 
781   cellwalker cd(c, d);
782 
783   cellwalker b1 = cd;
784   cellwalker b2 = PURE ? cd + wstep : cd + wstep + 3 + wstep + 3 + wstep;
785   cellwalker b3 = PURE ? cd - 1 + wstep + 3 : cd + wstep + 4 + wstep + 4;
786   cellwalker b4 = PURE ? cd + 1 + wstep - 3 : cd + wstep - 4 + wstep - 4;
787 
788   if(mode == 0) {
789     if(!((checkBarriersBack(b1) && checkBarriersBack(b2)))) return false;
790     if(!((checkBarriersFront(b3) && checkBarriersFront(b4)))) return false;
791     }
792 
793   if(mode == 1) {
794     if(!(checkBarriersFront(b3, 5, true) && checkBarriersFront(b4, 5, true)))
795       return false;
796     }
797 
798   if(mode == 2) {
799     if(!((checkBarriersBack(b1, 5, true) && checkBarriersBack(b2, 5, true))))
800       return false;
801     }
802 
803   eLand xl = oppositeElement(ll, lr);
804   eLand xr = oppositeElement(lr, ll);
805 
806   c->bardir = d, c->barleft = ll, c->barright = lr; extendBarrierBack(c);
807 
808   c= b2.at; d=b2.spin;
809   c->bardir = d, c->barleft = xl, c->barright = xr; extendBarrierBack(c);
810 
811   c= b3.at; d=b3.spin;
812   c->bardir = d, c->barleft = xl, c->barright = lr; extendBarrierFront(c);
813 
814   c= b4.at; d=b4.spin;
815   c->bardir = d, c->barleft = ll, c->barright = xr; extendBarrierFront(c);
816 
817   if(BITRUNCATED) for(int a=-3; a<=3; a++) if(a) {
818     setland((b1+a).cpeek(), a > 0 ? lr : ll);
819     setland((b2+a).cpeek(), a > 0 ? xr : xl);
820     setland((b3+a).cpeek(), a > 0 ? lr : xl);
821     setland((b4+a).cpeek(), a > 0 ? xr : ll);
822     }
823 
824   if(PURE) setbarrier(b1.at), setbarrier(b2.at), setbarrier(b3.at), setbarrier(b4.at);
825 
826   if(BITRUNCATED) {
827     cell *cp;
828     cp = (b1+wstep).at;
829     cp->barleft = ll; cp->barright = lr; setbarrier(cp);
830     cp = (b2+wstep).at;
831     cp->barleft = xl; cp->barright = xr; setbarrier(cp);
832     }
833 
834   return true;
835   }
836 
buildBarrierStrong(cell * c,int d,bool oldleft,eLand newland)837 EX void buildBarrierStrong(cell *c, int d, bool oldleft, eLand newland) {
838   d %= 7;
839   cellwalker bb(c, d);
840 
841   c->bardir = d;
842   eLand oldland = c->land;
843   landcount[newland]++;
844 
845   if(oldleft) c->barleft = oldland, c->barright = newland;
846   else c->barleft = newland, c->barright = oldland;
847   extendcheck(bb.at);
848   }
849 
buildBarrierStrong(cell * c,int d,bool oldleft)850 EX void buildBarrierStrong(cell *c, int d, bool oldleft) {
851   buildBarrierStrong(c, d, oldleft, getNewLand(c->land));
852   }
853 
buildCrossroads2(cell * c)854 EX void buildCrossroads2(cell *c) {
855 
856   if(buggyGeneration) return;
857 
858   if(!c) return;
859 
860   for(int i=0; i<c->type; i++)
861     if(c->move(i) && !c->move(i)->landparam && c->move(i)->mpdist < c->mpdist)
862       buildCrossroads2(c->move(i));
863 
864   if(hasbardir(c))
865     extendBarrier(c);
866 
867   if(c->land != laCrossroads2) return;
868 
869   if(!c->landparam) {
870     for(int i=0; i<c->type; i++) {
871       cell *c2 = createMov(c, i);
872       if(c2 && c2->landparam && (c2->land == laCrossroads2 || c2->land == laBarrier)) {
873         for(int j=0; j<c2->type; j++) {
874           createMov(c2, j);
875           cell *c3 = c2->move(j);
876           if(c3 && c3->landparam && (c3->land == laCrossroads2 || c3->land == laBarrier)) {
877             int h2 = c2->landparam;
878             int h3 = c3->landparam;
879 
880             if(h2 > 100) { raiseBuggyGeneration(c2, "bad c2 landparam"); return; }
881             if(h3 > 100) { raiseBuggyGeneration(c3, "bad c3 landparam"); return; }
882 
883             // ambiguous
884             if(h2/4 == 1 && h3/4 == 3) continue;
885             if(h2/4 == 3 && h3/4 == 1) continue;
886 
887             for(int d=0; d<c2->type; d++)
888               if(emeraldtable[h2][d] == h3) {
889                 int nh = emeraldtable[h2][(42+d + c->c.spin(i) - j) % c2->type];
890                 if(c->landparam>0 && c->landparam != nh) {
891                   printf("CONFLICT\n");
892                   raiseBuggyGeneration(c, "CONFLICT");
893                   }
894                 c->landparam = nh;
895                 }
896 
897             if(c->landparam == 0)
898               printf("H2 = %d H3=%d\n", c2->landparam, c3->landparam);
899             }
900           }
901         if(c->landparam == 0) {
902           printf("H2 = %d\n", c2->landparam);
903 //        halted = true;
904 //        c->heat = -1;
905           raiseBuggyGeneration(c, "buildCrossroads2x");
906           return;
907           }
908         }
909       }
910 
911     if(c->landparam) {
912 //    for(int i=0; i<c->type; i++) {
913 //      cell *c2 = c->move(i);
914 //      buildCrossroads2(c2);
915 //      }
916       }
917     else {
918       raiseBuggyGeneration(c, "buildCrossroads2");
919       return;
920       }
921     }
922 
923   int h = c->landparam;
924 
925   if(h/4 >= 8 && h/4 <= 11) {
926     for(int i=0; i<c->type; i++) if(c->land != laBarrier) {
927       cell *c2 = createMov(c, i);
928       if(c2->land == laBarrier) continue;
929       c2->land = laCrossroads2;
930       if(!c2->landparam) buildCrossroads2(c2);
931       }
932     if(h/4 == 8 || h/4 == 10)
933     for(int i=0; i<c->type; i++) {
934       if(c->move(i) && c->move(i)->landparam == h-4) {
935         bool oldleft = true;
936         for(int j=1; j<=3; j++)
937           if(c->modmove(i+j) && c->modmove(i+j)->mpdist < c->mpdist)
938             oldleft = false;
939 
940         c->landparam = h;
941         if(ls::single())
942           buildBarrierStrong(c, i, oldleft, specialland);
943         else
944           buildBarrierStrong(c, i, oldleft);
945         c->landparam = h;
946         extendBarrier(c);
947         }
948       }
949     }
950   }
951 
952 #if MAXMDIM >= 4
bufferzone()953 EX bool bufferzone() { return PURE && S7 == 6; }
basic_tests()954 EX int basic_tests() { return 50; }
955 
valid_dir(const vector<char> & ad,int j,cell * c)956 EX bool valid_dir(const vector<char>& ad, int j, cell *c) {
957   bool bch = variation == eVariation::bch;
958   if(!bch) return ad[j] == 1;
959 
960   if(ad[j] != 2) return false;
961   auto ad1 = currentmap->get_cellshape(c).dirdist[j];
962   int a = 0;
963   for(auto& dd: ad1) if(dd == 1) a++;
964 
965   int a0 = 0;
966   for(auto& dd: ad) if(dd == 1) a0++;
967   return a < 6;
968   }
969 
extend3D(cell * c)970 EX void extend3D(cell *c) {
971   eLand l1 = c->land;
972   c->barleft = NOWALLSEP_USED;
973 
974   cellwalker cw(c, c->bardir);
975 
976   if(bufferzone()) {
977     cw += wstep; cw += rev;
978     cw.at->bardir = NOBARRIERS;
979     setland(cw.at, laBarrier);
980     }
981 
982   auto cw1 = cw + wstep;
983   setland(cw1.at, c->barright);
984   if(cw1.at->bardir == NODIR) {
985     cw1.at->barleft = NOWALLSEP_USED;
986     cw1.at->barright = l1;
987     cw1.at->bardir = cw1.spin;
988     }
989 
990   bool bch = variation == eVariation::bch;
991 
992   auto& ad = currentmap->dirdist(cw);
993   for(int j=0; j<cw.at->type; j++) {
994 
995     if(!valid_dir(ad, j, cw.at)) {
996       if(bch && ad[j] == 1) {
997         cell *c1 = cw.at->cmove(j);
998         c1->bardir = NOBARRIERS;
999         setland(c1, c->barright);
1000         }
1001 
1002       if(bch && ad[j] == 2) {
1003         cell *c1 = cw.at->cmove(j);
1004         c1->bardir = NOBARRIERS;
1005         setland(c1, l1);
1006         }
1007 
1008       continue;
1009       }
1010 
1011     cellwalker bb2 = currentmap->strafe(cw, j);
1012     if(bufferzone()) { bb2 += rev; bb2 += wstep; }
1013 
1014     if(bb2.at->bardir == NODIR) {
1015       bb2.at->bardir = bb2.spin;
1016       bb2.at->barleft = NOWALLSEP;
1017       bb2.at->barright = c->barright;
1018       bb2.at->land = l1;
1019       // bb2.at->item = itGold;
1020       extendBarrier(bb2.at);
1021       }
1022     }
1023   }
1024 
1025 bool built = false;
1026 
buildBarrier3D(cell * c,eLand l2,int forced_dir)1027 EX bool buildBarrier3D(cell *c, eLand l2, int forced_dir) {
1028   if(forced_dir == NODIR) {
1029     for(int t=0; t<c->type; t++) if((!c->move(t) || c->move(t)->mpdist > c->mpdist) && buildBarrier3D(c, l2, t)) return true;
1030     return false;
1031     }
1032   bool bch = variation == eVariation::bch;
1033 
1034   cellwalker cw(c, forced_dir);
1035 
1036   if(bch) {
1037     auto& ad = currentmap->dirdist(cw);
1038     int a = 0;
1039     for(auto& dd: ad) if(dd == 1) a++;
1040     if(a == 6) return false;
1041     }
1042 
1043   if(bufferzone()) { cw += wstep; cw += rev; }
1044   set<cell*> listed_cells = { cw.at };
1045   vector<cellwalker> to_test { cw };
1046   int tc = basic_tests();
1047   for(int i=0; i<isize(to_test); i++) {
1048     auto bb = to_test[i];
1049     if(bb.at->mpdist < BARLEV) return false;
1050     if(bb.cpeek()->mpdist < BARLEV) return false;
1051     if(bb.cpeek()->bardir != NODIR) return false;
1052     if(bufferzone() && (bb+rev).cpeek()->mpdist < BARLEV) return false;
1053     if(bufferzone() && (bb+rev).cpeek()->bardir != NODIR) return false;
1054     if(bb.at->bardir != NODIR) return false;
1055     auto& ad = currentmap->dirdist(bb);
1056     for(int j=0; j<bb.at->type; j++) {
1057       if(i < tc) bb.at->cmove(j);
1058       if(!bb.at->move(j)) continue;
1059       if(!valid_dir(ad, j, bb.at)) continue;
1060       cellwalker bb2 = currentmap->strafe(bb, j);
1061       if(listed_cells.count(bb2.at)) continue;
1062       listed_cells.insert(bb2.at);
1063       to_test.push_back(bb2);
1064       }
1065     }
1066 
1067   for(int i=0; i<isize(to_test); i++) {
1068     auto bb = to_test[i];
1069     if(bufferzone()) { bb.at->bardir = NOBARRIERS; setland(bb.at, laBarrier); bb += rev; bb += wstep; }
1070     bb.at->land = c->land;
1071     bb.at->bardir = bb.spin;
1072     bb.at->barleft = NOWALLSEP;
1073     bb.at->barright = l2;
1074     extendBarrier(bb.at);
1075     }
1076 
1077   built = true;
1078   return true;
1079   }
1080 #endif
1081 
buildBarrierNowall(cell * c,eLand l2,int forced_dir IS (NODIR))1082 EX bool buildBarrierNowall(cell *c, eLand l2, int forced_dir IS(NODIR)) {
1083   return general_barrier_build(NOWALLSEP, c, l2, forced_dir);
1084   }
1085 
general_barrier_build(eLand ws,cell * c,eLand l2,int forced_dir IS (NODIR))1086 EX bool general_barrier_build(eLand ws, cell *c, eLand l2, int forced_dir IS(NODIR)) {
1087 
1088   if(S3 >= OINF) { c->land = l2; return true; }
1089 
1090   #if MAXMDIM >= 4
1091   // 3D binary tilings create walls using their own methods
1092   if(WDIM == 3 && bt::in()) return false;
1093 
1094   if(WDIM == 3 && hyperbolic) return buildBarrier3D(c, l2, forced_dir);
1095   #endif
1096 
1097   if(c->land == laNone) {
1098     printf("barrier nowall! [%p]\n", hr::voidp(c));
1099     raiseBuggyGeneration(c, "barrier nowall!");
1100     return false;
1101     }
1102 
1103   bool warpv = warped_version(c->land, l2);
1104   if(warpv && !arcm::in() && !pseudohept(c)) return false;
1105 
1106   vector<int> ds = hrandom_permutation(c->type);
1107 
1108   eLand wsx = warpv ? laWarpCoast : laNone;
1109   eLand l1 = c->land;
1110 
1111   if(forced_dir != NODIR) {
1112     cellwalker cw(c, forced_dir);
1113     general_barrier_check(cw, 20, -1, ws, l1, l2);
1114     general_barrier_check(cw, 20, 1, ws, l1, l2);
1115     extendBarrier(c);
1116     return true;
1117     }
1118 
1119   for(int d: ds) {
1120     if(c->move(d) && c->move(d)->mpdist <= c->mpdist) continue;
1121     cellwalker cw(c, d);
1122     if(general_barrier_check(cw, 20, -1, ws, wsx, wsx) && general_barrier_check(cw, 20, 1, ws, wsx, wsx)) {
1123       general_barrier_check(cw, 20, -1, ws, l1, l2);
1124       general_barrier_check(cw, 20, 1, ws, l1, l2);
1125       extendBarrier(c);
1126       return true;
1127       }
1128     }
1129 
1130   return false;
1131   }
1132 
1133 }
1134