From f4c24eb6acc08d39a411d3944b5cb22a0325bb25 Mon Sep 17 00:00:00 2001 From: FCGudder <-@-> Date: Thu, 29 Jun 2017 12:28:18 +0200 Subject: [PATCH] Utility methods Height.mean(...) and Height.random(...) --- src/js/utilJS.tw | 126 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/js/utilJS.tw b/src/js/utilJS.tw index 399048bb9bd..6e7b523fd51 100644 --- a/src/js/utilJS.tw +++ b/src/js/utilJS.tw @@ -1,5 +1,131 @@ :: UtilJS [script] +/* + * Height.mean(nationality, race, genes, age) - returns the mean height for the given combination and age in years (>=2) + * Height.mean(nationality, race, genes) - returns the mean adult height for the given combination + * Height.mean(slave) - returns the mean (expected) height for the given slave + * Height.random(nationality, race, genes, age) - returns a random height for the given combination, + * with Gaussian distribution (mean = 1, standard deviation = 0.05) around the mean height + * Height.random(nationality, race, genes) - returns a random height for the given combination of an adult, as above + * Height.random(slave) - returns a random height for the given slave, as above + */ +window.Height = (function(){ + const xxMeanHeight = { + "American.white": 165, "American.black": 163.6, "American.latina": 158.9, "American.asian": 158.4, "American": 161.8, + "Afghan": undefined, "Algerian": 162, "Argentinian": 159.6, "Armenian": undefined, "Australian": 161.8, "Austrian": 166, + "Bangladeshi": undefined, "Belarusian": 166.8, "Belgian": 168.1, "Bolivian": 142.2, "Brazilian": 158.8, + "British": 161.9, "Burmese": undefined, "Canadian": 162.3, "Chilean": 157.2, "Chinese": 155.8, "Colombian": 158.7, + "Congolese": 157.7, "Cuban": 156, "Czech": 167.22, "Danish": 168.7, "Dominican": 156.4, "Dutch": 169, "Egyptian": 158.9, + "Emirati": 158.9, "Estonian": 165.5, "Ethiopian": undefined, "Filipina": undefined, "Finnish": 165.3, "French": 162.5, + "German": 162.8, "Ghanan": 158.5, "Greek": 165, "Guatemalan": undefined, "Haitian": undefined, "Hungarian": 164, + "Icelandic": 168, "Indian": 151.9, "Indonesian": 147, "Iranian": 157.2, "Iraqi": 155.8, "Irish": 163, "Israeli": 166, + "Italian": 162.5, "Jamaican": 160.8, "Japanese": 158, "Jordanian": undefined, "Kazakh": 159.8, "Kenyan": undefined, + "Korean": 156.15, "Lebanese": 165, "Libyan": 160.5, "Lithuanian": 167.5, "Malaysian": 154.7, "Malian": 160.4, + "Mexican": 154, "Moroccan": 158.5, "Nepalese": 150.8, "Nigerian": 163.8, "Norwegian": 157.8, "Omani": undefined, + "Pakistani": 151.9, "Peruvian": 151, "Polish": 165.1, "Portuguese": 165.1, "Puerto Rican": 158.9, "Romanian": 157, + "Russian": 164.1, "Saudi": 156.3, "Scottish": 163, "Serbian": 166.8, "Slovak": 165.6, "South African": 159, + "Spanish": 162.6, "Sudanese": undefined, "Swedish": 166.8, "Swiss": 162.5, "Tanzanian": undefined, "Thai": 159, + "Tunisian": 160, "Turkish": 161.9, "Ugandan": undefined, "Ukrainian": 164.8, "Uzbek": 159.9, "Venezuelan": 159, + "Vietnamese": 155.2, "Yemeni": undefined, "a New Zealander": 164, "Zimbabwean": undefined, + "": 162.5 // default + }; + const xyMeanHeight = { + "American.white": 178.2, "American.black": 177.4, "American.latina": 172.5, "American.asian": 172.5, "American": 176.4, + "Afghan": undefined, "Algerian": 172.2, "Argentinian": 174.46, "Armenian": undefined, "Australian": 175.6, + "Austrian": 179, "Bangladeshi": undefined, "Belarusian": 176.9, "Belgian": 178.7, "Bolivian": 160, "Brazilian": 170.7, + "British": 175.3, "Burmese": undefined, "Canadian": 175.1, "Chilean": 169.6, "Chinese": 167.1, "Colombian": 170.6, + "Congolese": 158.9, "Cuban": 168, "Czech": 180.31, "Danish": 180.4, "Dominican": 168.4, "Dutch": 181, "Egyptian": 170.3, + "Emirati": 170.3, "Estonian": 179.1, "Ethiopian": undefined, "Filipina": undefined, "Finnish": 178.9, "French": 175.6, + "German": 175.4, "Ghanan": 169.5, "Greek": 177, "Guatemalan": undefined, "Haitian": undefined, "Hungarian": 176, + "Icelandic": 181, "Indian": 164.7, "Indonesian": 158, "Iranian": 170.3, "Iraqi": 165.4, "Irish": 177, "Israeli": 177, + "Italian": 176.5, "Jamaican": 171.8, "Japanese": 172, "Jordanian": undefined, "Kazakh": 169, "Kenyan": undefined, + "Korean": 168.15, "Lebanese": 176, "Libyan": 171.3, "Lithuanian": 177.2, "Malaysian": 166.3, "Malian": 171.3, + "Mexican": 167, "Moroccan": 172.7, "Nepalese": 163, "Nigerian": 163.8, "Norwegian": 179.63, "Omani": undefined, + "Pakistani": 164.7, "Peruvian": 164, "Polish": 178.7, "Portuguese": 173.9, "Puerto Rican": 172.5, "Romanian": 172, + "Russian": 177.2, "Saudi": 168.9, "Scottish": 177.6, "Serbian": 182, "Slovak": 179.4, "South African": 168, + "Spanish": 173.1, "Sudanese": undefined, "Swedish": 181.5, "Swiss": 178.2, "Tanzanian": undefined, "Thai": 170.3, + "Tunisian": 172.3, "Turkish": 173.6, "Ugandan": undefined, "Ukrainian": 176.5, "Uzbek": 175.4, "Venezuelan": 169, + "Vietnamese": 165.7, "Yemeni": undefined, "a New Zealander": 177, "Zimbabwean": undefined, + ".white": 177.6, "": 172.5 // defaults + }; + + // Helper method - table lookup for nationality/race combinations + const nationalityMeanHeight = function(table, nationality, race, def) { + return table[nationality + "." + race] || table[nationality] || table["." + race] || table[""] || def; + }; + + // Helper method - Gaussian distributed random variable, constrained to +/-max, using Box-Muller transform + const constrainedGaussian = function(max) { + var u = 1 - Math.random(); // Subtraction to flip [0, 1) to (0, 1]. + var v = 1 - Math.random(); + return Math.clamp(Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v), -max, max); + }; + + const _meanHeight = function(nationality, race, genes, age) { + if(_.isObject(nationality)) { + // We got called with a single slave as the argument + return _meanHeight(nationality.nationality, nationality.race, nationality.genes, nationality.physicalAge + nationality.birthWeek / 52.0); + } + let result = 0, minHeight = 0, midHeight = 0, midAge = 15; + switch(genes) { + case 'XX': // female + result = nationalityMeanHeight(xxMeanHeight, nationality, race); + minHeight = 85; midHeight = result * 157/164; midAge = 13; + break; + case 'XY': // male + result = nationalityMeanHeight(xyMeanHeight, nationality, race); + minHeight = 86; midHeight = result * 170/178; midAge = 15; + break; + // special cases. Extra SHOX genes on X and Y chromosomes make for larger people + case 'X0': case 'X': // Turner syndrome female + result = nationalityMeanHeight(xxMeanHeight, nationality, race) * 0.93; + minHeight = 85 * 0.93; midHeight = result * 157/164; midAge = 13; + break; + case 'XXX': // Triple X syndrome female + result = nationalityMeanHeight(xxMeanHeight, nationality, race) * 1.03; + minHeight = 85; midHeight = result * 157/164; midAge = 13; + break; + case 'XXY': // Kinefelter syndrome male + result = nationalityMeanHeight(xyMeanHeight, nationality, race) * 1.03; + minHeight = 86; midHeight = result * 170/178; midAge = 15; + break; + case 'XYY': // XYY syndrome male + result = nationalityMeanHeight(xyMeanHeight, nationality, race) * 1.04; + minHeight = 86; midHeight = result * 170/178; midAge = 15; + break; + case 'Y': case 'Y0': case 'YY': case 'YYY': + console.log("Height.mean(): non-viable genes " + genes); + break; + default: + console.log("Height.mean(): unknown genes " + genes + ", returning mean between XX and XY"); + result = nationalityMeanHeight(xxMeanHeight, nationality, race) * 0.5 + + nationalityMeanHeight(xyMeanHeight, nationality, race) * 0.5; + minHeight = 85.5, midHeight = result * 327/342, midAge = 14; + break; + } + if(_.isFinite(age) && age >= 2 && age < 20) { + if(age > midAge) { + // end of puberty to 20 + result = interpolate(midAge, midHeight, 20, result, age); + } else { + // 2 to end of puberty + result = interpolate(2, minHeight, midAge, midHeight, age); + } + } + return result; + }; + + const _randomHeight = function(nationality, race, genes, age) { + const mean = _meanHeight(nationality, race, genes, age); + return mean * (1 + constrainedGaussian(3) * 0.05); + } + + return { + mean: _meanHeight, + random: _randomHeight, + }; +})(); + if(!Array.prototype.findIndex) { Array.prototype.findIndex = function(predicate) { if (this == null) { -- GitLab