Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // name, lvl, modifiers, weights
- var rawData = `MapAtlasMapsHave20PercentQuality 1 "Maps have 20% Quality
- Unique Bosses deal 20% increased Damage" default 1,000
- MapAtlasContainsAdditionalRandomBoss 1 Areas are inhabited by an additional Invasion Boss "hall_of_grandmasters 0
- default 1,000"
- MapAtlasClustersOfBloodwormBarrels 1 Areas contain 30 additional Clusters of Mysterious Barrels default 850
- MapAtlasPhysicalDamageAndPacks_ 1 "Areas contain additional Monsters that deal Physical Damage
- Players and Monsters take 10% increased Physical Damage" "no_monster_packs 0
- default 1,000"
- MapAtlasHighValueEnemiesOnOwnTeam 1 Areas contain hunted traitors "no_monster_packs 0
- default 100"
- MapAtlasMagicAndRareMapsHaveAdditionalPacks 1 "Magic Maps contain additional Magic Monsters
- Rare Maps contain additional Rare Monsters
- Normal Maps contain additional Normal Monsters" "unique_map 0
- no_monster_packs 0
- default 1,000"
- MapAtlasMoveAttackCastSpeed 1 "20% increased Player and Monster Movement Speed
- 20% increased Monster Attack Speed
- 20% increased Monster Cast Speed" default 1,000
- MapAtlasMontersThatConvertOnDeath__ 1 Areas contain additional Monsters that Convert when Killed "no_monster_packs 0
- default 1,000"
- MapAtlasUnIDedMapsGrantQuantity 1 "30% increased Quantity of Items found in Unidentified Maps
- Found Items drop Identified in Identified Maps
- 15% increased Pack Size in Unidentified Maps" default 1,000
- MapAtlasReducedReflectedDamageTakenMirroredRares 1 "Players cannot take Reflected Damage
- Areas contain additional Packs with Mirrored Rare Monsters" default 1,000
- MapAtlasPlayersDamageDealtAndTakenWhileStationary_ 1 "Players deal 100% increased Damage while stationary
- Players take 10% increased Damage while stationary" "vaults_of_atziri 0
- default 1,000"
- MapAtlasFireDamageAndPacks 1 "Areas contain additional Monsters that deal Fire Damage
- Players and Monsters take 10% increased Fire Damage" "no_monster_packs 0
- default 1,000"
- MapAtlasGroundHaste 1 Areas contain Slipstreams default 1,000
- MapAtlasInstantFlasksAndHealingMonsters 1 "Life and Mana Recovery from Flasks are instant
- Areas contain additional Monsters that Heal" default 1,000
- MapAtlasOnslaughtWhenHitAndOnslaughtEffect 1 "Players and Monsters have Onslaught if they have been Hit Recently
- 100% increased effect of Onslaught on Players and Monsters" "vaults_of_atziri 0
- default 1,000"
- MapAtlasColdDamageAndPacks 1 "Areas contain additional Monsters that deal Cold Damage
- Players and Monsters take 10% increased Cold Damage" "no_monster_packs 0
- default 1,000"
- MapAtlasStrongboxMonstersEnrageAndDrops 1 "Strongbox Monsters have 500% increased Item Quantity
- Strongbox Monsters are Enraged
- Areas contain an extra Strongbox" "hall_of_grandmasters 0
- no_strongboxes 0
- default 1,000"
- MapAtlasMagicPackSize 1 30% increased Magic Pack Size "no_monster_packs 0
- default 1,000"
- MapAtlasBodyguardsAndBossMapDrops 1 "Unique Bosses are accompanied by Bodyguards
- An additional Map drops on Completing Areas" "no_boss 0
- default 1,000"
- MapAtlasLightningDamageAndPacks 1 "Areas contain additional Monsters that deal Lightning Damage
- Players and Monsters take 10% increased Lightning Damage" "no_monster_packs 0
- default 1,000"
- MapAtlasPoisonEffectsAndParasites 1 "Players deal 10% increased Damage for each Poison on them
- Players have 5% increased Movement Speed for each Poison on them
- Areas contain additional Poisonous Monsters" default 1,000
- MapAtlasRogueExilesDamageAndDropAdditionalJewels 1 "Rogue Exiles deal 20% increased Damage
- Rogue Exiles have 20% increased Maximum Life
- Rogue Exiles drop 2 additional Jewels
- Areas are inhabited by 1 additional Rogue Exile" "no_monster_packs 0
- default 1,000"
- MapAtlasCurseEffectOnPlayersAndMonsters 73 "20% reduced Effect of Curses on Monsters
- 60% reduced Effect of Curses on Players" "vaults_of_atziri 0
- default 1,000"
- MapAtlasContainsZanaMission 73 Areas contain Zana "unique_map 0
- default 200"
- MapAtlasMapQualityBonusAlsoAppliesToRarity 73 "Quality bonus of Maps also applies to Rarity of Items found
- Unique Bosses have 20% increased Life" default 1,000
- MapAtlasStrongboxesAreCorruptedAndUnided 73 "Strongboxes in Areas are Corrupted
- Areas contain an extra Strongbox
- Strongboxes in Areas are at least Rare Rarity" "no_strongboxes 0
- default 200"
- MapAtlasChaosDamageAndPacks_ 73 "Areas contain additional Monsters that deal Chaos Damage
- Players and Monsters take 10% increased Chaos Damage" "no_monster_packs 0
- default 1,000"
- MapAtlasUniqueMonstersDropCorruptedItems 73 Unique Monsters drop Corrupted Items "vaults_of_atziri 0
- hall_of_grandmasters 0
- default 200"
- MapAtlasCorruptedMapBossesDropAdditionalVaalItems 78 "Unique Bosses of Corrupted Maps drop an additional Vaal Item
- Found Items have a 5% chance to drop Corrupted in Areas" default 1,000
- MapAtlasBloodlinesDropAdditionalCurrency 78 Nemesis Monsters drop 3 additional Currency Items "no_monster_packs 0
- default 200"
- MapAtlasFishy 78 Areas are Alluring default 10`;
- // tier "enum"
- var tier = {
- WHITE: 1,
- YELLOW: 73,
- RED: 78,
- };
- function getSextantCost(tier) {
- switch (tier) {
- case 1:
- return 2.0; // 4 chaos
- case 73:
- return 4.0; // 5 chaos
- case 78:
- return 8.0; // 6 chaos
- }
- }
- var maxIterations = 500000;
- var mapData = {
- "Shaped Primordial Pool": {
- tier: tier.YELLOW,
- adjacent: [
- "Cavern",
- "Waste Pool",
- "Phantasmagoria",
- "Burial Chambers",
- ]
- },
- "Canyon": {
- tier: tier.YELLOW,
- adjacent: [
- "Waste Pool",
- ]
- },
- "Vaal City": {
- tier: tier.YELLOW,
- adjacent: [
- "Burial Chambers",
- "Vaults of Atziri",
- "Arachnid Tomb",
- "Olmec's Sanctum",
- "Shore",
- "Catacombs",
- ]
- },
- "Arachnid Tomb": {
- tier: tier.YELLOW,
- adjacent: [
- "Vaults of Atziri",
- "Shore",
- "Vaal City",
- "Vaal Pyramid",
- ]
- },
- "Shore": {
- tier: tier.YELLOW,
- adjacent: [
- "Arachnid Tomb",
- "Vaal City",
- "Catacombs",
- "Olmec's Sanctum",
- ]
- },
- "Catacombs": {
- tier: tier.YELLOW,
- adjacent: [
- "Vaal City",
- "Shore",
- "Olmec's Sanctum",
- ]
- },
- "Olmec's Sanctum": {
- tier: tier.YELLOW,
- adjacent: [
- "Vaal City",
- "Shore",
- "Catacombs",
- ]
- },
- "Vaal Pyramid": {
- tier: tier.WHITE,
- adjacent: [
- "Phantasmagoria",
- "Burial Chambers",
- "Marshes",
- "Vaults of Atziri",
- "Arachnid Tomb",
- ]
- },
- "Marshes": {
- tier: tier.WHITE,
- adjacent: [
- "Phantasmagoria",
- "Vaal Pyramid",
- ]
- },
- "Phantasmagoria": {
- tier: tier.WHITE,
- adjacent: [
- "Shaped Primordial Pool",
- "Vaal Pyramid",
- "Marshes",
- ]
- },
- "Burial Chambers": {
- tier: tier.WHITE,
- adjacent: [
- "Shaped Primordial Pool",
- "Vaal Pyramid",
- "Vaults of Atziri",
- "Vaal City",
- ]
- },
- "Cavern": {
- tier: tier.WHITE,
- adjacent: [
- "Shaped Primordial Pool",
- "Waste Pool",
- ]
- },
- "Waste Pool": {
- tier: tier.WHITE,
- adjacent: [
- "Shaped Primordial Pool",
- "Cavern",
- "Canyon",
- "Channel",
- ]
- },
- "Channel": {
- tier: tier.WHITE,
- adjacent: [
- "Waste Pool",
- "Acid Lakes",
- "Mesa",
- "Factory",
- ]
- },
- "Vaults of Atziri": {
- tier: tier.WHITE,
- adjacent: [
- "Burial Chambers",
- "Vaal City",
- "Arachnid Tomb",
- "Vaal Pyramid",
- ]
- },
- "Acid Lakes": {
- tier: tier.WHITE,
- adjacent: [
- "Channel",
- "Factory",
- ]
- },
- "Mesa": {
- tier: tier.WHITE,
- adjacent: [
- "Channel",
- ]
- },
- "Factory": {
- tier: tier.WHITE,
- adjacent: [
- "Channel",
- "Acid Lakes",
- ]
- },
- };
- var goodMods = {
- "MapAtlasFireDamageAndPacks": true,
- "MapAtlasColdDamageAndPacks": true,
- "MapAtlasLightningDamageAndPacks": true,
- "MapAtlasPhysicalDamageAndPacks_": true,
- "MapAtlasChaosDamageAndPacks_": true,
- "MapAtlasMontersThatConvertOnDeath__": true,
- "MapAtlasInstantFlasksAndHealingMonsters": true,
- "MapAtlasMagicAndRareMapsHaveAdditionalPacks": true,
- "MapAtlasHighValueEnemiesOnOwnTeam": true,
- "MapAtlasPoisonEffectsAndParasites": true,
- "MapAtlasClustersOfBloodwormBarrels": true,
- "MapAtlasClustersOfParasiteBarrels": true,
- "MapAtlasClustersOfWealthyBarrels": true,
- "MapAtlasClustersOfBeaconBarrels": true,
- "MapAtlasClustersOfVolatileBarrels": true,
- "MapAtlasMagicPackSize": true,
- "MapAtlasUniqueMonstersDropCorruptedItems": true,
- "MapAtlasBloodlinesDropAdditionalCurrency": true,
- "MapAtlasReducedReflectedDamageTakenMirroredRares": true,
- "MapAtlasStrongboxesAreCorruptedAndUnided": true,
- "MapAtlasStrongboxMonstersEnrageAndDrops": true,
- }
- var parsed = rawData.match(/.+\t.+\t[^\t]+\t[^\t]+\n/g);
- for (var i = 0; i < parsed.length; i++) {
- var str = parsed[i];
- str = str.match(/[^\t]+/g);
- parsed[i] = {
- name: str[0],
- level: parseInt(str[1]),
- mods: str[2].match(/[^\n"]+/g),
- weights: str[3].match(/[^\n"]+/g),
- }
- var weightsObj = {};
- for (var j = 0; j < parsed[i].weights.length; j++) {
- var r = parsed[i].weights[j].split(/\s/);
- weightsObj[r[0]] = parseInt(r[1].replace(",", ""));
- }
- parsed[i].weights = weightsObj;
- }
- var maps = {};
- // Map prototype -------------------------------------------------
- // constructor
- function Map(name, tier, adj, activeMod) {
- this.name = name;
- this.tier = tier;
- this.sextantCost = getSextantCost(this.tier);
- //console.log( this.name + " sextant cost is " + this.sextantCost + "c.");
- if (activeMod) {
- for (var mod in parsed) {
- if (mod.name == activeMod) {
- this.mod = mod;
- break;
- }
- }
- }
- this.adjacent = adj;
- maps[this.name] = this;
- }
- Map.prototype.getBlockedMods = function(log) {
- if (log) {
- console.log("Blocked mods for " + this.name + ":");
- }
- var blocked = {};
- for (var i in this.adjacent) {
- if (this.adjacent[i].mod) {
- if (log) {
- console.log(this.adjacent[i].name + " blocks " + this.adjacent[i].mod.name);
- }
- blocked[this.adjacent[i].mod.name] = true;
- }
- for (var j in this.adjacent[i].adjacent) {
- if (this.adjacent[i].adjacent[j].mod) {
- if (log) {
- console.log(this.adjacent[i].adjacent[j].name + " blocks " + this.adjacent[i].adjacent[j].mod.name);
- }
- blocked[this.adjacent[i].adjacent[j].mod.name] = true;
- }
- }
- }
- return blocked;
- }
- Map.prototype.getFilteredModList = function() {
- var blocked = this.getBlockedMods();
- return parsed.filter(function(mod) {
- if (this.tier < mod.level || blocked[mod.name] || mod == this.mod) {
- return false;
- }
- return true;
- });
- }
- Map.prototype.roll = function() {
- var mods = this.getFilteredModList();
- var totalWeight = mods.reduce(function(acc, val) {
- return acc + val.weights.default;
- }, 0);
- var roll = Math.random() * totalWeight;
- var sum = 0;
- for (var mod in mods) {
- sum += mods[mod].weights.default;
- if (roll < sum) {
- this.mod = mods[mod];
- //console.log("Applied '" + this.mod.name + "' to " + this.name + ".");
- return this.sextantCost;
- }
- }
- }
- Map.prototype.isAdjacentTo = function(name) {
- return Boolean(this.adjacent.find(map => map.name === name));
- }
- // END Map prototype -------------------------------------------
- // START atlas prototype ---------------------------------------
- function Atlas(maps) {
- this.maps = maps;
- this.savedMods = {};
- for (var map in this.maps) {
- this.savedMods[this.maps[map].name] = this.maps[map].mod;
- }
- //console.log(this.savedMods);
- }
- Atlas.prototype.reset = function() {
- for (var map in this.savedMods) {
- this.maps[map].mod = this.savedMods[map];
- }
- };
- // END atlas prototype -----------------------------------------
- // create map objects
- for (var map in mapData) {
- new Map(map, mapData[map].tier, mapData[map].adjacent, mapData[map].mod);
- }
- // objectify adjacent arrays
- for (var k in maps) {
- var map = maps[k];
- map.adjacent = map.adjacent.map(function(element) {
- if (!maps[element]) {
- throw new Error("Could not convert '" + element + "' to object");
- }
- return maps[element];
- });
- }
- // sanity check adjacents
- for (var k in maps) {
- var map = maps[k];
- map.adjacent.forEach(function(a) {
- if (!a.isAdjacentTo(map.name)) {
- throw new Error("1 sided adjacent relation: " + map.name + ", " + a.name);
- }
- })
- }
- // block mods
- function applyBlockMod(originMap, currentMap) {
- if (currentMap.name == "Shaped Primordial Pool" || currentMap.isAdjacentTo("Shaped Primordial Pool")) {
- return;
- }
- if (currentMap.mod) {
- return;
- }
- // filter out good mods
- // filter non-white mods
- var mods = originMap.getFilteredModList().filter(function(mod) {
- return !goodMods[mod.name];
- }).filter(function(mod) {
- return mod.level == tier.WHITE;
- });
- // sort by weight, descending
- mods.sort(function(a, b) {
- return b.weights.default-a.weights.default;
- });
- currentMap.mod = mods[0];
- console.log("Applied '" + currentMap.mod.name + "' to " + currentMap.name + ".");
- }
- // First, apply sextants to maps 2 degrees away from Pool.
- var m = maps["Shaped Primordial Pool"];
- m.adjacent.forEach(function(map) {
- map.adjacent.forEach(function(map2) {
- applyBlockMod(m, map2);
- });
- });
- // Second, block sextants on Burial Chambers.
- m = maps["Burial Chambers"];
- m.adjacent.forEach(function(map) {
- map.adjacent.forEach(function(map2) {
- applyBlockMod(m, map2);
- });
- });
- for (var m in maps) {
- var m = maps[m];
- if (!m.isAdjacentTo("Shaped Primordial Pool")) {
- continue;
- }
- // apply to adjacent maps
- for (var i = 0; i < m.adjacent.length; i++) {
- var c = m.adjacent[i];
- applyBlockMod(m, c);
- }
- // apply to 2nd degree adjacent maps
- for (var i = 0; i < m.adjacent.length; i++) {
- var proxy = m.adjacent[i];
- if (proxy.name == "Shaped Primordial Pool") {
- continue;
- }
- for (var j = 0; j < proxy.adjacent.length; j++) {
- var c = proxy.adjacent[j];
- applyBlockMod(m, c);
- }
- }
- }
- // create atlas object and snapshot blocked mods
- var atl = new Atlas(maps);
- // heaps permute --------------------------------------------
- var swap = function(array, pos1, pos2) {
- var temp = array[pos1];
- array[pos1] = array[pos2];
- array[pos2] = temp;
- };
- var heapsPermute = function(array, output, n) {
- n = n || array.length; // set n default to array.length
- if (n === 1) {
- output(array);
- } else {
- for (var i = 1; i <= n; i += 1) {
- heapsPermute(array, output, n - 1);
- if (n % 2) {
- var j = 1;
- } else {
- var j = i;
- }
- swap(array, j - 1, n - 1); // -1 to account for javascript zero-indexing
- }
- }
- };
- // END heaps permute -------------------------------------------
- // START result prototype --------------------------------------
- function result(order) {
- this.order = order;
- this.sum_x = 0;
- this.sum_x2 = 0;
- this.n = 0;
- }
- result.prototype.mean = function() {
- return this.sum_x / this.n;
- }
- result.prototype.stdDev = function() {
- var mean = this.mean();
- return Math.sqrt(this.sum_x2 / this.n - mean * mean);
- }
- result.prototype.generateSamples = function(n) {
- // Apply sextants to maps
- for (var iter = 0; iter < n; iter++) {
- atl.reset();
- var cost = 0;
- // First, roll any mod on each map in order.
- this.order.forEach(function(m) {
- if (!maps[m].mod) {
- cost += maps[m].roll();
- }
- });
- // Second, finish rolling each map to a good mod, again respecting the order.
- this.order.forEach(function(m) {
- // While no/bad sextant, keep rolling
- while (!(maps[m].mod && goodMods[maps[m].mod.name])) {
- cost += maps[m].roll();
- }
- });
- this.sum_x += cost;
- this.sum_x2 += cost * cost;
- this.n++;
- }
- }
- result.prototype.confidenceInterval = function() {
- return 1.645 * (this.stdDev() / Math.sqrt(this.n));
- }
- // END result prototype ------------------------------------------
- function countKeys(a) {
- var n = 0;
- for (var k in a) {
- n++;
- }
- return n;
- }
- var results = [];
- var mapsToRoll = [];
- for (var map in maps) {
- if (map == "Shaped Primordial Pool" || maps[map].isAdjacentTo("Shaped Primordial Pool")) {
- console.log(map + " blocks " + countKeys(maps[map].getBlockedMods()) + " mods");
- mapsToRoll.push(map);
- }
- }
- // create a result entry for every permutation
- heapsPermute(mapsToRoll, function(order) {
- /*if ( order[0] !== "Shaped Primordial Pool" ) {
- return;
- }*/
- results.push(new result(order.slice(0)));
- });
- while (true) {
- var results_b = results.filter(function(r) {
- if (r == results[0] || results[0].n == 0) {
- return true;
- }
- return r.mean() - r.confidenceInterval() <= results[0].mean() + results[0].confidenceInterval();
- });
- if (results_b.length <= 1 || results_b[0].n >= maxIterations) {
- break;
- }
- console.log(results[0].n + " iterations (" + results_b.length + " remaining permutations)");
- var iterTarget = results[0].n + 5000 / Math.sqrt(results_b.length);
- results_b.forEach(function(r) {
- r.generateSamples(iterTarget - r.n);
- });
- results.sort(function(a, b) {
- return a.mean() - b.mean();
- });
- }
- console.log("Results (up to " + maxIterations + " iterations):");
- for (var i = 0; i < results.length; i++) {
- var str = "" + results[i].mean().toFixed(3) + " ± " + results[i].confidenceInterval().toFixed(3) + "c";
- for (var j = 0; j < results[i].order.length; j++) {
- if (j > 0) {
- str += ","
- };
- str += " " + results[i].order[j];
- }
- console.log(str);
- }
Add Comment
Please, Sign In to add comment