View difference between Paste ID: gnb2N5j4 and nSXtyEwa
SHOW: | | - or go back to the newest paste.
1
// name, lvl, modifiers, weights
2
var rawData = `MapAtlasMapsHave20PercentQuality	1	"Maps have 20% Quality
3
Unique Bosses deal 20% increased Damage"	default 1,000
4
MapAtlasContainsAdditionalRandomBoss	1	Areas are inhabited by an additional Invasion Boss	"hall_of_grandmasters 0
5
default 1,000"
6
MapAtlasClustersOfBloodwormBarrels	1	Areas contain 30 additional Clusters of Mysterious Barrels	default 850
7
MapAtlasPhysicalDamageAndPacks_	1	"Areas contain additional Monsters that deal Physical Damage
8
Players and Monsters take 10% increased Physical Damage"	"no_monster_packs 0
9
default 1,000"
10
MapAtlasHighValueEnemiesOnOwnTeam	1	Areas contain hunted traitors	"no_monster_packs 0
11
default 100"
12
MapAtlasMagicAndRareMapsHaveAdditionalPacks	1	"Magic Maps contain additional Magic Monsters
13
Rare Maps contain additional Rare Monsters
14
Normal Maps contain additional Normal Monsters"	"unique_map 0
15
no_monster_packs 0
16
default 1,000"
17
MapAtlasMoveAttackCastSpeed	1	"20% increased Player and Monster Movement Speed
18
20% increased Monster Attack Speed
19
20% increased Monster Cast Speed"	default 1,000
20
MapAtlasMontersThatConvertOnDeath__	1	Areas contain additional Monsters that Convert when Killed	"no_monster_packs 0
21
default 1,000"
22
MapAtlasUnIDedMapsGrantQuantity	1	"30% increased Quantity of Items found in Unidentified Maps
23
Found Items drop Identified in Identified Maps
24
15% increased Pack Size in Unidentified Maps"	default 1,000
25
MapAtlasReducedReflectedDamageTakenMirroredRares	1	"Players cannot take Reflected Damage
26
Areas contain additional Packs with Mirrored Rare Monsters"	default 1,000
27
MapAtlasPlayersDamageDealtAndTakenWhileStationary_	1	"Players deal 100% increased Damage while stationary
28
Players take 10% increased Damage while stationary"	"vaults_of_atziri 0
29
default 1,000"
30
MapAtlasFireDamageAndPacks	1	"Areas contain additional Monsters that deal Fire Damage
31
Players and Monsters take 10% increased Fire Damage"	"no_monster_packs 0
32
default 1,000"
33
MapAtlasGroundHaste	1	Areas contain Slipstreams	default 1,000
34
MapAtlasInstantFlasksAndHealingMonsters	1	"Life and Mana Recovery from Flasks are instant
35
Areas contain additional Monsters that Heal"	default 1,000
36
MapAtlasOnslaughtWhenHitAndOnslaughtEffect	1	"Players and Monsters have Onslaught if they have been Hit Recently
37
100% increased effect of Onslaught on Players and Monsters"	"vaults_of_atziri 0
38
default 1,000"
39
MapAtlasColdDamageAndPacks	1	"Areas contain additional Monsters that deal Cold Damage
40
Players and Monsters take 10% increased Cold Damage"	"no_monster_packs 0
41
default 1,000"
42
MapAtlasStrongboxMonstersEnrageAndDrops	1	"Strongbox Monsters have 500% increased Item Quantity
43
Strongbox Monsters are Enraged
44
Areas contain an extra Strongbox"	"hall_of_grandmasters 0
45
no_strongboxes 0
46
default 1,000"
47
MapAtlasMagicPackSize	1	30% increased Magic Pack Size	"no_monster_packs 0
48
default 1,000"
49
MapAtlasBodyguardsAndBossMapDrops	1	"Unique Bosses are accompanied by Bodyguards
50
An additional Map drops on Completing Areas"	"no_boss 0
51
default 1,000"
52
MapAtlasLightningDamageAndPacks	1	"Areas contain additional Monsters that deal Lightning Damage
53
Players and Monsters take 10% increased Lightning Damage"	"no_monster_packs 0
54
default 1,000"
55
MapAtlasPoisonEffectsAndParasites	1	"Players deal 10% increased Damage for each Poison on them
56
Players have 5% increased Movement Speed for each Poison on them
57
Areas contain additional Poisonous Monsters"	default 1,000
58
MapAtlasRogueExilesDamageAndDropAdditionalJewels	1	"Rogue Exiles deal 20% increased Damage
59
Rogue Exiles have 20% increased Maximum Life
60
Rogue Exiles drop 2 additional Jewels
61
Areas are inhabited by 1 additional Rogue Exile"	"no_monster_packs 0
62
default 1,000"
63
MapAtlasCurseEffectOnPlayersAndMonsters	73	"20% reduced Effect of Curses on Monsters
64
60% reduced Effect of Curses on Players"	"vaults_of_atziri 0
65
default 1,000"
66
MapAtlasContainsZanaMission	73	Areas contain Zana	"unique_map 0
67
default 200"
68
MapAtlasMapQualityBonusAlsoAppliesToRarity	73	"Quality bonus of Maps also applies to Rarity of Items found
69
Unique Bosses have 20% increased Life"	default 1,000
70
MapAtlasStrongboxesAreCorruptedAndUnided	73	"Strongboxes in Areas are Corrupted
71
Areas contain an extra Strongbox
72
Strongboxes in Areas are at least Rare Rarity"	"no_strongboxes 0
73
default 200"
74
MapAtlasChaosDamageAndPacks_	73	"Areas contain additional Monsters that deal Chaos Damage
75
Players and Monsters take 10% increased Chaos Damage"	"no_monster_packs 0
76
default 1,000"
77
MapAtlasUniqueMonstersDropCorruptedItems	73	Unique Monsters drop Corrupted Items	"vaults_of_atziri 0
78
hall_of_grandmasters 0
79
default 200"
80
MapAtlasCorruptedMapBossesDropAdditionalVaalItems	78	"Unique Bosses of Corrupted Maps drop an additional Vaal Item
81
Found Items have a 5% chance to drop Corrupted in Areas"	default 1,000
82
MapAtlasBloodlinesDropAdditionalCurrency	78	Nemesis Monsters drop 3 additional Currency Items	"no_monster_packs 0
83
default 200"
84
MapAtlasFishy	78	Areas are Alluring	default 10`;
85
86
// tier "enum"
87
var tier = {
88-
	WHITE: 1,
88+
    WHITE: 1,
89-
  YELLOW: 73,
89+
    YELLOW: 73,
90-
  RED: 78,
90+
    RED: 78,
91
};
92
93
function getSextantCost(tier) {
94-
	switch ( tier ) {
94+
    switch (tier) {
95-
  	case 1:
95+
        case 1:
96-
    	return 2.0; // 4 chaos
96+
            return 2.0; // 4 chaos
97-
  	case 73:
97+
        case 73:
98-
    	return 4.0; // 5 chaos
98+
            return 4.0; // 5 chaos
99-
    case 78:
99+
        case 78:
100-
    	return 8.0; // 6 chaos
100+
            return 8.0; // 6 chaos
101-
  }
101+
102
}
103
104
var maxIterations = 500000;
105
106
var mapData = {
107-
	"Shaped Primordial Pool": {
107+
    "Shaped Primordial Pool": {
108-
  	tier: tier.YELLOW,
108+
        tier: tier.YELLOW,
109-
    adjacent: [
109+
        adjacent: [
110-
    	"Cavern",
110+
            "Cavern",
111-
      "Waste Pool",
111+
            "Waste Pool",
112-
      "Phantasmagoria",
112+
            "Phantasmagoria",
113-
      "Burial Chambers",
113+
            "Burial Chambers",
114-
    ]
114+
        ]
115-
  },
115+
    },
116-
	"Canyon": {
116+
    "Canyon": {
117-
  	tier: tier.YELLOW,
117+
        tier: tier.YELLOW,
118-
    adjacent: [
118+
        adjacent: [
119-
      "Waste Pool",
119+
            "Waste Pool",
120-
    ]
120+
        ]
121-
  },
121+
    },
122-
	"Vaal City": {
122+
    "Vaal City": {
123-
  	tier: tier.YELLOW,
123+
        tier: tier.YELLOW,
124-
    adjacent: [
124+
        adjacent: [
125-
    	"Burial Chambers",
125+
            "Burial Chambers",
126-
    	"Vaults of Atziri",
126+
            "Vaults of Atziri",
127-
    	"Arachnid Tomb",
127+
            "Arachnid Tomb",
128-
    	"Olmec's Sanctum",
128+
            "Olmec's Sanctum",
129-
    	"Shore",
129+
            "Shore",
130-
    	"Catacombs",
130+
            "Catacombs",
131-
    ]
131+
        ]
132-
  },
132+
    },
133-
	"Arachnid Tomb": {
133+
    "Arachnid Tomb": {
134-
  	tier: tier.YELLOW,
134+
        tier: tier.YELLOW,
135-
    adjacent: [
135+
        adjacent: [
136-
    	"Vaults of Atziri",
136+
            "Vaults of Atziri",
137-
    	"Shore",
137+
            "Shore",
138-
      "Vaal City",
138+
            "Vaal City",
139-
      "Vaal Pyramid",
139+
            "Vaal Pyramid",
140-
    ]
140+
        ]
141-
  },
141+
    },
142-
	"Shore": {
142+
    "Shore": {
143-
  	tier: tier.YELLOW,
143+
        tier: tier.YELLOW,
144-
    adjacent: [
144+
        adjacent: [
145-
    	"Arachnid Tomb",
145+
            "Arachnid Tomb",
146-
      "Vaal City",
146+
            "Vaal City",
147-
      "Catacombs",
147+
            "Catacombs",
148-
      "Olmec's Sanctum",
148+
            "Olmec's Sanctum",
149-
    ]
149+
        ]
150-
  },
150+
    },
151-
	"Catacombs": {
151+
    "Catacombs": {
152-
  	tier: tier.YELLOW,
152+
        tier: tier.YELLOW,
153-
    adjacent: [
153+
        adjacent: [
154-
      "Vaal City",
154+
            "Vaal City",
155-
      "Shore",
155+
            "Shore",
156-
      "Olmec's Sanctum",
156+
            "Olmec's Sanctum",
157-
    ]
157+
        ]
158-
  },
158+
    },
159-
	"Olmec's Sanctum": {
159+
    "Olmec's Sanctum": {
160-
  	tier: tier.YELLOW,
160+
        tier: tier.YELLOW,
161-
    adjacent: [
161+
        adjacent: [
162-
      "Vaal City",
162+
            "Vaal City",
163-
      "Shore",
163+
            "Shore",
164-
      "Catacombs",
164+
            "Catacombs",
165-
    ]
165+
        ]
166-
  },
166+
    },
167-
	"Vaal Pyramid": {
167+
    "Vaal Pyramid": {
168-
  	tier: tier.WHITE,
168+
        tier: tier.WHITE,
169-
    adjacent: [
169+
        adjacent: [
170-
      "Phantasmagoria",
170+
            "Phantasmagoria",
171-
      "Burial Chambers",
171+
            "Burial Chambers",
172-
      "Marshes",
172+
            "Marshes",
173-
      "Vaults of Atziri",
173+
            "Vaults of Atziri",
174-
      "Arachnid Tomb",
174+
            "Arachnid Tomb",
175-
    ]
175+
        ]
176-
  },
176+
    },
177-
	"Marshes": {
177+
    "Marshes": {
178-
  	tier: tier.WHITE,
178+
        tier: tier.WHITE,
179-
    adjacent: [
179+
        adjacent: [
180-
      "Phantasmagoria",
180+
            "Phantasmagoria",
181-
      "Vaal Pyramid",
181+
            "Vaal Pyramid",
182-
    ]
182+
        ]
183-
  },
183+
    },
184-
	"Phantasmagoria": {
184+
    "Phantasmagoria": {
185-
  	tier: tier.WHITE,
185+
        tier: tier.WHITE,
186-
    adjacent: [
186+
        adjacent: [
187-
      "Shaped Primordial Pool",
187+
            "Shaped Primordial Pool",
188-
      "Vaal Pyramid",
188+
            "Vaal Pyramid",
189-
      "Marshes",
189+
            "Marshes",
190-
    ]
190+
        ]
191-
  },
191+
    },
192-
	"Burial Chambers": {
192+
    "Burial Chambers": {
193-
  	tier: tier.WHITE,
193+
        tier: tier.WHITE,
194-
    adjacent: [
194+
        adjacent: [
195-
      "Shaped Primordial Pool",
195+
            "Shaped Primordial Pool",
196-
      "Vaal Pyramid",
196+
            "Vaal Pyramid",
197-
      "Vaults of Atziri",
197+
            "Vaults of Atziri",
198-
      "Vaal City",
198+
            "Vaal City",
199-
    ]
199+
        ]
200-
  },
200+
    },
201-
	"Cavern": {
201+
    "Cavern": {
202-
  	tier: tier.WHITE,
202+
        tier: tier.WHITE,
203-
    adjacent: [
203+
        adjacent: [
204-
      "Shaped Primordial Pool",
204+
            "Shaped Primordial Pool",
205-
      "Waste Pool",
205+
            "Waste Pool",
206-
    ]
206+
        ]
207-
  },
207+
    },
208-
	"Waste Pool": {
208+
    "Waste Pool": {
209-
  	tier: tier.WHITE,
209+
        tier: tier.WHITE,
210-
    adjacent: [
210+
        adjacent: [
211-
      "Shaped Primordial Pool",
211+
            "Shaped Primordial Pool",
212-
      "Cavern",
212+
            "Cavern",
213-
      "Canyon",
213+
            "Canyon",
214-
      "Channel",
214+
            "Channel",
215-
    ]
215+
        ]
216-
  },
216+
    },
217-
	"Channel": {
217+
    "Channel": {
218-
  	tier: tier.WHITE,
218+
        tier: tier.WHITE,
219-
    adjacent: [
219+
        adjacent: [
220-
      "Waste Pool",
220+
            "Waste Pool",
221-
      "Acid Lakes",
221+
            "Acid Lakes",
222-
      "Mesa",
222+
            "Mesa",
223-
      "Factory",
223+
            "Factory",
224-
    ]
224+
        ]
225-
  },
225+
    },
226-
	"Vaults of Atziri": {
226+
    "Vaults of Atziri": {
227-
  	tier: tier.WHITE,
227+
        tier: tier.WHITE,
228-
    adjacent: [
228+
        adjacent: [
229-
      "Burial Chambers",
229+
            "Burial Chambers",
230-
      "Vaal City",
230+
            "Vaal City",
231-
      "Arachnid Tomb",
231+
            "Arachnid Tomb",
232-
      "Vaal Pyramid",
232+
            "Vaal Pyramid",
233-
    ]
233+
        ]
234-
  },
234+
    },
235-
	"Acid Lakes": {
235+
    "Acid Lakes": {
236-
  	tier: tier.WHITE,
236+
        tier: tier.WHITE,
237-
    adjacent: [
237+
        adjacent: [
238-
      "Channel",
238+
            "Channel",
239-
      "Factory",
239+
            "Factory",
240-
    ]
240+
        ]
241-
  },
241+
    },
242-
	"Mesa": {
242+
    "Mesa": {
243-
  	tier: tier.WHITE,
243+
        tier: tier.WHITE,
244-
    adjacent: [
244+
        adjacent: [
245-
      "Channel",
245+
            "Channel",
246-
    ]
246+
        ]
247-
  },
247+
    },
248-
	"Factory": {
248+
    "Factory": {
249-
  	tier: tier.WHITE,
249+
        tier: tier.WHITE,
250-
    adjacent: [
250+
        adjacent: [
251-
      "Channel",
251+
            "Channel",
252-
      "Acid Lakes",
252+
            "Acid Lakes",
253-
    ]
253+
        ]
254-
  },
254+
    },
255
};
256
257
var goodMods = {
258-
  "MapAtlasFireDamageAndPacks": true,
258+
    "MapAtlasFireDamageAndPacks": true,
259-
  "MapAtlasColdDamageAndPacks": true,
259+
    "MapAtlasColdDamageAndPacks": true,
260-
  "MapAtlasLightningDamageAndPacks": true,
260+
    "MapAtlasLightningDamageAndPacks": true,
261-
  "MapAtlasPhysicalDamageAndPacks_": true,
261+
    "MapAtlasPhysicalDamageAndPacks_": true,
262-
  "MapAtlasChaosDamageAndPacks_": true,
262+
    "MapAtlasChaosDamageAndPacks_": true,
263-
  "MapAtlasMontersThatConvertOnDeath__": true,
263+
    "MapAtlasMontersThatConvertOnDeath__": true,
264-
  "MapAtlasInstantFlasksAndHealingMonsters": true,
264+
    "MapAtlasInstantFlasksAndHealingMonsters": true,
265-
  "MapAtlasMagicAndRareMapsHaveAdditionalPacks": true,
265+
    "MapAtlasMagicAndRareMapsHaveAdditionalPacks": true,
266-
  "MapAtlasHighValueEnemiesOnOwnTeam": true,
266+
    "MapAtlasHighValueEnemiesOnOwnTeam": true,
267-
  "MapAtlasPoisonEffectsAndParasites": true,
267+
    "MapAtlasPoisonEffectsAndParasites": true,
268-
	"MapAtlasClustersOfBloodwormBarrels": true,
268+
    "MapAtlasClustersOfBloodwormBarrels": true,
269-
  "MapAtlasClustersOfParasiteBarrels": true,
269+
    "MapAtlasClustersOfParasiteBarrels": true,
270-
  "MapAtlasClustersOfWealthyBarrels": true,
270+
    "MapAtlasClustersOfWealthyBarrels": true,
271-
  "MapAtlasClustersOfBeaconBarrels": true,
271+
    "MapAtlasClustersOfBeaconBarrels": true,
272-
  "MapAtlasClustersOfVolatileBarrels": true,
272+
    "MapAtlasClustersOfVolatileBarrels": true,
273-
  "MapAtlasMagicPackSize": true,
273+
    "MapAtlasMagicPackSize": true,
274-
  "MapAtlasUniqueMonstersDropCorruptedItems": true,
274+
    "MapAtlasUniqueMonstersDropCorruptedItems": true,
275-
  "MapAtlasBloodlinesDropAdditionalCurrency": true,
275+
    "MapAtlasBloodlinesDropAdditionalCurrency": true,
276-
  "MapAtlasReducedReflectedDamageTakenMirroredRares": true,
276+
    "MapAtlasReducedReflectedDamageTakenMirroredRares": true,
277-
  "MapAtlasStrongboxesAreCorruptedAndUnided": true,
277+
    "MapAtlasStrongboxesAreCorruptedAndUnided": true,
278-
  "MapAtlasStrongboxMonstersEnrageAndDrops": true,
278+
    "MapAtlasStrongboxMonstersEnrageAndDrops": true,
279
}
280
281-
var parsed = rawData.match( /.+\t.+\t[^\t]+\t[^\t]+\n/g );
281+
var parsed = rawData.match(/.+\t.+\t[^\t]+\t[^\t]+\n/g);
282
283-
for ( var i = 0; i < parsed.length; i++ ) {
283+
for (var i = 0; i < parsed.length; i++) {
284-
	var str = parsed[i];
284+
    var str = parsed[i];
285-
  
285+
286-
  str = str.match( /[^\t]+/g );
286+
    str = str.match(/[^\t]+/g);
287-
  
287+
288-
  parsed[i] = {
288+
    parsed[i] = {
289-
  	name: str[0],
289+
        name: str[0],
290-
    level: parseInt(str[1]),
290+
        level: parseInt(str[1]),
291-
    mods: str[2].match( /[^\n"]+/g ),
291+
        mods: str[2].match(/[^\n"]+/g),
292-
    weights: str[3].match( /[^\n"]+/g ),
292+
        weights: str[3].match(/[^\n"]+/g),
293-
  }
293+
294-
  
294+
295-
  var weightsObj = {};
295+
    var weightsObj = {};
296-
  
296+
297-
  for ( var j = 0; j < parsed[i].weights.length; j++ ) {
297+
    for (var j = 0; j < parsed[i].weights.length; j++) {
298-
  	var r = parsed[i].weights[j].split( /\s/ );
298+
        var r = parsed[i].weights[j].split(/\s/);
299-
    weightsObj[r[0]] = parseInt( r[1].replace(",", "") );
299+
        weightsObj[r[0]] = parseInt(r[1].replace(",", ""));
300-
  }
300+
301-
  
301+
302-
  parsed[i].weights = weightsObj;
302+
    parsed[i].weights = weightsObj;
303
}
304
305
var maps = {};
306
307
// Map prototype -------------------------------------------------
308
309
// constructor
310
311
function Map(name, tier, adj, activeMod) {
312-
	this.name = name;
312+
    this.name = name;
313-
  this.tier = tier;
313+
    this.tier = tier;
314-
  this.sextantCost = getSextantCost( this.tier );
314+
    this.sextantCost = getSextantCost(this.tier);
315-
  //console.log( this.name + " sextant cost is " + this.sextantCost + "c.");
315+
    //console.log( this.name + " sextant cost is " + this.sextantCost + "c.");
316-
  if ( activeMod ) {
316+
    if (activeMod) {
317-
  	for ( var mod in parsed ) {
317+
        for (var mod in parsed) {
318-
    	if ( mod.name == activeMod ) {
318+
            if (mod.name == activeMod) {
319-
      	this.mod = mod;
319+
                this.mod = mod;
320
                break;
321-
      }
321+
            }
322
        }
323-
  }
323+
324-
  this.adjacent = adj;
324+
    this.adjacent = adj;
325-
  
325+
326-
  maps[this.name] = this;
326+
    maps[this.name] = this;
327
}
328
329-
Map.prototype.getBlockedMods = function( log ) {
329+
Map.prototype.getBlockedMods = function(log) {
330-
	if ( log ) { console.log("Blocked mods for " + this.name + ":"); }
330+
    if (log) {
331-
	var blocked = {};
331+
        console.log("Blocked mods for " + this.name + ":");
332-
	for ( var i in this.adjacent ) {
332+
333-
  	if ( this.adjacent[i].mod ) {
333+
    var blocked = {};
334-
    	if ( log ) { console.log(this.adjacent[i].name + " blocks " + this.adjacent[i].mod.name); }
334+
    for (var i in this.adjacent) {
335-
  		blocked[this.adjacent[i].mod.name] = true;
335+
        if (this.adjacent[i].mod) {
336
            if (log) {
337-
    
337+
                console.log(this.adjacent[i].name + " blocks " + this.adjacent[i].mod.name);
338-
    for ( var j in this.adjacent[i].adjacent ) {
338+
            }
339-
    	if ( this.adjacent[i].adjacent[j].mod ) {
339+
            blocked[this.adjacent[i].mod.name] = true;
340-
    		if ( log ) { console.log(this.adjacent[i].adjacent[j].name + " blocks " + this.adjacent[i].adjacent[j].mod.name); }
340+
        }
341-
    		blocked[this.adjacent[i].adjacent[j].mod.name] = true;
341+
342-
      }
342+
        for (var j in this.adjacent[i].adjacent) {
343
            if (this.adjacent[i].adjacent[j].mod) {
344-
  }
344+
                if (log) {
345-
  return blocked;
345+
                    console.log(this.adjacent[i].adjacent[j].name + " blocks " + this.adjacent[i].adjacent[j].mod.name);
346
                }
347
                blocked[this.adjacent[i].adjacent[j].mod.name] = true;
348
            }
349-
	var blocked = this.getBlockedMods();
349+
        }
350
    }
351-
	return parsed.filter( function(mod) {
351+
    return blocked;
352-
  	if ( this.tier < mod.level || blocked[mod.name] || mod == this.mod ) {
352+
353-
    	return false;
353+
354
Map.prototype.getFilteredModList = function() {
355-
    return true;
355+
    var blocked = this.getBlockedMods();
356-
  } );
356+
357
    return parsed.filter(function(mod) {
358
        if (this.tier < mod.level || blocked[mod.name] || mod == this.mod) {
359
            return false;
360-
	var mods = this.getFilteredModList();
360+
        }
361-
	
361+
        return true;
362-
  var totalWeight = mods.reduce( function( acc, val ) {
362+
363-
  	return acc + val.weights.default;
363+
364-
  }, 0);
364+
365-
	var roll = Math.random() * totalWeight;
365+
366-
  var sum = 0;
366+
    var mods = this.getFilteredModList();
367-
  
367+
368-
  for ( var mod in mods ) {
368+
    var totalWeight = mods.reduce(function(acc, val) {
369-
  	sum += mods[mod].weights.default;
369+
        return acc + val.weights.default;
370-
  	if ( roll < sum ) {
370+
    }, 0);
371-
    	this.mod = mods[mod];
371+
    var roll = Math.random() * totalWeight;
372-
      //console.log("Applied '" + this.mod.name + "' to " + this.name + ".");
372+
    var sum = 0;
373-
      return this.sextantCost;
373+
374
    for (var mod in mods) {
375-
  }
375+
        sum += mods[mod].weights.default;
376
        if (roll < sum) {
377
            this.mod = mods[mod];
378-
Map.prototype.isAdjacentTo = function( name ) {
378+
            //console.log("Applied '" + this.mod.name + "' to " + this.name + ".");
379-
  return Boolean( this.adjacent.find( map => map.name === name ) );
379+
            return this.sextantCost;
380
        }
381
    }
382
}
383
384
Map.prototype.isAdjacentTo = function(name) {
385
    return Boolean(this.adjacent.find(map => map.name === name));
386-
function Atlas( maps ) {
386+
387-
	this.maps = maps;
387+
388-
	this.savedMods = {};
388+
389-
  
389+
390-
	for ( var map in this.maps ) {
390+
391-
  	this.savedMods[this.maps[map].name] = this.maps[map].mod;
391+
392-
  }
392+
function Atlas(maps) {
393-
  
393+
    this.maps = maps;
394-
  //console.log(this.savedMods);
394+
    this.savedMods = {};
395
396
    for (var map in this.maps) {
397
        this.savedMods[this.maps[map].name] = this.maps[map].mod;
398-
	for ( var map in this.savedMods ) {
398+
399-
  	this.maps[map].mod = this.savedMods[map];
399+
400-
  }
400+
    //console.log(this.savedMods);
401
}
402
403
Atlas.prototype.reset = function() {
404
    for (var map in this.savedMods) {
405
        this.maps[map].mod = this.savedMods[map];
406
    }
407-
for ( var map in mapData ) {
407+
408-
	new Map( map, mapData[map].tier, mapData[map].adjacent, mapData[map].mod );
408+
409
// END atlas prototype -----------------------------------------
410
411
// create map objects
412
413-
for ( var k in maps ) {
413+
for (var map in mapData) {
414-
	var map = maps[k];
414+
    new Map(map, mapData[map].tier, mapData[map].adjacent, mapData[map].mod);
415-
  
415+
416-
  map.adjacent = map.adjacent.map( function( element ) {
416+
417-
  	if ( ! maps[element] ) { 
417+
418-
    	throw new Error( "Could not convert '" + element + "' to object" );
418+
419
for (var k in maps) {
420-
  	return maps[element];
420+
    var map = maps[k];
421-
  } );
421+
422
    map.adjacent = map.adjacent.map(function(element) {
423
        if (!maps[element]) {
424
            throw new Error("Could not convert '" + element + "' to object");
425
        }
426-
for ( var k in maps ) {
426+
        return maps[element];
427-
	var map = maps[k];
427+
428-
  
428+
429-
  map.adjacent.forEach( function( a ) {
429+
430-
  	if ( ! a.isAdjacentTo( map.name ) ) {
430+
431-
    	throw new Error( "1 sided adjacent relation: " + map.name + ", " + a.name );
431+
432
for (var k in maps) {
433-
  } )
433+
    var map = maps[k];
434
435
    map.adjacent.forEach(function(a) {
436
        if (!a.isAdjacentTo(map.name)) {
437
            throw new Error("1 sided adjacent relation: " + map.name + ", " + a.name);
438-
function applyBlockMod( originMap, currentMap ) {
438+
        }
439-
		if ( currentMap.name == "Shaped Primordial Pool" || currentMap.isAdjacentTo( "Shaped Primordial Pool" ) ) {
439+
    })
440-
    	return;
440+
441
442-
    if ( currentMap.mod ) {
442+
443-
    	return;
443+
444
function applyBlockMod(originMap, currentMap) {
445
    if (currentMap.name == "Shaped Primordial Pool" || currentMap.isAdjacentTo("Shaped Primordial Pool")) {
446
        return;
447
    }
448-
    var mods = originMap.getFilteredModList().filter( function( mod ) {
448+
    if (currentMap.mod) {
449-
      return ! goodMods[ mod.name ];
449+
        return;
450-
    }).filter( function( mod ) {
450+
451-
      return mod.level == tier.WHITE;
451+
452
    // filter out good mods
453-
    
453+
454
    var mods = originMap.getFilteredModList().filter(function(mod) {
455-
    mods.sort( function(a, b) {
455+
        return !goodMods[mod.name];
456-
      return b.weights.default - a.weights.default;
456+
    }).filter(function(mod) {
457
        return mod.level == tier.WHITE;
458-
    
458+
459
460
    // sort by weight, descending
461
    mods.sort(function(a, b) {
462
        return b.weights.default-a.weights.default;
463
    });
464
465
    currentMap.mod = mods[0];
466
    console.log("Applied '" + currentMap.mod.name + "' to " + currentMap.name + ".");
467-
m.adjacent.forEach( function( map ) {
467+
468-
	map.adjacent.forEach( function( map2 ) {
468+
469-
  	applyBlockMod( m, map2 );
469+
470-
  });
470+
471
var m = maps["Shaped Primordial Pool"];
472
473
m.adjacent.forEach(function(map) {
474
    map.adjacent.forEach(function(map2) {
475
        applyBlockMod(m, map2);
476
    });
477-
m.adjacent.forEach( function( map ) {
477+
478-
	map.adjacent.forEach( function( map2 ) {
478+
479-
  	applyBlockMod( m, map2 );
479+
480-
  });
480+
481
m = maps["Burial Chambers"];
482
483-
for ( var m in maps ) {
483+
m.adjacent.forEach(function(map) {
484-
	var m = maps[m];
484+
    map.adjacent.forEach(function(map2) {
485-
  
485+
        applyBlockMod(m, map2);
486-
  if ( ! m.isAdjacentTo( "Shaped Primordial Pool" ) ) {
486+
487-
  	continue;
487+
488-
  }
488+
489-
  
489+
for (var m in maps) {
490-
  // apply to adjacent maps
490+
    var m = maps[m];
491-
  
491+
492-
  for ( var i = 0; i < m.adjacent.length; i++ ) {
492+
    if (!m.isAdjacentTo("Shaped Primordial Pool")) {
493-
  	var c = m.adjacent[i];
493+
        continue;
494-
    applyBlockMod( m, c );
494+
495-
  }
495+
496-
  
496+
    // apply to adjacent maps
497-
  // apply to 2nd degree adjacent maps
497+
498-
  
498+
    for (var i = 0; i < m.adjacent.length; i++) {
499-
  for ( var i = 0; i < m.adjacent.length; i++ ) {
499+
        var c = m.adjacent[i];
500-
  	var proxy = m.adjacent[i];
500+
        applyBlockMod(m, c);
501-
    
501+
502-
    if ( proxy.name == "Shaped Primordial Pool" ) { continue; }
502+
503-
    
503+
    // apply to 2nd degree adjacent maps
504-
    for ( var j = 0; j < proxy.adjacent.length; j++ ) {
504+
505-
      var c = proxy.adjacent[j];
505+
    for (var i = 0; i < m.adjacent.length; i++) {
506-
      applyBlockMod( m, c );
506+
        var proxy = m.adjacent[i];
507
508-
  }
508+
        if (proxy.name == "Shaped Primordial Pool") {
509
            continue;
510
        }
511
512-
var atl = new Atlas( maps );
512+
        for (var j = 0; j < proxy.adjacent.length; j++) {
513
            var c = proxy.adjacent[j];
514
            applyBlockMod(m, c);
515
        }
516-
var swap = function (array, pos1, pos2) {
516+
517-
  var temp = array[pos1];
517+
518-
  array[pos1] = array[pos2];
518+
519-
  array[pos2] = temp;
519+
520
var atl = new Atlas(maps);
521
522-
var heapsPermute = function (array, output, n) {
522+
523-
  n = n || array.length; // set n default to array.length
523+
524-
  if (n === 1) {
524+
var swap = function(array, pos1, pos2) {
525-
    output(array);
525+
    var temp = array[pos1];
526-
  } else {
526+
    array[pos1] = array[pos2];
527-
    for (var i = 1; i <= n; i += 1) {
527+
    array[pos2] = temp;
528-
      heapsPermute(array, output, n - 1);
528+
529-
      if (n % 2) {
529+
530-
        var j = 1;
530+
var heapsPermute = function(array, output, n) {
531-
      } else {
531+
    n = n || array.length; // set n default to array.length
532-
        var j = i;
532+
    if (n === 1) {
533-
      }
533+
        output(array);
534-
      swap(array, j - 1, n - 1); // -1 to account for javascript zero-indexing
534+
    } else {
535
        for (var i = 1; i <= n; i += 1) {
536-
  }
536+
            heapsPermute(array, output, n - 1);
537
            if (n % 2) {
538
                var j = 1;
539
            } else {
540
                var j = i;
541
            }
542
            swap(array, j - 1, n - 1); // -1 to account for javascript zero-indexing
543-
function result( order ) {
543+
        }
544-
	this.order = order;
544+
545-
  this.sum_x = 0;
545+
546-
  this.sum_x2 = 0;
546+
547-
  this.n = 0;
547+
548
549
// START result prototype --------------------------------------
550
551-
	return this.sum_x / this.n;
551+
function result(order) {
552
    this.order = order;
553
    this.sum_x = 0;
554
    this.sum_x2 = 0;
555-
	var mean = this.mean();
555+
    this.n = 0;
556-
	return Math.sqrt( this.sum_x2 / this.n - mean * mean );
556+
557
558
result.prototype.mean = function() {
559-
result.prototype.generateSamples = function( n ) {
559+
    return this.sum_x / this.n;
560-
	// Apply sextants to maps
560+
561-
  for ( var iter = 0; iter < n; iter++ ) {
561+
562-
    atl.reset();
562+
563-
    var cost = 0;
563+
    var mean = this.mean();
564-
    
564+
    return Math.sqrt(this.sum_x2 / this.n - mean * mean);
565-
    // First, roll any mod on each map in order.
565+
566-
    this.order.forEach( function( m ) {
566+
567-
      if ( ! maps[m].mod ) {
567+
result.prototype.generateSamples = function(n) {
568-
        cost += maps[m].roll();
568+
    // Apply sextants to maps
569-
      }
569+
    for (var iter = 0; iter < n; iter++) {
570-
    } );
570+
        atl.reset();
571-
    
571+
        var cost = 0;
572-
		// Second, finish rolling each map to a good mod, again respecting the order.
572+
573-
    this.order.forEach( function( m ) {
573+
        // First, roll any mod on each map in order.
574-
      // While no/bad sextant, keep rolling
574+
        this.order.forEach(function(m) {
575-
      while ( ! ( maps[m].mod && goodMods[maps[m].mod.name] ) ) {
575+
            if (!maps[m].mod) {
576-
        cost += maps[m].roll();
576+
                cost += maps[m].roll();
577-
      }
577+
            }
578-
    } );
578+
        });
579-
    
579+
580-
    this.sum_x += cost;
580+
        // Second, finish rolling each map to a good mod, again respecting the order.
581-
    this.sum_x2 += cost * cost;
581+
        this.order.forEach(function(m) {
582-
    this.n++;
582+
            // While no/bad sextant, keep rolling
583-
  }
583+
            while (!(maps[m].mod && goodMods[maps[m].mod.name])) {
584
                cost += maps[m].roll();
585
            }
586
        });
587-
	return 1.645 * ( this.stdDev() / Math.sqrt( this.n ) );
587+
588
        this.sum_x += cost;
589
        this.sum_x2 += cost * cost;
590
        this.n++;
591-
 
591+
592
}
593-
	var n = 0;
593+
594-
	for ( var k in a ) {
594+
595-
  	n++;
595+
    return 1.645 * (this.stdDev() / Math.sqrt(this.n));
596-
  }
596+
597-
  return n;
597+
598
// END result prototype ------------------------------------------
599
600
function countKeys(a) {
601
    var n = 0;
602
    for (var k in a) {
603-
for ( var map in maps ) {
603+
        n++;
604-
	if ( map == "Shaped Primordial Pool" || maps[map].isAdjacentTo( "Shaped Primordial Pool" ) ) {
604+
605-
  	console.log( map + " blocks " + countKeys(maps[map].getBlockedMods()) + " mods");
605+
    return n;
606-
  	mapsToRoll.push(map);
606+
607-
  }
607+
608
var results = [];
609
610
var mapsToRoll = [];
611-
heapsPermute(mapsToRoll, function( order ) {
611+
for (var map in maps) {
612-
	/*if ( order[0] !== "Shaped Primordial Pool" ) {
612+
    if (map == "Shaped Primordial Pool" || maps[map].isAdjacentTo("Shaped Primordial Pool")) {
613
        console.log(map + " blocks " + countKeys(maps[map].getBlockedMods()) + " mods");
614
        mapsToRoll.push(map);
615
    }
616-
	results.push( new result( order.slice( 0 ) ) );
616+
617-
} );
617+
618
// create a result entry for every permutation
619-
while ( true ) {
619+
heapsPermute(mapsToRoll, function(order) {
620-
	var results_b = results.filter( function( r ) {
620+
    /*if ( order[0] !== "Shaped Primordial Pool" ) {
621-
  	if ( r == results[0] || results[0].n == 0 ) {
621+
622-
    	return true;
622+
623
624-
    return r.mean() - r.confidenceInterval() <= results[0].mean() + results[0].confidenceInterval();
624+
    results.push(new result(order.slice(0)));
625-
  } );
625+
626-
  
626+
627-
  if ( results_b.length <= 1 || results_b[0].n >= maxIterations ) {
627+
while (true) {
628-
  	break;
628+
    var results_b = results.filter(function(r) {
629-
  }
629+
        if (r == results[0] || results[0].n == 0) {
630-
   
630+
            return true;
631-
  console.log( results[0].n + " iterations (" + results_b.length + " remaining permutations)" );
631+
        }
632-
  
632+
        return r.mean() - r.confidenceInterval() <= results[0].mean() + results[0].confidenceInterval();
633-
  var iterTarget = results[0].n + 5000 / Math.sqrt( results_b.length );
633+
634-
  
634+
635-
  results_b.forEach( function( r ) {
635+
    if (results_b.length <= 1 || results_b[0].n >= maxIterations) {
636-
  	r.generateSamples( iterTarget - r.n );
636+
637-
  } );
637+
638-
  
638+
639-
  results.sort( function( a, b ) {
639+
    console.log(results[0].n + " iterations (" + results_b.length + " remaining permutations)");
640-
  	return a.mean() - b.mean(); 
640+
641-
  } );
641+
    var iterTarget = results[0].n + 5000 / Math.sqrt(results_b.length);
642
643
    results_b.forEach(function(r) {
644-
console.log( "Results (up to " + maxIterations + " iterations):" );
644+
        r.generateSamples(iterTarget - r.n);
645
    });
646-
for ( var i = 0; i < results.length; i++ ) {
646+
647-
  var str = "" + results[i].mean().toFixed(3) + " ± " + results[i].confidenceInterval().toFixed(3) + "c";
647+
    results.sort(function(a, b) {
648
        return a.mean() - b.mean();
649-
  for ( var j = 0; j < results[i].order.length; j++ ) {
649+
650-
    if ( j > 0 ) { str += "," };
650+
651-
    str += " " + results[i].order[j];
651+
652-
  }
652+
console.log("Results (up to " + maxIterations + " iterations):");
653
654-
  console.log(str);
654+
for (var i = 0; i < results.length; i++) {
655
    var str = "" + results[i].mean().toFixed(3) + " ± " + results[i].confidenceInterval().toFixed(3) + "c";
656
657
    for (var j = 0; j < results[i].order.length; j++) {
658
        if (j > 0) {
659
            str += ","
660
        };
661
        str += " " + results[i].order[j];
662
    }
663
664
    console.log(str);
665
}