From a84ccf062e77009f6a9824e57055805129a40f36 Mon Sep 17 00:00:00 2001 From: GoteGuru Date: Sat, 9 Apr 2022 13:05:18 +0000 Subject: [PATCH] complete rewrite of burg relocation --- modules/submap.js | 95 +++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 53 deletions(-) diff --git a/modules/submap.js b/modules/submap.js index 2d1c8c8b..6b67ee79 100644 --- a/modules/submap.js +++ b/modules/submap.js @@ -317,28 +317,36 @@ window.Submap = (function () { /* find the nearest cell accepted by filter f *and* having at * least one *neighbor* fulfilling filter g, up to cell-distance `max` * returns [cellid, neighbor] tuple or undefined if no such cell. + * accepts pointLike (object having .x and .y) */ - const findNearest = (f, g, max=3) => centerId => { - const met = new Set(); // cache, f might be expensive - const kernel = (c, dist) => { - const ncs = pack.cells.c[c].filter(nc => !met.has(nc)); - const n = ncs.find(g); - if (f(c) && n) return [c, n]; - if (dist >= max || !ncs.length) return undefined; - met.add(c); - const targets = ncs.filter(f) - let answer; - while (targets.length && !answer) answer = kernel(targets.shift(), dist+1); - return answer; + const findNearest = (f, g, max=3) => pointLike => { + const d2 = c => (pointLike.x-pack.cells.p[c][0])**2 + (pointLike.y-pack.cells.p[c][0])**2 + const startCell = findCell(pointLike.x, pointLike.y); + const tested = new Set([startCell]); // ignore analyzed cells + const kernel = (cs, level) => { + const [bestf, bestg] = cs.filter(f).reduce(([cf, cg], c) => { + const neighbors = pack.cells.c[c]; + const bestg = neighbors.filter(g).reduce((u, x) => d2(c) pack.cells.c[c]).flat()) + const ring = Array.from(targets).filter(nc => !tested.has(nc)); + if (level >= max || !ring.length) + return [undefined, undefined]; + ring.forEach(c => tested.add(c)); + return kernel(ring, level+1); } - return kernel(centerId, 1); + const pair = kernel([startCell], 1); + return pair; } function copyBurgs(parentMap, projection, options) { const cells = pack.cells; const childMap = { grid, pack } - const isCoastFree = c => cells.t[c] === 1 && !cells.burg[c] - const isNearCoast = c => cells.t[c] === -1 pack.burgs = parentMap.pack.burgs; // remap burgs to the best new cell @@ -353,55 +361,36 @@ window.Submap = (function () { return; } - let cityCell = findCell(b.x, b.y); + const cityCell = findCell(b.x, b.y); + let searchFunc; + const isFreeLand = c => cells.t[c] === 1 && !cells.burg[c]; + const nearCoast = c => cells.t[c] === -1; - const searchCoastCell = findNearest(isCoastFree, isNearCoast, 6); - const searchFreeCell = findNearest(c => !cells.burg[c], _=>true, 3); + // check if we need to relocate the burg + if (cells.burg[cityCell]) // already occupied + searchFunc = findNearest(isFreeLand, _ => true, 3); - // pull sunken burgs out of water - if (isWater(childMap, cityCell)) { - const res = searchCoastCell(cityCell) - if (!res) { - WARN && console.warn(`Burg ${b.name} sank like Atlantis. Unable to find coastal cells nearby. Try to reduce resample zoom level.`); + if (isWater(childMap, cityCell) || b.port) // burg is in water or port + searchFunc = findNearest(isFreeLand, nearCoast, 6); + + if (searchFunc) { + const [newCell, neighbor] = searchFunc(b); + if (!newCell) { + WARN && console.warn(`Can not relocate Burg: ${b.name} sunk and destroyed. :-(`); b.cell = null; b.removed = true; return; } - const [coast, water] = res; - [b.x, b.y] = b.port? getMiddlePoint(coast, water): cells.p[coast]; - if (b.port) b.port = cells.f[water]; - b.cell = coast; - } else if (b.port) { - // find coast for ports on land - const res = searchCoastCell(cityCell); - if (res) { - const [coast, water] = res; - [b.x, b.y] = getMiddlePoint(coast, water); - b.port = cells.f[water]; // copy feature number - b.cell = coast; - } else { - WARN && console.warn(`Can't find water near port ${b.name}. Increase search radius in searchCoastCell. (Removing port status)`); - b.cell = cityCell; - [b.x, b.y] = cells.p[cityCell]; - b.port = 0; - } - } else if (cells.burg[b.cell]) { // already occupied - const res = searchFreeCell(cityCell); - if (!res) { - WARN && console.warn(`No space left for ${b.name}. Removed.`); - b.cell = null; - b.removed = true; - return; - } - const [newCell, _] = res; + DEBUG && console.log(`Moving ${b.name} from ${cityCell} to ${newCell} near ${neighbor}.`); + [b.x, b.y] = b.port? getMiddlePoint(newCell, neighbor): cells.p[newCell]; + if (b.port) b.port = cells.f[neighbor]; // copy feature number + if (b.port && !isWater(childMap, neighbor)) console.error('betrayal! negihbor must be water!', b); b.cell = newCell; - [b.x, b.y] = cells.p[newCell]; } else { b.cell = cityCell; - [b.x, b.y] = cells.p[cityCell]; } if (!b.lock) b.lock = options.lockBurgs; - pack.cells.burg[b.cell] = id; + cells.burg[b.cell] = id; }); }