From 77e5efa28fbaabe149d5f5a886fa0ea123f6254f Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Thu, 14 Sep 2023 15:06:39 +0200 Subject: [PATCH] ideal Voronoi implemented --- bigstuff.cpp | 174 ++++++++++++++++++++++++++++++++++++++++++++++++--- complex.cpp | 3 +- landgen.cpp | 12 ++-- landlock.cpp | 22 ++++--- 4 files changed, 185 insertions(+), 26 deletions(-) diff --git a/bigstuff.cpp b/bigstuff.cpp index bdd5ee29..c6def972 100644 --- a/bigstuff.cpp +++ b/bigstuff.cpp @@ -219,6 +219,141 @@ void hrmap::extend_altmap(heptagon *h, int levs, bool link_cdata) { } } +void new_voronoi_root(heptagon *h, int dist, int dir, eLand last, eLand last2) { + heptagon *alt = init_heptagon(h->type); + allmaps.push_back(newAltMap(alt)); + alt->s = hsA; + alt->alt = alt; + alt->cdata = (cdata*) h; + alt->distance = dist; + h->alt = alt; + altmap::relspin(alt) = dir; + + horodisk_land[alt] = getNewLand(last, last2); + horodisk_last_land[alt] = last; + + while(alt->distance > -100) { + auto alt1 = createStep(alt, 0); + alt1->alt = alt->alt; + auto h1 = createStep(h, dir); + h1->alt = alt1; + + auto dir_alt = alt->c.spin(0); + auto dir_h = h->c.spin(dir); + + dir = altmap::relspin(alt1) = gmod(dir_h - dir_alt, alt->type); + + h = h1; alt = alt1; + } + } + +struct cand_info { + int best, bqty; + heptagon *candidate; + vector free_dirs; + }; + +cand_info voronoi_candidate(heptagon *h) { + cand_info ci; + ci.best = 999999; ci.bqty = 0; + + for(int i=0; itype; i++) { + heptagon *ho = createStep(h, i); + int ri = h->c.spin(i); + auto hoa = ho->alt; + if(hoa && hoa->alt) { + auto relspin = altmap::relspin(hoa); + /* we want ho->move(ri) which is hoa->move(ri - relspin) */ + int dir = gmod(ri - relspin, hoa->type); + heptagon *hoa1 = createStep(hoa, dir); + if(!hoa1->alt) hoa1->alt = hoa->alt; + auto dist = hoa1->distance; + if(dist < ci.best) { + ci.best = dist; + ci.bqty = 0; + } + if(dist == ci.best && ci.candidate != hoa1) { + ci.bqty++; + ci.candidate = hoa1; + int rb = hoa->c.spin(dir); + /* hoa1->alt->move(rb) is h->move(rb+relspin to compute) */ + altmap::relspin(ci.candidate) = gmod(i - rb, ci.candidate->type); + } + } + else ci.free_dirs.push_back(i); + } + return ci; + } + +void extend_altmap_voronoi(heptagon *h) { + if(h->alt) return; + + auto ci = voronoi_candidate(h); + + if(ci.bqty == 0) { + new_voronoi_root(h, -30, hrand(h->type), laBarrier, laBarrier); + return; + } + else if(ci.bqty > 0 && isize(ci.free_dirs)) { + auto& expansion = get_expansion(); + ld growth = expansion.get_growth(); + ld odds = pow(growth, ci.candidate->distance) * isize(ci.free_dirs); + if(hrandf() < odds / (1 + odds)) { + new_voronoi_root(h, ci.candidate->distance - 1, hrand_elt(ci.free_dirs), horodisk_land[ci.candidate->alt], horodisk_last_land[ci.candidate->alt]); + return; + } + } + + h->alt = ci.candidate; + } + +EX pair get_voronoi_winner(cell *c) { + if(c == c->master->c7) { + extend_altmap_voronoi(c->master); + auto ci = voronoi_candidate(c->master); + if(ci.bqty == 1) return { ci.candidate->alt, ci.best }; + else return { nullptr, ci.best }; + } + else if(!BITRUNCATED) return get_voronoi_winner(c->master->c7); + else { + vector nearh; + for(int i=0; itype; i++) { + c->cmove(i); + if(c->move(i)->master->c7 == c->move(i)) nearh.push_back(c->move(i)->master); + } + for(auto h: nearh) extend_altmap_voronoi(h); + for(auto h: nearh) if(!h->alt) return { nullptr, 0 }; + pair best = {nullptr, 999999}; + int bqty = 0; + for(auto h: nearh) { + vector dists; + for(auto h1: nearh) { + if(h == h1) dists.push_back(h->alt->distance); + else { + for(int i=0; itype; i++) if(h->cmove(i) == h1) { + auto ha1 = createStep(h->alt, gmod(i - altmap::relspin(h->alt), h->type)); + dists.push_back(ha1->distance); + } + } + } + sort(dists.begin(), dists.end()); + int gdist; + if(dists.back() == dists[0]) gdist = dists[0] - 1; + else if(dists.back() == dists[0] + 2) gdist = dists[0] + 1; + else gdist = dists[0]; + if(gdist < best.second) { + best.second = gdist; bqty = 0; + } + if(gdist == best.second) { + bqty++; + if(bqty == 1 || best.first == h->alt->alt) best.first = h->alt->alt; + else best.first = nullptr; + } + } + return best; + } + } + #if MAXMDIM >= 4 EX int hrandom_adjacent(cellwalker cw) { auto& da = currentmap->dirdist(cw); @@ -1662,6 +1797,7 @@ EX void start_camelot(cell *c) { } EX map horodisk_land; +EX map horodisk_last_land; EX void build_horocycles(cell *c, cell *from) { @@ -1674,7 +1810,7 @@ EX void build_horocycles(cell *c, cell *from) { // buildbigstuff - if(ls::any_order() && bearsCamelot(c->land) && can_start_horo(c) && !bt::in() && + if(ls::any_order() && bearsCamelot(c->land) && can_start_horo(c) && !bt::in() && !ls::voronoi_structure() && #if MAXMDIM >= 4 !(hyperbolic && WDIM == 3 && !reg3::in_hrmap_rule_or_subrule()) && #endif @@ -1978,6 +2114,19 @@ EX void moreBigStuff(cell *c) { c->wall = waSea; } + if(ls::voronoi_structure()) { + auto p = get_voronoi_winner(c); + auto ph = p.first; + if(ph) { + eLand l = horodisk_land[ph]; + setland(c, l); + if(isEquidLand(l)) c->landparam = 1-p.second; + } + else { + setland(c, laBarrier); + } + } + if(ls::horodisk_structure()) { if(have_alt(c) && masterAlt(c) <= 0) { gen_alt(c); @@ -1997,11 +2146,11 @@ EX void moreBigStuff(cell *c) { setland(c, laCrossroads); } - extend_alt(c, laPalace, laPalace, false, PRADIUS1); + if(!ls::hv_structure()) extend_alt(c, laPalace, laPalace, false, PRADIUS1); extend_alt(c, laCanvas, laCanvas); - if(extend_alt(c, laStorms, laStorms, false) && !ls::hv_structure()) { + if(!ls::hv_structure() && extend_alt(c, laStorms, laStorms, false)) { int d = celldistAlt(c); if(d <= -2) { c->wall = eubinary ? waCharged : (altmap::which(c->master->alt->alt) & 1) ? waCharged : waGrounded; @@ -2020,12 +2169,12 @@ EX void moreBigStuff(cell *c) { c->wall = waColumn; } - else if(extend_alt(c, laTemple, laRlyeh) && !ls::hv_structure()) + else if(!ls::hv_structure() && extend_alt(c, laTemple, laRlyeh)) gen_temple(c); - if(c->land == laTemple && ls::hv_structure()) gen_temple(c); + if(ls::hv_structure() && c->land == laTemple) gen_temple(c); - if(extend_alt(c, laClearing, laOvergrown) && !ls::hv_structure()) { + if(!ls::hv_structure() && extend_alt(c, laClearing, laOvergrown)) { if(in_single_horo(c, laClearing)) { c->land = laClearing, c->wall = waNone; } @@ -2033,16 +2182,21 @@ EX void moreBigStuff(cell *c) { c->wall = waSmallTree, c->monst = moNone, c->item = itNone, c->landparam = 1; } - if(c->land == laClearing && ls::hv_structure()) { + if(ls::horodisk_structure() && c->land == laClearing) { if(celldistAlt(c) >= -1) c->wall = waSmallTree, c->monst = moNone, c->item = itNone, c->landparam = 1; - } + } - if(extend_alt(c, laMountain, laJungle) && in_single_horo(c, laMountain) && !ls::hv_structure()) { + if(ls::voronoi_structure() && c->land == laClearing) { + if(celldistAlt(c) == -20) + c->wall = waSmallTree, c->monst = moNone, c->item = itNone, c->landparam = 1; + } + + if(!ls::hv_structure() && extend_alt(c, laMountain, laJungle) && in_single_horo(c, laMountain)) { c->land = laMountain, c->wall = waNone; } - if(!ls::horodisk_structure() && extend_alt(c, laWhirlpool, laOcean) && in_single_horo(c, laWhirlpool)) + if(!ls::hv_structure() && extend_alt(c, laWhirlpool, laOcean) && in_single_horo(c, laWhirlpool)) c->land = laWhirlpool, c->wall = waSea, c->monst = moNone, c->item = itNone; } diff --git a/complex.cpp b/complex.cpp index bc8049b1..77bcae59 100644 --- a/complex.cpp +++ b/complex.cpp @@ -908,7 +908,8 @@ EX namespace clearing { return; } - if(c->land == laClearing && ls::hv_structure() && celldistAlt(c) >= -1) return; + if(c->land == laClearing && ls::horodisk_structure() && celldistAlt(c) >= -1) return; + if(c->land == laClearing && ls::voronoi_structure() && celldistAlt(c) >= -20) return; if(!eubinary && !horo_ok()) return; // cell *oc = c; diff --git a/landgen.cpp b/landgen.cpp index 9437c9af..56d25590 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -276,7 +276,7 @@ EX void place_random_gate_continuous(cell *c) { } EX void giantLandSwitch(cell *c, int d, cell *from) { - bool fargen = d == min(BARLEV, ls::horodisk_structure() ? 8 : 9); + bool fargen = d == min(BARLEV, ls::hv_structure() ? 8 : 9); switch(c->land) { case laPrairie: // ------------------------------------------------------------- @@ -405,7 +405,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { } else { - if(d == (ls::horodisk_structure() ? 8 : 9)) { + if(d == (ls::hv_structure() ? 8 : 9)) { cell *c2 = NONSTDVAR ? c->master->c7 : c; if(cdist50(c2) == 3 && polarb50(c2)) c->wall = waPalace; @@ -453,7 +453,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { } // note: Princess Challenge brings back the normal Palace generation - bool lookingForPrincess = !euclid && c->master->alt && !princess::challenge && !ls::horodisk_structure(); + bool lookingForPrincess = !euclid && c->master->alt && !princess::challenge && !ls::hv_structure(); bool pgate = false; if(PURE || GOLDBERG) { @@ -551,7 +551,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { } ONEMPTY { - bool lookingForPrincess0 = !euclid && c->master->alt && !ls::horodisk_structure(); + bool lookingForPrincess0 = !euclid && c->master->alt && !ls::hv_structure(); bool lookingForPrincess = lookingForPrincess0 && !princess::challenge; int hardness = lookingForPrincess ? 5 : items[itPalace] + yendor::hardness(); @@ -2090,7 +2090,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { if(d >= 8) c->wall = waSea; if(d == 7 && !safety) { - if(ls::horodisk_structure() && c->master->alt && horodisk_land[c->master->alt->alt] == laWhirlpool) { + if(ls::hv_structure() && c->master->alt && horodisk_land[c->master->alt->alt] == laWhirlpool) { if(hrand(100) < 10) c->wall = waBoat; return; } @@ -2267,7 +2267,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { case laClearing: if(d == 7) { clearing::generate(c); - if(ls::horodisk_structure() && celldistAlt(c) >= -1) c->monst = moNone; + if(ls::hv_structure() && celldistAlt(c) >= -1) c->monst = moNone; if(pseudohept(c)) { int d = -celldistAlt(c); if(hrand_monster(2500) < items[itMutant2] + yendor::hardness() - 10 && !reptilecheat) diff --git a/landlock.cpp b/landlock.cpp index fb903721..72d0816b 100644 --- a/landlock.cpp +++ b/landlock.cpp @@ -99,6 +99,7 @@ EX bool any_order() { return among(land_structure, lsNiceWalls, lsNoWalls, lsHor EX bool nice_walls() { return land_structure == lsNiceWalls; } EX bool no_walls() { return land_structure == lsNoWalls; } EX bool horodisk_structure() { return land_structure == lsHorodisks; } +EX bool voronoi_structure() { return land_structure == lsVoronoi; } EX bool hv_structure() { return among(land_structure, lsHorodisks, lsVoronoi); } EX bool any_nowall() { return no_walls() || std_chaos(); } @@ -137,7 +138,7 @@ EX string land_structure_name(bool which) { case lsHorodisks: return XLAT("horodisks"); case lsVoronoi: - return XLAT("limit Voronoi"); + return XLAT("ideal Voronoi"); case lsNoWalls: return XLAT("wall-less"); default: @@ -158,7 +159,7 @@ EX void fix_land_structure_choice() { land_structure = lsNoWalls; if(!nice_walls_available() && land_structure == lsWallChaos) land_structure = lsChaos; - if(ls::hv_structure() && !hyperbolic) + if(ls::hv_structure() && (!hyperbolic || bt::in() || quotient)) land_structure = lsSingle; if(walls_not_implemented() && among(land_structure, lsChaos, lsNoWalls)) land_structure = lsSingle; @@ -382,7 +383,7 @@ EX bool all_unlocked = false; EX vector cheatdest_list; -EX eLand getNewLand(eLand old) { +EX eLand getNewLand(eLand old, eLand old2 IS(laBarrier)) { #if CAP_LEGACY if(legacy_racing()) { @@ -394,7 +395,7 @@ EX eLand getNewLand(eLand old) { eLand l = callhandlers(laNone, hooks_nextland, old); if(l) return l; - if(cheatdest != old && cheatdest != laElementalWall) if(!isCyclic(cheatdest) && !isTechnicalLand(cheatdest)) return cheatdest; + if(cheatdest != old && cheatdest != old2 && cheatdest != laElementalWall) if(!isCyclic(cheatdest) && !isTechnicalLand(cheatdest)) return cheatdest; if(cheatdest_list.size()) { eLand l = cheatdest_list[0]; @@ -408,7 +409,9 @@ EX eLand getNewLand(eLand old) { while(true) { eLand n = eLand(hrand(landtypes)); if(n == old) continue; + if(n == old2) continue; if(incompatible(n,old)) continue; + if(incompatible(n,old2)) continue; if(!isLandIngame(n)) continue; if(n == laElementalWall || isTechnicalLand(n)) continue; if(n == laWildWest) continue; @@ -420,9 +423,9 @@ EX eLand getNewLand(eLand old) { if(markOrb(itOrbLuck)) { int i = items[itOrbLuck]; items[itOrbLuck] = 0; - eLand l1 = getNewLand(old); + eLand l1 = getNewLand(old, old2); for(int i=1; i<3; i++) - l1 = pickluck(l1, getNewLand(old)); + l1 = pickluck(l1, getNewLand(old, old2)); items[itOrbLuck] = i; return l1; } @@ -446,9 +449,9 @@ EX eLand getNewLand(eLand old) { #endif if(tactic::on) return specialland; - if(specialland != old && easy_to_find_specialland && specialland != laElementalWall) return specialland; + if(specialland != old && specialland != old2 && easy_to_find_specialland && specialland != laElementalWall) return specialland; - if(specialland != old && easy_specialland && specialland != laElementalWall) { + if(specialland != old && specialland != old2 && easy_specialland && specialland != laElementalWall) { easy_specialland--; return specialland; } @@ -858,7 +861,8 @@ EX land_validity_t& land_validity(eLand l) { if(l == laMirrorOld && !shmup::on) return not_implemented; } - if(ls::hv_structure() && among(l, laPrairie, laIvoryTower, laDungeon, laEndorian, laBrownian, laTortoise, laElementalWall)) return not_in_hv; + if(ls::hv_structure() && among(l, laPrairie, laIvoryTower, laDungeon, laEndorian, laBrownian, laTortoise, laElementalWall, laWarpCoast, laWarpSea, laHive)) return not_in_hv; + if(ls::voronoi_structure() && among(l, laCamelot, laWhirlpool, laClearing)) return not_in_hv; if(l == laBrownian) { if(quotient || !hyperbolic || cryst) return dont_work;