complete rewrite of burg relocation

This commit is contained in:
GoteGuru 2022-04-09 13:05:18 +00:00
parent 711653e115
commit a84ccf062e

View file

@ -317,28 +317,36 @@ window.Submap = (function () {
/* find the nearest cell accepted by filter f *and* having at /* find the nearest cell accepted by filter f *and* having at
* least one *neighbor* fulfilling filter g, up to cell-distance `max` * least one *neighbor* fulfilling filter g, up to cell-distance `max`
* returns [cellid, neighbor] tuple or undefined if no such cell. * 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 findNearest = (f, g, max=3) => pointLike => {
const met = new Set(); // cache, f might be expensive const d2 = c => (pointLike.x-pack.cells.p[c][0])**2 + (pointLike.y-pack.cells.p[c][0])**2
const kernel = (c, dist) => { const startCell = findCell(pointLike.x, pointLike.y);
const ncs = pack.cells.c[c].filter(nc => !met.has(nc)); const tested = new Set([startCell]); // ignore analyzed cells
const n = ncs.find(g); const kernel = (cs, level) => {
if (f(c) && n) return [c, n]; const [bestf, bestg] = cs.filter(f).reduce(([cf, cg], c) => {
if (dist >= max || !ncs.length) return undefined; const neighbors = pack.cells.c[c];
met.add(c); const bestg = neighbors.filter(g).reduce((u, x) => d2(c)<d2(u)? c:x);
const targets = ncs.filter(f) if (cf === undefined) return [c, bestg];
let answer; return (bestg && d2(cf) < d2(c))? [c, bestg]: [cf, cg];
while (targets.length && !answer) answer = kernel(targets.shift(), dist+1); }, [undefined, undefined]);
return answer; if (bestf && bestg) return [bestf, bestg];
// no suitable pair found, retry with next ring
const targets = new Set(cs.map(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) { function copyBurgs(parentMap, projection, options) {
const cells = pack.cells; const cells = pack.cells;
const childMap = { grid, pack } 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; pack.burgs = parentMap.pack.burgs;
// remap burgs to the best new cell // remap burgs to the best new cell
@ -353,55 +361,36 @@ window.Submap = (function () {
return; 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); // check if we need to relocate the burg
const searchFreeCell = findNearest(c => !cells.burg[c], _=>true, 3); if (cells.burg[cityCell]) // already occupied
searchFunc = findNearest(isFreeLand, _ => true, 3);
// pull sunken burgs out of water if (isWater(childMap, cityCell) || b.port) // burg is in water or port
if (isWater(childMap, cityCell)) { searchFunc = findNearest(isFreeLand, nearCoast, 6);
const res = searchCoastCell(cityCell)
if (!res) { if (searchFunc) {
WARN && console.warn(`Burg ${b.name} sank like Atlantis. Unable to find coastal cells nearby. Try to reduce resample zoom level.`); const [newCell, neighbor] = searchFunc(b);
if (!newCell) {
WARN && console.warn(`Can not relocate Burg: ${b.name} sunk and destroyed. :-(`);
b.cell = null; b.cell = null;
b.removed = true; b.removed = true;
return; return;
} }
const [coast, water] = res; DEBUG && console.log(`Moving ${b.name} from ${cityCell} to ${newCell} near ${neighbor}.`);
[b.x, b.y] = b.port? getMiddlePoint(coast, water): cells.p[coast]; [b.x, b.y] = b.port? getMiddlePoint(newCell, neighbor): cells.p[newCell];
if (b.port) b.port = cells.f[water]; if (b.port) b.port = cells.f[neighbor]; // copy feature number
b.cell = coast; if (b.port && !isWater(childMap, neighbor)) console.error('betrayal! negihbor must be water!', b);
} 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;
b.cell = newCell; b.cell = newCell;
[b.x, b.y] = cells.p[newCell];
} else { } else {
b.cell = cityCell; b.cell = cityCell;
[b.x, b.y] = cells.p[cityCell];
} }
if (!b.lock) b.lock = options.lockBurgs; if (!b.lock) b.lock = options.lockBurgs;
pack.cells.burg[b.cell] = id; cells.burg[b.cell] = id;
}); });
} }