From bdc0c9f8ee0c88769c472c22c8fc388e03130db1 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 10 Oct 2021 17:59:27 +0300 Subject: [PATCH] minmax util function --- main.js | 6 +++--- modules/burgs-and-states.js | 10 ++++----- modules/cultures-generator.js | 5 +++-- modules/load.js | 4 ++-- modules/military-generator.js | 39 +++++++++++++++++++--------------- modules/relief-icons.js | 2 +- modules/ui/battle-screen.js | 2 +- modules/ui/burg-editor.js | 2 +- modules/ui/heightmap-editor.js | 2 +- modules/ui/hotkeys.js | 2 +- modules/ui/layers.js | 6 +++--- modules/ui/options.js | 8 +++---- modules/utils.js | 10 ++++++--- 13 files changed, 54 insertions(+), 44 deletions(-) diff --git a/main.js b/main.js index 2067ad1e..9a603827 100644 --- a/main.js +++ b/main.js @@ -954,7 +954,7 @@ function calculateTemperatures() { const lat = Math.abs(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT); // [0; 90] const initTemp = tEq - int(lat / 90) * tDelta; 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; cells.prec[current] += precipitation; 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 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 - return Math.min(Math.max(normalLoss + diff * mod, 1), humidity); + return minmax(normalLoss + diff * mod, 1, humidity); } void (function drawWindDirection() { diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js index 37931059..d25e038c 100644 --- a/modules/burgs-and-states.js +++ b/modules/burgs-and-states.js @@ -416,7 +416,7 @@ window.BurgsAndStates = (function () { function getRiverCost(r, i, type) { if (type === "River") return r ? 0 : 100; // penalty for river cultures 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) { @@ -606,15 +606,15 @@ window.BurgsAndStates = (function () { if (pathLength < s.name.length) { // only short name will fit 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) { // full name will fit in one line 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 { // try miltilined label 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 @@ -665,7 +665,7 @@ window.BurgsAndStates = (function () { example.text(name); const left = example.node().getBBox().width / -2; // x offset el.innerHTML = `${name}`; - 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 + "%"); }); diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js index 04a96dca..b4733260 100644 --- a/modules/cultures-generator.js +++ b/modules/cultures-generator.js @@ -117,7 +117,8 @@ window.Cultures = (function () { 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 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.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes return "Generic"; @@ -435,7 +436,7 @@ window.Cultures = (function () { function getRiverCost(r, i, type) { if (type === "River") return r ? 0 : 100; // penalty for river cultures 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) { diff --git a/modules/load.js b/modules/load.js index bf285631..60e8a5c7 100644 --- a/modules/load.js +++ b/modules/load.js @@ -225,8 +225,8 @@ function parseLoadedData(data) { if (settings[11]) barPosY.value = settings[11]; if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12]; 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[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(settings[15], 100), 0); + if (settings[14]) mapSizeInput.value = mapSizeOutput.value = minmax(settings[14], 1, 100); + if (settings[15]) latitudeInput.value = latitudeOutput.value = minmax(settings[15], 0, 100); if (settings[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = settings[16]; if (settings[17]) temperaturePoleInput.value = temperaturePoleOutput.value = settings[17]; if (settings[18]) precInput.value = precOutput.value = settings[18]; diff --git a/modules/military-generator.js b/modules/military-generator.js index 0089373d..f777f4cf 100644 --- a/modules/military-generator.js +++ b/modules/military-generator.js @@ -3,9 +3,8 @@ window.Military = (function () { const generate = function () { TIME && console.time("generateMilitaryForces"); - const cells = pack.cells, - p = cells.p, - states = pack.states; + const {cells, states} = pack; + const {p} = cells; const valid = states.filter(s => s.i && !s.removed); // valid states 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}, 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}, - // non-default generic: 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}, magical: {Nomadic: 1, Highland: 2, Lake: 1, Naval: 1, Hunting: 1, River: 1} @@ -38,18 +36,14 @@ window.Military = (function () { }; valid.forEach(s => { - const temp = (s.temp = {}), - 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 temp = (s.temp = {}); + const d = s.diplomacy; + + 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 neighborsRate = Math.min( - Math.max( - s.neighbors.map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")).reduce((s, r) => (s += rate[r]), 0.5), - 0.3 - ), - 3 - ); // neighbors rate - s.alert = Math.min(Math.max(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1), 5); // war alert rate (army modifier) + const neighborsRateRaw = s.neighbors.map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")).reduce((s, r) => (s += rate[r]), 0.5); + const neighborsRate = minmax(neighborsRateRaw, 0.3, 3); // neighbors rate + s.alert = minmax(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1, 5); // alert rate (area modifier) temp.platoons = []; // apply overall state modifiers for unit types based on state features @@ -334,7 +328,13 @@ window.Military = (function () { const getName = function (r, regiments) { 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 form = r.n ? "Fleet" : "Regiment"; return `${number}${proper ? ` (${proper}) ` : ` `}${form}`; @@ -351,7 +351,12 @@ window.Military = (function () { const generateNote = function (r, s) { 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 composition = r.a diff --git a/modules/relief-icons.js b/modules/relief-icons.js index 3cb4fd84..498886df 100644 --- a/modules/relief-icons.js +++ b/modules/relief-icons.js @@ -52,7 +52,7 @@ window.ReliefIcons = (function () { function getReliefIcon(i, h) { const temp = grid.cells.temp[pack.cells.g[i]]; 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]; } } diff --git a/modules/ui/battle-screen.js b/modules/ui/battle-screen.js index bd76f22c..37a1bd51 100644 --- a/modules/ui/battle-screen.js +++ b/modules/ui/battle-screen.js @@ -330,7 +330,7 @@ class Battle { } 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 powerDiff = this.defenders.power / this.attackers.power; this.attackers.morale = powerFee(powerDiff) - distanceFee(this.attackers.distances); diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js index c33cd488..98cae1a5 100644 --- a/modules/ui/burg-editor.js +++ b/modules/ui/burg-editor.js @@ -411,7 +411,7 @@ function editBurg(id) { const {cells} = pack; const {name, population, cell} = 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 hub = +cells.road[cell] > 50; diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index 9bf20753..b42aad76 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -621,7 +621,7 @@ function editHeightmap() { const interpolate = d3.interpolateRound(power, 1); const land = changeOnlyLand.checked; 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; diff --git a/modules/ui/hotkeys.js b/modules/ui/hotkeys.js index bc0f5c44..9081d002 100644 --- a/modules/ui/hotkeys.js +++ b/modules/ui/hotkeys.js @@ -122,7 +122,7 @@ function pressNumpadSign(key) { else if (religionsManuallyBrush.offsetParent) brush = document.getElementById("religionsManuallyBrush"); 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; return; } diff --git a/modules/ui/layers.js b/modules/ui/layers.js index e4846631..baae4c88 100644 --- a/modules/ui/layers.js +++ b/modules/ui/layers.js @@ -1670,21 +1670,21 @@ function drawEmblems() { const validBurgs = burgs.filter(b => b.i && !b.removed && b.coa && b.coaSize != 0); 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 sizeMod = +document.getElementById("emblemsStateSizeInput").value || 1; return rn((startSize / statesMod) * sizeMod); // target size ~50px on 1536x754 map with 15 states }; 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 sizeMod = +document.getElementById("emblemsProvinceSizeInput").value || 1; return rn((startSize / provincesMod) * sizeMod); // target size ~20px on 1536x754 map with 115 provinces }; 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 sizeMod = +document.getElementById("emblemsBurgSizeInput").value || 1; return rn((startSize / burgsMod) * sizeMod); // target size ~8.5px on 1536x754 map with 450 burgs diff --git a/modules/ui/options.js b/modules/ui/options.js index 7dac5462..d6252bc6 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -473,10 +473,10 @@ function changeDialogsTheme(themeColor, transparency) { } function changeZoomExtent(value) { - const min = Math.max(+zoomExtentMin.value, 0.01), - max = Math.min(+zoomExtentMax.value, 200); + const min = Math.max(+zoomExtentMin.value, 0.01); + const max = Math.min(+zoomExtentMax.value, 200); 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); } @@ -517,7 +517,7 @@ function applyStoredOptions() { uiSizeInput.max = uiSizeOutput.max = getUImaxSize(); 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 const params = new URL(window.location.href).searchParams; diff --git a/modules/utils.js b/modules/utils.js index 883296a6..1aa308ca 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -248,7 +248,7 @@ function each(n) { // random number (normal or gaussian distribution) 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 @@ -416,9 +416,13 @@ function getSegmentId(points, point, step = 10) { return minSegment; } +function minmax(value, min, max) { + return Math.min(Math.max(value, min), max); +} + // normalization function 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) @@ -542,7 +546,7 @@ function rw(object) { // return value in range [0, 100] (height range) 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"