minmax util function

This commit is contained in:
Azgaar 2021-10-10 17:59:27 +03:00
parent b202a58db9
commit bdc0c9f8ee
13 changed files with 54 additions and 44 deletions

View file

@ -954,7 +954,7 @@ function calculateTemperatures() {
const lat = Math.abs(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT); // [0; 90] const lat = Math.abs(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT); // [0; 90]
const initTemp = tEq - int(lat / 90) * tDelta; const initTemp = tEq - int(lat / 90) * tDelta;
for (let i = r; i < r + grid.cellsX; i++) { for (let i = r; i < r + grid.cellsX; i++) {
cells.temp[i] = Math.max(Math.min(initTemp - convertToFriendly(cells.h[i]), 127), -128); cells.temp[i] = minmax(initTemp - convertToFriendly(cells.h[i]), -128, 127);
} }
}); });
@ -1069,7 +1069,7 @@ function generatePrecipitation() {
const precipitation = isPassable ? getPrecipitation(humidity, current, next) : humidity; const precipitation = isPassable ? getPrecipitation(humidity, current, next) : humidity;
cells.prec[current] += precipitation; cells.prec[current] += precipitation;
const evaporation = precipitation > 1.5 ? 1 : 0; // some humidity evaporates back to the atmosphere const evaporation = precipitation > 1.5 ? 1 : 0; // some humidity evaporates back to the atmosphere
humidity = isPassable ? Math.min(Math.max(humidity - precipitation + evaporation, 0), maxPrec) : 0; humidity = isPassable ? minmax(humidity - precipitation + evaporation, 0, maxPrec) : 0;
} }
} }
} }
@ -1078,7 +1078,7 @@ function generatePrecipitation() {
const normalLoss = Math.max(humidity / (10 * modifier), 1); // precipitation in normal conditions const normalLoss = Math.max(humidity / (10 * modifier), 1); // precipitation in normal conditions
const diff = Math.max(cells.h[i + n] - cells.h[i], 0); // difference in height const diff = Math.max(cells.h[i + n] - cells.h[i], 0); // difference in height
const mod = (cells.h[i + n] / 70) ** 2; // 50 stands for hills, 70 for mountains const mod = (cells.h[i + n] / 70) ** 2; // 50 stands for hills, 70 for mountains
return Math.min(Math.max(normalLoss + diff * mod, 1), humidity); return minmax(normalLoss + diff * mod, 1, humidity);
} }
void (function drawWindDirection() { void (function drawWindDirection() {

View file

@ -416,7 +416,7 @@ window.BurgsAndStates = (function () {
function getRiverCost(r, i, type) { function getRiverCost(r, i, type) {
if (type === "River") return r ? 0 : 100; // penalty for river cultures if (type === "River") return r ? 0 : 100; // penalty for river cultures
if (!r) return 0; // no penalty for others if there is no river if (!r) return 0; // no penalty for others if there is no river
return Math.min(Math.max(cells.fl[i] / 10, 20), 100); // river penalty from 20 to 100 based on flux return minmax(cells.fl[i] / 10, 20, 100); // river penalty from 20 to 100 based on flux
} }
function getTypeCost(t, type) { function getTypeCost(t, type) {
@ -606,15 +606,15 @@ window.BurgsAndStates = (function () {
if (pathLength < s.name.length) { if (pathLength < s.name.length) {
// only short name will fit // only short name will fit
lines = splitInTwo(s.name); lines = splitInTwo(s.name);
ratio = Math.max(Math.min(rn((pathLength / lines[0].length) * 60), 150), 50); ratio = minmax(rn((pathLength / lines[0].length) * 60), 50, 150);
} else if (pathLength > s.fullName.length * 2.5) { } else if (pathLength > s.fullName.length * 2.5) {
// full name will fit in one line // full name will fit in one line
lines = [s.fullName]; lines = [s.fullName];
ratio = Math.max(Math.min(rn((pathLength / lines[0].length) * 70), 170), 70); ratio = minmax(rn((pathLength / lines[0].length) * 70), 70, 170);
} else { } else {
// try miltilined label // try miltilined label
lines = splitInTwo(s.fullName); lines = splitInTwo(s.fullName);
ratio = Math.max(Math.min(rn((pathLength / lines[0].length) * 60), 150), 70); ratio = minmax(rn((pathLength / lines[0].length) * 60), 70, 150);
} }
// prolongate path if it's too short // prolongate path if it's too short
@ -665,7 +665,7 @@ window.BurgsAndStates = (function () {
example.text(name); example.text(name);
const left = example.node().getBBox().width / -2; // x offset const left = example.node().getBBox().width / -2; // x offset
el.innerHTML = `<tspan x="${left}px">${name}</tspan>`; el.innerHTML = `<tspan x="${left}px">${name}</tspan>`;
ratio = Math.max(Math.min(rn((pathLength / name.length) * 60), 130), 40); ratio = minmax(rn((pathLength / name.length) * 60), 40, 130);
el.setAttribute("font-size", ratio + "%"); el.setAttribute("font-size", ratio + "%");
}); });

View file

@ -117,7 +117,8 @@ window.Cultures = (function () {
if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations
const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature
if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline
if ((cells.harbor[i] && f.type !== "lake" && P(0.1)) || (cells.harbor[i] === 1 && P(0.6)) || (pack.features[cells.f[i]].group === "isle" && P(0.4))) return "Naval"; // low water cross penalty and high for non-along-coastline growth if ((cells.harbor[i] && f.type !== "lake" && P(0.1)) || (cells.harbor[i] === 1 && P(0.6)) || (pack.features[cells.f[i]].group === "isle" && P(0.4)))
return "Naval"; // low water cross penalty and high for non-along-coastline growth
if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth
if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes
return "Generic"; return "Generic";
@ -435,7 +436,7 @@ window.Cultures = (function () {
function getRiverCost(r, i, type) { function getRiverCost(r, i, type) {
if (type === "River") return r ? 0 : 100; // penalty for river cultures if (type === "River") return r ? 0 : 100; // penalty for river cultures
if (!r) return 0; // no penalty for others if there is no river if (!r) return 0; // no penalty for others if there is no river
return Math.min(Math.max(cells.fl[i] / 10, 20), 100); // river penalty from 20 to 100 based on flux return minmax(cells.fl[i] / 10, 20, 100); // river penalty from 20 to 100 based on flux
} }
function getTypeCost(t, type) { function getTypeCost(t, type) {

View file

@ -225,8 +225,8 @@ function parseLoadedData(data) {
if (settings[11]) barPosY.value = settings[11]; if (settings[11]) barPosY.value = settings[11];
if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12]; if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12];
if (settings[13]) urbanization = urbanizationInput.value = urbanizationOutput.value = settings[13]; if (settings[13]) urbanization = urbanizationInput.value = urbanizationOutput.value = settings[13];
if (settings[14]) mapSizeInput.value = mapSizeOutput.value = Math.max(Math.min(settings[14], 100), 1); if (settings[14]) mapSizeInput.value = mapSizeOutput.value = minmax(settings[14], 1, 100);
if (settings[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(settings[15], 100), 0); if (settings[15]) latitudeInput.value = latitudeOutput.value = minmax(settings[15], 0, 100);
if (settings[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = settings[16]; if (settings[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = settings[16];
if (settings[17]) temperaturePoleInput.value = temperaturePoleOutput.value = settings[17]; if (settings[17]) temperaturePoleInput.value = temperaturePoleOutput.value = settings[17];
if (settings[18]) precInput.value = precOutput.value = settings[18]; if (settings[18]) precInput.value = precOutput.value = settings[18];

View file

@ -3,9 +3,8 @@
window.Military = (function () { window.Military = (function () {
const generate = function () { const generate = function () {
TIME && console.time("generateMilitaryForces"); TIME && console.time("generateMilitaryForces");
const cells = pack.cells, const {cells, states} = pack;
p = cells.p, const {p} = cells;
states = pack.states;
const valid = states.filter(s => s.i && !s.removed); // valid states const valid = states.filter(s => s.i && !s.removed); // valid states
if (!options.military) options.military = getDefaultOptions(); if (!options.military) options.military = getDefaultOptions();
@ -19,7 +18,6 @@ window.Military = (function () {
mounted: {Nomadic: 2.3, Highland: 0.6, Lake: 0.7, Naval: 0.3, Hunting: 0.7, River: 0.8}, mounted: {Nomadic: 2.3, Highland: 0.6, Lake: 0.7, Naval: 0.3, Hunting: 0.7, River: 0.8},
machinery: {Nomadic: 0.8, Highland: 1.4, Lake: 1.1, Naval: 1.4, Hunting: 0.4, River: 1.1}, machinery: {Nomadic: 0.8, Highland: 1.4, Lake: 1.1, Naval: 1.4, Hunting: 0.4, River: 1.1},
naval: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.8, Hunting: 0.7, River: 1.2}, naval: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.8, Hunting: 0.7, River: 1.2},
// non-default generic:
armored: {Nomadic: 1, Highland: 0.5, Lake: 1, Naval: 1, Hunting: 0.7, River: 1.1}, armored: {Nomadic: 1, Highland: 0.5, Lake: 1, Naval: 1, Hunting: 0.7, River: 1.1},
aviation: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.2, Hunting: 0.6, River: 1.2}, aviation: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.2, Hunting: 0.6, River: 1.2},
magical: {Nomadic: 1, Highland: 2, Lake: 1, Naval: 1, Hunting: 1, River: 1} magical: {Nomadic: 1, Highland: 2, Lake: 1, Naval: 1, Hunting: 1, River: 1}
@ -38,18 +36,14 @@ window.Military = (function () {
}; };
valid.forEach(s => { valid.forEach(s => {
const temp = (s.temp = {}), const temp = (s.temp = {});
d = s.diplomacy; const d = s.diplomacy;
const expansionRate = Math.min(Math.max(s.expansionism / expn / (s.area / area), 0.25), 4); // how much state expansionism is realized
const expansionRate = minmax(s.expansionism / expn / (s.area / area), 0.25, 4); // how much state expansionism is realized
const diplomacyRate = d.some(d => d === "Enemy") ? 1 : d.some(d => d === "Rival") ? 0.8 : d.some(d => d === "Suspicion") ? 0.5 : 0.1; // peacefulness const diplomacyRate = d.some(d => d === "Enemy") ? 1 : d.some(d => d === "Rival") ? 0.8 : d.some(d => d === "Suspicion") ? 0.5 : 0.1; // peacefulness
const neighborsRate = Math.min( const neighborsRateRaw = s.neighbors.map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")).reduce((s, r) => (s += rate[r]), 0.5);
Math.max( const neighborsRate = minmax(neighborsRateRaw, 0.3, 3); // neighbors rate
s.neighbors.map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")).reduce((s, r) => (s += rate[r]), 0.5), s.alert = minmax(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1, 5); // alert rate (area modifier)
0.3
),
3
); // neighbors rate
s.alert = Math.min(Math.max(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1), 5); // war alert rate (army modifier)
temp.platoons = []; temp.platoons = [];
// apply overall state modifiers for unit types based on state features // apply overall state modifiers for unit types based on state features
@ -334,7 +328,13 @@ window.Military = (function () {
const getName = function (r, regiments) { const getName = function (r, regiments) {
const cells = pack.cells; const cells = pack.cells;
const proper = r.n ? null : cells.province[r.cell] && pack.provinces[cells.province[r.cell]] ? pack.provinces[cells.province[r.cell]].name : cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]] ? pack.burgs[cells.burg[r.cell]].name : null; const proper = r.n
? null
: cells.province[r.cell] && pack.provinces[cells.province[r.cell]]
? pack.provinces[cells.province[r.cell]].name
: cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]]
? pack.burgs[cells.burg[r.cell]].name
: null;
const number = nth(regiments.filter(reg => reg.n === r.n && reg.i < r.i).length + 1); const number = nth(regiments.filter(reg => reg.n === r.n && reg.i < r.i).length + 1);
const form = r.n ? "Fleet" : "Regiment"; const form = r.n ? "Fleet" : "Regiment";
return `${number}${proper ? ` (${proper}) ` : ` `}${form}`; return `${number}${proper ? ` (${proper}) ` : ` `}${form}`;
@ -351,7 +351,12 @@ window.Military = (function () {
const generateNote = function (r, s) { const generateNote = function (r, s) {
const cells = pack.cells; const cells = pack.cells;
const base = cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]] ? pack.burgs[cells.burg[r.cell]].name : cells.province[r.cell] && pack.provinces[cells.province[r.cell]] ? pack.provinces[cells.province[r.cell]].fullName : null; const base =
cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]]
? pack.burgs[cells.burg[r.cell]].name
: cells.province[r.cell] && pack.provinces[cells.province[r.cell]]
? pack.provinces[cells.province[r.cell]].fullName
: null;
const station = base ? `${r.name} is ${r.n ? "based" : "stationed"} in ${base}. ` : ""; const station = base ? `${r.name} is ${r.n ? "based" : "stationed"} in ${base}. ` : "";
const composition = r.a const composition = r.a

View file

@ -52,7 +52,7 @@ window.ReliefIcons = (function () {
function getReliefIcon(i, h) { function getReliefIcon(i, h) {
const temp = grid.cells.temp[pack.cells.g[i]]; const temp = grid.cells.temp[pack.cells.g[i]];
const type = h > 70 && temp < 0 ? "mountSnow" : h > 70 ? "mount" : "hill"; const type = h > 70 && temp < 0 ? "mountSnow" : h > 70 ? "mount" : "hill";
const size = h > 70 ? (h - 45) * mod : Math.min(Math.max((h - 40) * mod, 3), 6); const size = h > 70 ? (h - 45) * mod : minmax((h - 40) * mod, 3, 6);
return [getIcon(type), size]; return [getIcon(type), size];
} }
} }

View file

@ -330,7 +330,7 @@ class Battle {
} }
getInitialMorale() { getInitialMorale() {
const powerFee = diff => Math.min(Math.max(100 - diff ** 1.5 * 10 + 10, 50), 100); const powerFee = diff => minmax(100 - diff ** 1.5 * 10 + 10, 50, 100);
const distanceFee = dist => Math.min(d3.mean(dist) / 50, 15); const distanceFee = dist => Math.min(d3.mean(dist) / 50, 15);
const powerDiff = this.defenders.power / this.attackers.power; const powerDiff = this.defenders.power / this.attackers.power;
this.attackers.morale = powerFee(powerDiff) - distanceFee(this.attackers.distances); this.attackers.morale = powerFee(powerDiff) - distanceFee(this.attackers.distances);

View file

@ -411,7 +411,7 @@ function editBurg(id) {
const {cells} = pack; const {cells} = pack;
const {name, population, cell} = burg; const {name, population, cell} = burg;
const burgSeed = getBurgSeed(burg); const burgSeed = getBurgSeed(burg);
const size = Math.max(Math.min(rn(population), 100), 6); const size = minmax(rn(population), 6, 100);
const people = rn(population * populationRate * urbanization); const people = rn(population * populationRate * urbanization);
const hub = +cells.road[cell] > 50; const hub = +cells.road[cell] > 50;

View file

@ -621,7 +621,7 @@ function editHeightmap() {
const interpolate = d3.interpolateRound(power, 1); const interpolate = d3.interpolateRound(power, 1);
const land = changeOnlyLand.checked; const land = changeOnlyLand.checked;
function lim(v) { function lim(v) {
return Math.max(Math.min(v, 100), land ? 20 : 0); return minmax(v, land ? 20 : 0, 100);
} }
const h = grid.cells.h; const h = grid.cells.h;

View file

@ -122,7 +122,7 @@ function pressNumpadSign(key) {
else if (religionsManuallyBrush.offsetParent) brush = document.getElementById("religionsManuallyBrush"); else if (religionsManuallyBrush.offsetParent) brush = document.getElementById("religionsManuallyBrush");
if (brush) { if (brush) {
const value = Math.max(Math.min(+brush.value + change, +brush.max), +brush.min); const value = minmax(+brush.value + change, +brush.min, +brush.max);
brush.value = document.getElementById(brush.id + "Number").value = value; brush.value = document.getElementById(brush.id + "Number").value = value;
return; return;
} }

View file

@ -1670,21 +1670,21 @@ function drawEmblems() {
const validBurgs = burgs.filter(b => b.i && !b.removed && b.coa && b.coaSize != 0); const validBurgs = burgs.filter(b => b.i && !b.removed && b.coa && b.coaSize != 0);
const getStateEmblemsSize = () => { const getStateEmblemsSize = () => {
const startSize = Math.min(Math.max((graphHeight + graphWidth) / 40, 10), 100); const startSize = minmax((graphHeight + graphWidth) / 40, 10, 100);
const statesMod = 1 + validStates.length / 100 - (15 - validStates.length) / 200; // states number modifier const statesMod = 1 + validStates.length / 100 - (15 - validStates.length) / 200; // states number modifier
const sizeMod = +document.getElementById("emblemsStateSizeInput").value || 1; const sizeMod = +document.getElementById("emblemsStateSizeInput").value || 1;
return rn((startSize / statesMod) * sizeMod); // target size ~50px on 1536x754 map with 15 states return rn((startSize / statesMod) * sizeMod); // target size ~50px on 1536x754 map with 15 states
}; };
const getProvinceEmblemsSize = () => { const getProvinceEmblemsSize = () => {
const startSize = Math.min(Math.max((graphHeight + graphWidth) / 100, 5), 70); const startSize = minmax((graphHeight + graphWidth) / 100, 5, 70);
const provincesMod = 1 + validProvinces.length / 1000 - (115 - validProvinces.length) / 1000; // states number modifier const provincesMod = 1 + validProvinces.length / 1000 - (115 - validProvinces.length) / 1000; // states number modifier
const sizeMod = +document.getElementById("emblemsProvinceSizeInput").value || 1; const sizeMod = +document.getElementById("emblemsProvinceSizeInput").value || 1;
return rn((startSize / provincesMod) * sizeMod); // target size ~20px on 1536x754 map with 115 provinces return rn((startSize / provincesMod) * sizeMod); // target size ~20px on 1536x754 map with 115 provinces
}; };
const getBurgEmblemSize = () => { const getBurgEmblemSize = () => {
const startSize = Math.min(Math.max((graphHeight + graphWidth) / 185, 2), 50); const startSize = minmax((graphHeight + graphWidth) / 185, 2, 50);
const burgsMod = 1 + validBurgs.length / 1000 - (450 - validBurgs.length) / 1000; // states number modifier const burgsMod = 1 + validBurgs.length / 1000 - (450 - validBurgs.length) / 1000; // states number modifier
const sizeMod = +document.getElementById("emblemsBurgSizeInput").value || 1; const sizeMod = +document.getElementById("emblemsBurgSizeInput").value || 1;
return rn((startSize / burgsMod) * sizeMod); // target size ~8.5px on 1536x754 map with 450 burgs return rn((startSize / burgsMod) * sizeMod); // target size ~8.5px on 1536x754 map with 450 burgs

View file

@ -473,10 +473,10 @@ function changeDialogsTheme(themeColor, transparency) {
} }
function changeZoomExtent(value) { function changeZoomExtent(value) {
const min = Math.max(+zoomExtentMin.value, 0.01), const min = Math.max(+zoomExtentMin.value, 0.01);
max = Math.min(+zoomExtentMax.value, 200); const max = Math.min(+zoomExtentMax.value, 200);
zoom.scaleExtent([min, max]); zoom.scaleExtent([min, max]);
const scale = Math.max(Math.min(+value, 200), 0.01); const scale = minmax(+value, 0.01, 200);
zoom.scaleTo(svg, scale); zoom.scaleTo(svg, scale);
} }
@ -517,7 +517,7 @@ function applyStoredOptions() {
uiSizeInput.max = uiSizeOutput.max = getUImaxSize(); uiSizeInput.max = uiSizeOutput.max = getUImaxSize();
if (localStorage.getItem("uiSize")) changeUIsize(localStorage.getItem("uiSize")); if (localStorage.getItem("uiSize")) changeUIsize(localStorage.getItem("uiSize"));
else changeUIsize(Math.max(Math.min(rn(mapWidthInput.value / 1280, 1), 2.5), 1)); else changeUIsize(minmax(rn(mapWidthInput.value / 1280, 1), 1, 2.5));
// search params overwrite stored and default options // search params overwrite stored and default options
const params = new URL(window.location.href).searchParams; const params = new URL(window.location.href).searchParams;

View file

@ -248,7 +248,7 @@ function each(n) {
// random number (normal or gaussian distribution) // random number (normal or gaussian distribution)
function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) { function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) {
return rn(Math.max(Math.min(d3.randomNormal(expected, deviation)(), max), min), round); return rn(minmax(d3.randomNormal(expected, deviation)(), min, max), round);
} }
// probability shorthand for floats // probability shorthand for floats
@ -416,9 +416,13 @@ function getSegmentId(points, point, step = 10) {
return minSegment; return minSegment;
} }
function minmax(value, min, max) {
return Math.min(Math.max(value, min), max);
}
// normalization function // normalization function
function normalize(val, min, max) { function normalize(val, min, max) {
return Math.min(Math.max((val - min) / (max - min), 0), 1); return minmax((val - min) / (max - min), 0, 1);
} }
// return a random integer from min to max biased towards one end based on exponent distribution (the bigger ex the higher bias towards min) // return a random integer from min to max biased towards one end based on exponent distribution (the bigger ex the higher bias towards min)
@ -542,7 +546,7 @@ function rw(object) {
// return value in range [0, 100] (height range) // return value in range [0, 100] (height range)
function lim(v) { function lim(v) {
return Math.max(Math.min(v, 100), 0); return minmax(v, 0, 100);
} }
// get number from string in format "1-3" or "2" or "0.5" // get number from string in format "1-3" or "2" or "0.5"