From 6d70458aaf895bf9a67d80786c55413359ea5316 Mon Sep 17 00:00:00 2001 From: max Date: Thu, 28 Jul 2022 21:08:49 +0300 Subject: [PATCH] refactor(generation): cultures continue --- src/scripts/generation/pack/cultures.ts | 52 +++++++++++++++++-------- src/utils/colorUtils.ts | 4 +- src/utils/probabilityUtils.ts | 14 +++++-- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/scripts/generation/pack/cultures.ts b/src/scripts/generation/pack/cultures.ts index 9f6abae5..9814e05d 100644 --- a/src/scripts/generation/pack/cultures.ts +++ b/src/scripts/generation/pack/cultures.ts @@ -40,6 +40,7 @@ export const generateCultures = function ( const cultureIds = new Uint16Array(cells.i.length); // cell cultures const populatedCellIds = cells.i.filter(cellId => cells.pop[cellId] > 0); + const maxSuitability = d3.max(cells.s)!; const culturesNumber = getCulturesNumber(populatedCellIds.length); if (!culturesNumber) return {cultureIds, cultures: [wildlands]}; @@ -117,12 +118,12 @@ export const generateCultures = function ( function selectCulturesData(culturesNumber: number) { let defaultCultures = getDefault(culturesNumber); - if (defaultCultures.length >= culturesNumber) return defaultCultures; + if (defaultCultures.length <= culturesNumber) return defaultCultures; - const culturesAvailable = Math.min(culturesNumber, defaultCultures.length); const cultures = []; + const MAX_ITERATIONS = 200; - for (let culture, rnd, i = 0; cultures.length < culturesAvailable && i < 200; i++) { + for (let culture, rnd, i = 0; cultures.length < culturesNumber && i < MAX_ITERATIONS; i++) { do { rnd = rand(defaultCultures.length - 1); culture = defaultCultures[rnd]; @@ -135,34 +136,59 @@ export const generateCultures = function ( } function placeCenter(sortingString: string) { + let spacing = (graphWidth + graphHeight) / 2 / culturesNumber; + + const sorted = sortPopulatedCellByCultureSuitability(sortingString); + const MAX = Math.floor(sorted.length / 2); + const BIAS_EXPONENT = 6; + + return (function getCellId(): number { + const cellId = sorted[biased(0, MAX, BIAS_EXPONENT)]; + if (centers.find(...cells.p[cellId], spacing) !== undefined) { + // to close to another center, try again with reduced spacing + spacing *= 0.9; + return getCellId(); // call recursively + } + + return cellId; + })(); + } + + function sortPopulatedCellByCultureSuitability(sortingString: string) { let cellId: number; - const sMax = d3.max(cells.s)!; - const sortingMethods = { - n: () => Math.ceil((cells.s[cellId] / sMax) * 3), // normalized cell score + n: () => Math.ceil((cells.s[cellId] / maxSuitability) * 3), // normalized cell score + td: (goalTemp: number) => { const tempDelta = Math.abs(temp[cells.g[cellId]] - goalTemp); return tempDelta ? tempDelta + 1 : 1; }, + bd: (biomes: number[], fee = 4) => { return biomes.includes(cells.biome[cellId]) ? 1 : fee; }, + sf: (fee = 4) => { const haven = cells.haven[cellId]; const havenHeature = features[haven]; return haven && havenHeature && havenHeature.type !== "lake" ? 1 : fee; }, + t: () => cells.t[cellId], + h: () => cells.h[cellId], + s: () => cells.s[cellId] }; + const allSortingMethods = `{${Object.keys(sortingMethods).join(", ")}}`; const sortFn = new Function(allSortingMethods, "return " + sortingString); + const comparator = (a: number, b: number) => { cellId = a; - const cellA = sortFn({...sortingMethods}); + const cellA = sortFn(sortingMethods); cellId = b; const cellB = sortFn(sortingMethods); @@ -170,15 +196,8 @@ export const generateCultures = function ( return cellB - cellA; }; - let spacing = (graphWidth + graphHeight) / 2 / culturesNumber; const sorted = Array.from(populatedCellIds).sort(comparator); - const max = Math.floor(sorted.length / 2); - - do { - cellId = sorted[biased(0, max, 5)]; - spacing *= 0.9; - } while (centers.find(...cells.p[cellId], spacing) !== undefined); - return cellId; + return sorted; } // set culture type based on culture center position @@ -230,7 +249,8 @@ export const generateCultures = function ( } // check if base is in nameBases - if (base > nameBases.length) return base; + if (base < nameBases.length) return base; + ERROR && console.error(`Name base ${base} is not available, applying a fallback one`); return base % nameBases.length; } diff --git a/src/utils/colorUtils.ts b/src/utils/colorUtils.ts index 8b36878c..bdd05c93 100644 --- a/src/utils/colorUtils.ts +++ b/src/utils/colorUtils.ts @@ -25,14 +25,16 @@ const colorSchemeMap: Dict = { }; export function getColors(number: number) { - const scheme = colorSchemeMap.bright; + if (number <= cardinal12.length) return d3.shuffle(cardinal12.slice(0, number)); + const scheme = colorSchemeMap.bright; const colors = d3.range(number).map(index => { if (index < 12) return cardinal12[index]; const rgb = scheme((index - 12) / (number - 12))!; return d3.color(rgb)!.formatHex() as Hex; }); + return d3.shuffle(colors); } diff --git a/src/utils/probabilityUtils.ts b/src/utils/probabilityUtils.ts index 37483659..08695bdc 100644 --- a/src/utils/probabilityUtils.ts +++ b/src/utils/probabilityUtils.ts @@ -49,9 +49,17 @@ export function rw(object: {[key: string]: number}) { return ra(weightedArray); } -// return a random integer from min to max biased towards one end based on exponent distribution (the bigger ex the higher bias towards min) -export function biased(min: number, max: number, ex: number) { - return Math.round(min + (max - min) * Math.pow(Math.random(), ex)); +// return a random integer from min to max biased towards one end based on exponent distribution +// the bigger exponent the higher bias towards min +// biased(0, 10, 10): {0: 74%, 1: 9%, 2: 4%, 3: 3%, 4: 2%, 5: 2%, 6: 2%, 7: 1%, 8: 1%, 9: 1%, 10: 0%} +// biased(0, 10, 5): {0: 55%, 1: 14%, 2: 7%, 3: 5%, 4: 4%, 5: 4%, 6: 3%, 7%: 3%, 8: 2%, 9: 2%, 10: 1%} +// biased(0, 10, 4): {0: 46%, 1: 15%, 2: 8%, 3: 6%, 4: 5%, 5: 4%, 6: 4%, 7%: 3, 8: 3%, 9: 3%, 10: 1%} +// biased(0, 10, 3): {0: 36%, 1: 16%, 2: 10%, 3: 8%, 4: 6%, 5: 5%, 6: 5%, 7%: 4, 8: 4%, 9: 4%, 10: 2%} +// biased(0, 10, 2): {0: 22%, 1: 17%, 2: 11%, 3: 9%, 4: 8%, 5: 7%, 6: 6%, 7%: 6, 8: 6%, 9: 5%, 10: 2%} +// biased{0, 10, 1): {0: 5%, 1: 10%, 2: 10%, 3: 10%, 4: 10%, 5: 10%, 6: 10%, 7%: 10, 8%: 10, 9: 10%, 10: 5%} +export function biased(min: number, max: number, exponent: number) { + if (exponent <= 1) throw new Error("Exponent must be greater than 1"); + return Math.round(min + (max - min) * Math.pow(Math.random(), exponent)); } // get number from string in format "1-3" or "2" or "0.5"