Created
April 13, 2018 17:28
-
-
Save walkingeyerobot/3c95e4dc222afba6c92099098c21fcad to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Node { | |
constructor(area, itemCount, smallKeyCount, dungeonPrizeCount) { | |
this.area_ = area; | |
this.itemCount_ = itemCount; | |
this.smallKeyCount_ = smallKeyCount; | |
this.dungeonPrizeCount_ = dungeonPrizeCount; | |
this.edgesOut_ = []; | |
} | |
}; | |
function N(a,b,c,d) { | |
return new Node(a,b,c,d); | |
} | |
class Edge { | |
constructor(nodeFrom, nodeTo, requirements, bidirectional) { | |
this.nodeFrom_ = nodeFrom; | |
this.nodeTo_ = nodeTo; | |
this.requirements_ = requirements; | |
this.bidirectional_ = bidirectional; | |
} | |
}; | |
function E(nodeFrom, nodeTo, requirements, bidirectional) { | |
var e = new Edge(Nodes[nodeFrom], Nodes[nodeTo], requirements || [], bidirectional !== false); | |
Nodes[nodeFrom].edgesOut_.push(e); | |
if (bidirectional) { | |
Nodes[nodeTo].edgesOut_.push(e); | |
} | |
return e; | |
} | |
let Items = [ | |
'L1Sword', | |
'L2Sword', | |
'L3Sword', | |
'L4Sword', | |
'Bow', | |
'Hammer', | |
'Lamp', | |
'L1Gloves', | |
'L2Gloves', | |
'MoonPearl', | |
'FireRod', | |
'PODKey', | |
'PODMap', | |
'PODCompass', | |
'PODBigKey', | |
'EPMap', | |
'EPCompass', | |
'EPBigKey', | |
'EPKey', | |
'Boots' | |
]; | |
let Enemies = { | |
'Popo': ['L1Sword', 'L2Sword', 'L3Sword', 'L4Sword', 'Hammer', 'FireRod', 'Bow'], | |
'Terrorpin': ['Hammer'], | |
'RedMimic': ['Bow'], | |
'GreenMimic': ['L1Sword', 'L2Sword', 'L3Sword', 'L4Sword', 'Bow', 'Hammer', 'FireRod'], | |
'ArmosKnights': ['L1Sword', 'L2Sword', 'L3Sword', 'L4Sword', 'Bow', 'Hammer'], | |
'HelmasaurKing': ['Hammer'], | |
'Bubble': [], | |
'GreenEyegore': ['L1Sword', 'L2Sword', 'L3Sword', 'L4Sword', 'Hammer', 'Bow'], | |
'RedEyegore': ['Bow'], | |
'Stalfos': [], | |
}; | |
// N(area, num_items, num_small_keys, num_dungeon_prizes) | |
let Nodes = [ | |
/* 0 */ N('A'), | |
/* 1 */ N('U', 1), | |
/* 2 */ N('LW'), | |
/* 3 */ N('U'), | |
/* 4 */ N('U'), | |
/* 5 */ N('U', 3), | |
/* 6 */ N('EP', 3), | |
/* 7 */ N('EP', 1), | |
/* 8 */ N('EP', 0, 1), | |
/* 9 */ N('EP', 1), | |
/* 10 */ N('EP'), | |
/* 11 */ N('EP', 0, 1), | |
/* 12 */ N('EP', 1, 0, 1), | |
/* 13 */ N('DW'), | |
/* 14 */ N('U'), | |
/* 15 */ N('U'), | |
/* 16 */ N('U'), | |
/* 17 */ N('POD', 1), | |
/* 18 */ N('POD', 2), | |
/* 19 */ N('POD', 2), | |
/* 20 */ N('POD', 1), | |
/* 21 */ N('POD', 1), | |
/* 22 */ N('POD', 1), | |
/* 23 */ N('POD', 2), | |
/* 24 */ N('POD', 2), | |
/* 25 */ N('POD'), | |
/* 26 */ N('POD', 1), | |
/* 27 */ N('POD', 1, 0, 1), | |
/* 28 */ N('A', 1) | |
]; | |
// E(node_from, node_to, requirements) | |
let Edges = [ | |
E(0, 1), | |
E(1, 2), | |
E(2, 3), | |
E(2, 4), | |
E(2, 5), | |
E(2, 6), | |
E(2, 13, [['Hammer', 'L1Gloves', 'MoonPearl'], ['Hammer', 'L2Gloves', 'MoonPearl']]), | |
E(6, 7, ['EPBigKey']), | |
E(6, 8, ['EPDarkRoom1']), | |
E(6, 10, ['EPDarkRoom2', 'EPBigKey']), | |
E(8, 9, ['GreenEyegore', 'Stalfos', 'Stalfos', 'Popo', 'Popo', 'Bubble', 'Bubble', 'Bubble', 'Bubble', 'EP Door']), | |
E(10, 11, ['GreenEyegore']), | |
E(10, 12, ['RedEyegore', 'RedEyegore', 'RedEyegore', 'Stalfos', 'Stalfos', 'Popo', 'Popo', 'Popo', 'Popo', 'Popo', 'Popo', 'EPDoor', 'ArmosKnights']), | |
E(13, 14), | |
E(13, 15), | |
E(13, 16), | |
E(13, 17), | |
E(17, 18, ['PODDoor']), | |
E(17, 19, ['RedMimic', 'GreenMimic', 'GreenMimic']), | |
E(18, 19, ['Hover'], false), | |
E(18, 20, ['PODDoor']), | |
E(18, 21, ['PODDoor']), | |
E(19, 18, ['Hammer'], false), | |
E(19, 27, ['PODDoor', 'PODBigKey', 'RedMimic', 'GreenMimic', 'GreenMimic', 'Bow', 'Hammer', 'Terrorpin', 'Terrorpin', 'Terrorpin', 'Terrorpin', 'Terrorpin', 'Terrorpin', 'PODDarkRoomFinal', 'HelmasaurKing']), | |
E(21, 22, ['PODDoor']), | |
E(21, 23, ['PODDark Basement']), | |
E(21, 24, ['PODDoor', 'PODDarkMaze']), | |
E(21, 25, [['PODHammerJump'], ['Hover']]), | |
E(24, 25, ['PODDarkMaze']), | |
E(25, 24, ['PODDarkMaze']), | |
E(25, 26, ['PODBigKey']), | |
E(0, 28, ['Crystal1', 'Crystal2']) | |
]; | |
let map = { | |
items: new WeakMap([ | |
[Nodes[ 1], ['L1Sword']], | |
[Nodes[ 5], ['L2Sword', 'L3Sword', 'L4Sword']], | |
[Nodes[ 6], ['Bow', 'Hammer', 'Lamp']], | |
[Nodes[ 7], ['L1Gloves']], | |
[Nodes[ 8], ['EPKey']], | |
[Nodes[ 9], ['L2Gloves']], | |
[Nodes[11], ['EPKey']], | |
[Nodes[12], ['MoonPearl']], | |
[Nodes[17], ['FireRod']], | |
[Nodes[18], ['PODKey', 'PODKey']], | |
[Nodes[19], ['PODKey', 'PODKey']], | |
[Nodes[20], ['PODKey']], | |
[Nodes[21], ['PODKey']], | |
[Nodes[22], ['PODMap']], | |
[Nodes[23], ['PODCompass', 'PODBigKey']], | |
[Nodes[24], ['EPMap', 'EPCompass']], | |
[Nodes[26], ['EPBigKey']], | |
[Nodes[27], ['Boots']], | |
[Nodes[28], ['Triforce']] | |
]), | |
prizes: new WeakMap([ | |
[Nodes[12], 'Crystal1'], | |
[Nodes[27], 'Crystal2'] | |
]) | |
}; | |
function isValidMap(map) { | |
// this probably needs better logic. | |
// we need to support something that can handle very basic boolean logic | |
// like the dark world entrace below eastern requires: | |
// Hammer && MoonPearl && (L1Gloves || L2Gloves) | |
// in the current implementation, this is represented by this: | |
// ['Hammer', 'MoonPearl', ['L1Gloves', 'L2Gloves']] | |
// so the first level is assumed to be && and subsequent levels are assumed to be ||. | |
function isPassable(edge) { | |
function isPassableSingle(req) { | |
if (Array.isArray(req)) { | |
return req.some(isPassableSingle); | |
} | |
if (Items.includes(req) || req.match(/Crystal/)) { | |
if (!items.includes(req)) { | |
return false; | |
} | |
return true; | |
} else if (Enemies[req]) { | |
var foo = !Enemies[req].some((v, i, a) => { | |
return items.includes(v); | |
}); | |
if (!Enemies[req].length || foo) { | |
return false; | |
} | |
return true; | |
} else if (req.match(/Dark/)) { | |
if (!items.includes('Lamp')) { | |
return false; | |
} | |
return true; | |
} else if (req === 'Hover') { | |
return false; | |
} else if (req.match(/Jump/)) { | |
return false; | |
} else if (req.match(/Door/)) { | |
return false; | |
} else { | |
throw Error('unknown requirement'); | |
} | |
return true; | |
} | |
return edge.requirements_.every(isPassableSingle); | |
} | |
let items = []; | |
let visitedEdges = []; | |
let availableEdges = []; | |
let visitedNodes = []; | |
let availableNodes = [Nodes[0]]; | |
while (true) { | |
while (availableNodes.length) { | |
let node = availableNodes.pop(); | |
if (visitedNodes.includes(node)) { | |
continue; | |
} | |
visitedNodes.push(node); | |
if (map.items.has(node)) { | |
items = items.concat(map.items.get(node)); | |
} | |
if (map.prizes.has(node)) { | |
items.push(map.prizes.get(node)); | |
} | |
for (let i = 0; i < node.edgesOut_.length; i++) { | |
let edgeOut = node.edgesOut_[i]; | |
if (visitedEdges.includes(edgeOut)) { | |
continue; | |
} | |
if (visitedNodes.includes(edgeOut.nodeFrom_) && visitedNodes.includes(edgeOut.nodeTo_)) { | |
// need to account for small keys in this situation. | |
// this is an edge we haven't passed but don't need to because we have already visited both nodes. | |
// if it has a small key requirement, it needs to be accounted for. | |
continue; | |
} | |
if (node === edgeOut.nodeFrom_ && visitedNodes.includes(edgeOut.nodeTo_)) { | |
continue; | |
} | |
if (node === edgeOut.nodeTo_ && visitedNodes.includes(edgeOut.nodeFrom_)) { | |
continue; | |
} | |
if (isPassable(edgeOut)) { | |
visitedEdges.push(edgeOut); | |
if (node === edgeOut.nodeTo_) { | |
availableNodes.push(edgeOut.nodeFrom_); | |
} else if (node === edgeOut.nodeFrom_) { | |
availableNodes.push(edgeOut.nodeTo_); | |
} else { | |
throw Error('weird edge thing just happened'); | |
} | |
} else if (!availableEdges.includes(edgeOut)) { | |
availableEdges.push(edgeOut); | |
} | |
} | |
} | |
let newAvailableEdges = []; | |
for (let i = 0; i < availableEdges.length; i++) { | |
let edge = availableEdges[i]; | |
if (isPassable(edge)) { | |
visitedEdges.push(edge); | |
if (!visitedNodes.includes(edge.nodeFrom_)) { | |
availableNodes.push(edge.nodeFrom_); | |
} | |
if (!visitedNodes.includes(edge.nodeTo_)) { | |
availableNodes.push(edge.nodeTo_); | |
} | |
} else { | |
newAvailableEdges.push(edge); | |
} | |
} | |
if (newAvailableEdges.length === availableEdges.length) { | |
break; | |
} | |
availableEdges = newAvailableEdges; | |
} | |
return items.includes('Triforce'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment