diff --git a/binaries/data/mods/public/globalscripts/random.js b/binaries/data/mods/public/globalscripts/random.js index 7c9af9bc3e..29c6d169a9 100644 --- a/binaries/data/mods/public/globalscripts/random.js +++ b/binaries/data/mods/public/globalscripts/random.js @@ -16,9 +16,50 @@ function randomNormal2D() } /** - * Return a random element of the source array + * Return a random element of the source array. */ function pickRandom(source) { return source.length ? source[Math.floor(source.length * Math.random())] : undefined; } + +/** + * Return a random floating point number. + * + * If two parameters are given, and the returned float is in the interval [min, max). + * If no parameter is given, the returned float is in the interval [0, 1). + */ +function randFloat(min = 0, max = 1) +{ + return min + Math.random() * (max - min); +} + +/** + * Return a random integer of the interval [floor(min) .. ceil(max)] using Math.random library. + * + * If an argument is not integer, the uniform distribution is cut off at that endpoint. + * For example randIntInclusive(1.5, 2.5) yields 50% chance to get 2 and 25% chance for 1 and 3. + */ +function randIntInclusive(min, max) +{ + return Math.floor(min + Math.random() * (max + 1 - min)); +} + +/** + * Return a random integer of the interval [floor(min) .. ceil(max-1)]. + * + * If an argument is not integer, the uniform distribution is cut off at that endpoint. + * For example randIntExclusive(1.5, 3.5) yields 50% chance to get 2 and 25% chance for 1 and 3. + */ +function randIntExclusive(min, max) +{ + return Math.floor(min + Math.random() * (max - min)); +} + +/** + * Returns true or false randomly. + */ +function randBool() +{ + return Math.random() < 0.5; +} diff --git a/binaries/data/mods/public/globalscripts/utility.js b/binaries/data/mods/public/globalscripts/utility.js index cb854d1761..dea0a6ef22 100644 --- a/binaries/data/mods/public/globalscripts/utility.js +++ b/binaries/data/mods/public/globalscripts/utility.js @@ -28,7 +28,7 @@ function shuffleArray(source) let result = [source[0]]; for (let i = 1; i < source.length; ++i) { - let j = Math.floor(Math.random() * (i+1)); + let j = randIntInclusive(0, i); result[i] = result[j]; result[j] = source[i]; } diff --git a/binaries/data/mods/public/gui/common/functions_utility.js b/binaries/data/mods/public/gui/common/functions_utility.js index e93770ce0a..991fbd0904 100644 --- a/binaries/data/mods/public/gui/common/functions_utility.js +++ b/binaries/data/mods/public/gui/common/functions_utility.js @@ -3,16 +3,6 @@ */ var g_LastNickNotification = -1; -function getRandom(randomMin, randomMax) -{ - // Returns a random whole number in a min..max range. - // NOTE: There should probably be an engine function for this, - // since we'd need to keep track of random seeds for replays. - - var randomNum = randomMin + (randomMax-randomMin)*Math.random(); // num is random, from A to B - return Math.round(randomNum); -} - // Get list of XML files in pathname with recursion, excepting those starting with _ function getXMLFileList(pathname) { diff --git a/binaries/data/mods/public/gui/common/music.js b/binaries/data/mods/public/gui/common/music.js index 1f45471d73..44e8532762 100644 --- a/binaries/data/mods/public/gui/common/music.js +++ b/binaries/data/mods/public/gui/common/music.js @@ -79,7 +79,7 @@ Music.prototype.updateState = function() break; case this.states.MENU: - this.switchMusic(this.getRandomTrack(this.tracks.MENU), 0.0, true); + this.switchMusic(pickRandom(this.tracks.MENU), 0, true); break; case this.states.PEACE: @@ -130,11 +130,6 @@ Music.prototype.storeTracks = function(civMusic) } }; -Music.prototype.getRandomTrack = function(tracks) -{ - return tracks[getRandom(0, tracks.length-1)]; -}; - Music.prototype.startPlayList = function(tracks, fadeInPeriod, isLooping) { Engine.ClearPlaylist(); diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js index 40c4d97687..4ddc770a2d 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -1298,8 +1298,7 @@ function launchGame() { let victoryScriptsSelected = g_GameAttributes.settings.VictoryScripts; let gameTypeSelected = g_GameAttributes.settings.GameType; - selectMap(Engine.GetGUIObjectByName("mapSelection").list_data[Math.floor(Math.random() * - (Engine.GetGUIObjectByName("mapSelection").list.length - 1)) + 1]); + selectMap(pickRandom(Engine.GetGUIObjectByName("mapSelection").list_data.slice(1))); g_GameAttributes.settings.VictoryScripts = victoryScriptsSelected; g_GameAttributes.settings.GameType = gameTypeSelected; } @@ -1322,9 +1321,8 @@ function launchGame() let chosenCiv = g_GameAttributes.settings.PlayerData[i].Civ || "random"; if (chosenCiv == "random") { - let culture = cultures[Math.floor(Math.random() * cultures.length)]; - let civs = Object.keys(g_CivData).filter(civ => g_CivData[civ].Culture == culture); - chosenCiv = civs[Math.floor(Math.random() * civs.length)]; + let culture = pickRandom(cultures); + chosenCiv = pickRandom(Object.keys(g_CivData).filter(civ => g_CivData[civ].Culture == culture)); } g_GameAttributes.settings.PlayerData[i].Civ = chosenCiv; @@ -1332,7 +1330,7 @@ function launchGame() if (g_GameAttributes.mapType === "scenario" || !g_GameAttributes.settings.PlayerData[i].AI) continue; - let chosenName = g_CivData[chosenCiv].AINames[Math.floor(Math.random() * g_CivData[chosenCiv].AINames.length)]; + let chosenName = pickRandom(g_CivData[chosenCiv].AINames); if (!g_IsNetworked) chosenName = translate(chosenName); @@ -1352,8 +1350,8 @@ function launchGame() } // Seed used for both map generation and simulation - g_GameAttributes.settings.Seed = Math.floor(Math.random() * Math.pow(2, 32)); - g_GameAttributes.settings.AISeed = Math.floor(Math.random() * Math.pow(2, 32)); + g_GameAttributes.settings.Seed = randIntExclusive(0, Math.pow(2, 32)); + g_GameAttributes.settings.AISeed = randIntExclusive(0, Math.pow(2, 32)); // Used for identifying rated game reports for the lobby g_GameAttributes.matchID = Engine.GetMatchID(); diff --git a/binaries/data/mods/public/gui/loading/loading.js b/binaries/data/mods/public/gui/loading/loading.js index 24e8aa4db6..826301b7c3 100644 --- a/binaries/data/mods/public/gui/loading/loading.js +++ b/binaries/data/mods/public/gui/loading/loading.js @@ -13,7 +13,7 @@ function init(data) if (tipTextLoadingArray.length > 0) { // Set tip text - let tipTextFilePath = tipTextLoadingArray[getRandom(0, tipTextLoadingArray.length-1)]; + let tipTextFilePath = pickRandom(tipTextLoadingArray); let tipText = Engine.TranslateLines(Engine.ReadFile(tipTextFilePath)); if (tipText) @@ -62,7 +62,7 @@ function init(data) // Pick a random quote of the day (each line is a separate tip). let quoteArray = Engine.ReadFileLines("gui/text/quotes.txt"); - Engine.GetGUIObjectByName("quoteText").caption = translate(quoteArray[getRandom(0, quoteArray.length-1)]); + Engine.GetGUIObjectByName("quoteText").caption = translate(pickRandom(quoteArray)); } function displayProgress() diff --git a/binaries/data/mods/public/gui/pregame/mainmenu.js b/binaries/data/mods/public/gui/pregame/mainmenu.js index 37b1204cbc..a1ec09ec63 100644 --- a/binaries/data/mods/public/gui/pregame/mainmenu.js +++ b/binaries/data/mods/public/gui/pregame/mainmenu.js @@ -1,11 +1,19 @@ var userReportEnabledText; // contains the original version with "$status" placeholder var currentSubmenuType; // contains submenu type const MARGIN = 4; // menu border size -var g_BackgroundCode; // Background type. var g_ShowSplashScreens; + +/** + * Available backdrops + */ var g_BackgroundLayerData = []; +/** + * Chosen backdrop + */ +var g_BackgroundLayerset; + var g_T0 = +(new Date()); var g_LastTickTime = new Date(); @@ -26,13 +34,12 @@ function init(initData, hotloadData) g_ShowSplashScreens = hotloadData ? hotloadData.showSplashScreens : initData && initData.isStartup; // Pick a random background and initialise it - g_BackgroundCode = Math.floor(Math.random() * g_BackgroundLayerData.length); - var layerset = g_BackgroundLayerData[g_BackgroundCode]; - for (var i = 0; i < layerset.length; ++i) + g_BackgroundLayerset = pickRandom(g_BackgroundLayerData); + for (let i = 0; i < g_BackgroundLayerset.length; ++i) { var guiObj = Engine.GetGUIObjectByName("background["+i+"]"); guiObj.hidden = false; - guiObj.sprite = layerset[i].sprite; + guiObj.sprite = g_BackgroundLayerset[i].sprite; guiObj.z = i; } } @@ -44,10 +51,8 @@ function getHotloadData() function scrollBackgrounds() { - var layerset = g_BackgroundLayerData[g_BackgroundCode]; - for (var i = 0; i < layerset.length; ++i) + for (let i = 0; i < g_BackgroundLayerset.length; ++i) { - var layer = layerset[i]; var guiObj = Engine.GetGUIObjectByName("background["+i+"]"); var screen = guiObj.parent.getComputedSize(); @@ -56,8 +61,8 @@ function scrollBackgrounds() var iw = h * 2; var time = (new Date() - g_T0) / 1000; - var offset = layer.offset(time, w); - if (layer.tiling) + var offset = g_BackgroundLayerset[i].offset(time, w); + if (g_BackgroundLayerset[i].tiling) { var left = offset % iw; if (left >= 0) diff --git a/binaries/data/mods/public/gui/session/placement.js b/binaries/data/mods/public/gui/session/placement.js index 3ebc3965c5..95be00ff60 100644 --- a/binaries/data/mods/public/gui/session/placement.js +++ b/binaries/data/mods/public/gui/session/placement.js @@ -36,7 +36,7 @@ PlacementSupport.prototype.SetDefaultAngle = function() PlacementSupport.prototype.RandomizeActorSeed = function() { - this.actorSeed = Math.floor(65535 * Math.random()); + this.actorSeed = randIntExclusive(0, Math.pow(2, 16)); }; var placementSupport = new PlacementSupport(); diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index 2d0e1c9109..63d707d0d8 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -1257,7 +1257,7 @@ function updateAdditionalHighlight() function playAmbient() { - Engine.PlayAmbientSound(g_Ambient[Math.floor(Math.random() * g_Ambient.length)], true); + Engine.PlayAmbientSound(pickRandom(g_Ambient), true); } function getBuildString() diff --git a/binaries/data/mods/public/maps/random/rmgen/random.js b/binaries/data/mods/public/maps/random/rmgen/random.js index 3ece081dd8..b22ddfacfc 100644 --- a/binaries/data/mods/public/maps/random/rmgen/random.js +++ b/binaries/data/mods/public/maps/random/rmgen/random.js @@ -1,31 +1,5 @@ // TODO: rename/change these functions, so the bounds are more clear -/* - * Return a random floating point number using Math.random library - * - * If no parameter given, the returned float is in the interval [0, 1) - * If two parameters are given, they are minval and maxval, and the returned float is in the interval [minval, maxval) - */ -function randFloat() -{ - if (arguments.length == 0) - { - return Math.random(); - } - else if (arguments.length == 2) - { - var minVal = arguments[0]; - var maxVal = arguments[1]; - - return minVal + randFloat() * (maxVal - minVal); - } - else - { - error("randFloat: invalid number of arguments: "+arguments.length); - return undefined; - } -} - /* * Return a random integer using Math.random library * diff --git a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js index 6ebe9f63b9..f96864011c 100644 --- a/binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ b/binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -158,7 +158,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data) } // Put some randomness on the attack size - let variation = 0.8 + 0.4*Math.random(); + let variation = randFloat(0.8, 1.2); // and lower priority and smaller sizes for easier difficulty levels if (this.Config.difficulty < 2) { @@ -1444,7 +1444,7 @@ m.AttackPlan.prototype.update = function(gameState, events) ent.attack(mStruct[0].id(), m.allowCapture(gameState, ent, mStruct[0])); else { - let rand = Math.floor(Math.random() * mStruct.length * 0.2); + let rand = randIntExclusive(0, mStruct.length * 0.2); ent.attack(mStruct[rand].id(), m.allowCapture(gameState, ent, mStruct[rand])); } } @@ -1502,7 +1502,7 @@ m.AttackPlan.prototype.update = function(gameState, events) valb -= 20000; return valb - vala; }); - let rand = Math.floor(Math.random() * mUnit.length * 0.1); + let rand = randIntExclusive(0, mUnit.length * 0.1); ent.attack(mUnit[rand].id(), m.allowCapture(gameState, ent, mUnit[rand])); } else if (this.isBlocked) @@ -1553,7 +1553,7 @@ m.AttackPlan.prototype.update = function(gameState, events) ent.attack(mStruct[0].id(), false); else { - let rand = Math.floor(Math.random() * mStruct.length * 0.2); + let rand = randIntExclusive(0, mStruct.length * 0.2); ent.attack(mStruct[rand].id(), m.allowCapture(gameState, ent, mStruct[rand])); } } diff --git a/binaries/data/mods/public/simulation/ai/petra/config.js b/binaries/data/mods/public/simulation/ai/petra/config.js index dfd931b22a..b22f492554 100644 --- a/binaries/data/mods/public/simulation/ai/petra/config.js +++ b/binaries/data/mods/public/simulation/ai/petra/config.js @@ -131,9 +131,9 @@ m.Config.prototype.setConfig = function(gameState) // initialize personality traits if (this.difficulty > 1) { - this.personality.aggressive = Math.random(); - this.personality.cooperative = Math.random(); - this.personality.defensive = Math.random(); + this.personality.aggressive = randFloat(0, 1); + this.personality.cooperative = randFloat(0, 1); + this.personality.defensive = randFloat(0, 1); } else { diff --git a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js index 1ea965ebb5..434cbe1897 100644 --- a/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/diplomacyManager.js @@ -216,7 +216,7 @@ m.DiplomacyManager.prototype.lastManStandingCheck = function(gameState) // wait a bit before turning if (!this.waitingToBetray) { - this.betrayLapseTime = gameState.ai.elapsedTime + Math.random() * 100 + 10; + this.betrayLapseTime = gameState.ai.elapsedTime + randFloat(10, 110); this.waitingToBetray = true; return; } @@ -277,7 +277,7 @@ m.DiplomacyManager.prototype.handleDiplomacyRequest = function(gameState, player let moreEnemiesThanAllies = gameState.getEnemies().length > gameState.getMutualAllies().length; // For any given diplomacy request be likely to permanently decline - if (!request && gameState.getPlayerCiv() !== gameState.getPlayerCiv(player) && Math.random() > 0.4 || + if (!request && gameState.getPlayerCiv() !== gameState.getPlayerCiv(player) && randFloat(0, 1) > 0.4 || !moreEnemiesThanAllies || gameState.ai.HQ.attackManager.currentEnemyPlayer === player) { this.diplomacyRequests.set(player, { "requestType": requestType, "status": "declinedRequest" }); @@ -296,7 +296,7 @@ m.DiplomacyManager.prototype.handleDiplomacyRequest = function(gameState, player } } else if (requestType === "ally" && gameState.getEntities(player).length < gameState.getOwnEntities().length && - Math.random() > 0.6 || requestType === "neutral" && moreEnemiesThanAllies && Math.random() > 0.2) + randFloat(0, 1) > 0.6 || requestType === "neutral" && moreEnemiesThanAllies && randFloat(0, 1) > 0.2) { response = "accept"; this.changePlayerDiplomacy(gameState, player, requestType); diff --git a/binaries/data/mods/public/simulation/ai/petra/headquarters.js b/binaries/data/mods/public/simulation/ai/petra/headquarters.js index a0bc23ab36..dcf7f1cf7a 100644 --- a/binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -504,12 +504,8 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues) requirements = [ ["strength", 1] ]; let classes = ["CitizenSoldier", "Infantry"]; - let proba = Math.random(); - // we require at least 30% ranged and 30% melee - if ( proba < 0.3 ) - classes.push("Ranged"); - else if ( proba < 0.6 ) - classes.push("Melee"); + // We want at least 33% ranged and 33% melee + classes.push(pickRandom(["Ranged", "Melee", "Infantry"])); template = this.findBestTrainableUnit(gameState, classes, requirements); } @@ -1770,7 +1766,7 @@ m.HQ.prototype.trainEmergencyUnits = function(gameState, positions) } let autogarrison = numGarrisoned < nearestAnchor.garrisonMax() && nearestAnchor.hitpoints() > nearestAnchor.garrisonEjectHealth() * nearestAnchor.maxHitpoints(); - let rangedWanted = Math.random() > 0.5 && autogarrison; + let rangedWanted = randBool() && autogarrison; let total = gameState.getResources(); let templateFound; diff --git a/binaries/data/mods/public/simulation/ai/petra/researchManager.js b/binaries/data/mods/public/simulation/ai/petra/researchManager.js index 7fedfa16d4..7a90947d03 100644 --- a/binaries/data/mods/public/simulation/ai/petra/researchManager.js +++ b/binaries/data/mods/public/simulation/ai/petra/researchManager.js @@ -235,9 +235,9 @@ m.ResearchManager.prototype.update = function(gameState, queues) } if (!techs.length) return; + // randomly pick one. No worries about pairs in that case. - let p = Math.floor(Math.random()*techs.length); - queues.minorTech.addPlan(new m.ResearchPlan(gameState, techs[p][0])); + queues.minorTech.addPlan(new m.ResearchPlan(gameState, pickRandom(techs)[0])); }; m.ResearchManager.prototype.CostSum = function(cost) diff --git a/binaries/data/mods/public/simulation/components/BuildingAI.js b/binaries/data/mods/public/simulation/components/BuildingAI.js index efacf79c5f..7869a99cfb 100644 --- a/binaries/data/mods/public/simulation/components/BuildingAI.js +++ b/binaries/data/mods/public/simulation/components/BuildingAI.js @@ -300,7 +300,7 @@ BuildingAI.prototype.FireArrows = function() arrowsToFire = this.arrowsLeft; else arrowsToFire = Math.min( - Math.round(2 * Math.random() * this.GetArrowCount() / roundCount), + randIntInclusive(0, 2 * this.GetArrowCount() / roundCount), this.arrowsLeft ); diff --git a/binaries/data/mods/public/simulation/components/Formation.js b/binaries/data/mods/public/simulation/components/Formation.js index d80e755aa4..4d2303c521 100644 --- a/binaries/data/mods/public/simulation/components/Formation.js +++ b/binaries/data/mods/public/simulation/components/Formation.js @@ -667,7 +667,7 @@ Formation.prototype.ComputeFormationOffsets = function(active, positions) for (var i = 0; i < count; ++i) { - var obj = new Vector2D(Math.random()*width, Math.random()*width); + var obj = new Vector2D(randFloat(0, width), randFloat(0, width)); obj.row = 1; obj.column = i + 1; offsets.push(obj); @@ -720,13 +720,9 @@ Formation.prototype.ComputeFormationOffsets = function(active, positions) x += side * centerGap / 2; } var column = Math.ceil(n/2) + Math.ceil(c/2) * side; - var r1 = 0; - var r2 = 0; - if (this.sloppyness != 0) - { - r1 = (Math.random() * 2 - 1) * this.sloppyness; - r2 = (Math.random() * 2 - 1) * this.sloppyness; - } + var r1 = randFloat(-1, 1) * this.sloppyness; + var r2 = randFloat(-1, 1) * this.sloppyness; + offsets.push(new Vector2D(x + r1, z + r2)); offsets[offsets.length - 1].row = r+1; offsets[offsets.length - 1].column = column; diff --git a/binaries/data/mods/public/simulation/components/Player.js b/binaries/data/mods/public/simulation/components/Player.js index 1c017bc309..58771fad68 100644 --- a/binaries/data/mods/public/simulation/components/Player.js +++ b/binaries/data/mods/public/simulation/components/Player.js @@ -308,7 +308,7 @@ Player.prototype.TrySubtractResources = function(amounts) Player.prototype.GetNextTradingGoods = function() { - var value = 100*Math.random(); + var value = randFloat(0, 100); var last = this.tradingGoods.length - 1; var sumProba = 0; for (var i = 0; i < last; ++i) diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index 1ede2f69b8..a6cee5e991 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -3225,7 +3225,7 @@ UnitAI.prototype.UnitFsmSpec = { this.SelectAnimation("walk", false, this.GetWalkSpeed()); this.MoveRandomly(+this.template.RoamDistance); // Set a random timer to switch to feeding state - this.StartTimer(RandomInt(+this.template.RoamTimeMin, +this.template.RoamTimeMax)); + this.StartTimer(randIntInclusive(+this.template.RoamTimeMin, +this.template.RoamTimeMax)); this.SetFacePointAfterMove(false); }, @@ -3270,7 +3270,7 @@ UnitAI.prototype.UnitFsmSpec = { // Stop and eat for a while this.SelectAnimation("feeding"); this.StopMoving(); - this.StartTimer(RandomInt(+this.template.FeedTimeMin, +this.template.FeedTimeMax)); + this.StartTimer(randIntInclusive(+this.template.FeedTimeMin, +this.template.FeedTimeMax)); }, "leave": function() { @@ -5989,8 +5989,8 @@ UnitAI.prototype.MoveRandomly = function(distance) // Randomly adjust the range's center a bit, so we tend to prefer // moving in random directions (if there's nothing in the way) - var tx = pos.x + (2*Math.random()-1)*jitter; - var tz = pos.z + (2*Math.random()-1)*jitter; + var tx = pos.x + randFloat(-1, 1) * jitter; + var tz = pos.z + randFloat(-1, 1) * jitter; var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); cmpMotion.MoveToPointRange(tx, tz, distance, distance); diff --git a/binaries/data/mods/public/simulation/helpers/Random.js b/binaries/data/mods/public/simulation/helpers/Random.js deleted file mode 100644 index 254084c83d..0000000000 --- a/binaries/data/mods/public/simulation/helpers/Random.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Returns a random integer from min (inclusive) to max (exclusive) - */ -function RandomInt(min, max) -{ - return Math.floor(min + Math.random() * (max-min)); -} - -Engine.RegisterGlobal("RandomInt", RandomInt); diff --git a/binaries/data/mods/public/simulation/helpers/WeightedList.js b/binaries/data/mods/public/simulation/helpers/WeightedList.js index 5fe0646b17..3a51fabf29 100644 --- a/binaries/data/mods/public/simulation/helpers/WeightedList.js +++ b/binaries/data/mods/public/simulation/helpers/WeightedList.js @@ -31,9 +31,9 @@ WeightedList.prototype.itemAt = function(index) }; WeightedList.prototype.randomIndex = function() { - var element, - targetWeight = Math.random() * this.totalWeight, - cumulativeWeight = 0; + var element; + var targetWeight = randFloat(0, this.totalWeight); + var cumulativeWeight = 0; for (var index = 0; index < this.elements.length; index++) { element = this.elements[index];