--- /dev/null
+import {
+ getDungeonBoss,
+ getDungeonPrize,
+ getGTBoss,
+ getGTCrystals,
+ hasDungeonBoss,
+ hasDungeonPrize,
+} from './tracker';
+
+const and = (...predicates) => (...args) =>
+ predicates.reduce((acc, cur) => acc && cur(...args), true);
+
+const or = (...predicates) => (...args) =>
+ predicates.reduce((acc, cur) => acc || cur(...args), false);
+
+const fromBool = b => (...args) => b(...args) ? 'available' : 'unavailable';
+
+const agaDead = (config, dungeons, state) =>
+ hasDungeonBoss(state, dungeons.find(d => d.id === 'ct'));
+
+const countCrystals = (config, dungeons, state) => dungeons
+ .filter(dungeon =>
+ (getDungeonPrize(state, dungeon) || 'crystal').endsWith('crystal') &&
+ dungeon.prize &&
+ hasDungeonPrize(state, dungeon)
+ ).length;
+
+const countRedCrystals = (config, dungeons, state) => dungeons
+ .filter(dungeon =>
+ getDungeonPrize(state, dungeon) === 'red-crystal' &&
+ hasDungeonPrize(state, dungeon)
+ ).length;
+
+const hasRedCrystals = n => (...args) => countRedCrystals(...args) >= n;
+
+const hasGTCrystals = (config, dungeons, state) =>
+ countCrystals(config, dungeons, state) >= getGTCrystals(state);
+
+const countPendants = (config, dungeons, state) => dungeons
+ .filter(dungeon =>
+ (getDungeonPrize(state, dungeon) || '').endsWith('pendant') &&
+ hasDungeonPrize(state, dungeon)
+ ).length;
+
+const hasGreenPendant = (config, dungeons, state) => dungeons
+ .filter(dungeon =>
+ getDungeonPrize(state, dungeon) === 'green-pendant' &&
+ hasDungeonPrize(state, dungeon)
+ ).length >= 1;
+
+const hasPendants = n => (...args) => countPendants(...args) >= n;
+
+// Equipment
+
+const hasBig = dungeon => (config, dungeons, state) =>
+ !config.wildBig || !!state[`${dungeon}-big-key`];
+
+const hasBird = (config, dungeons, state) => !!state['bird'];
+
+const hasBombos = (config, dungeons, state) => !!state['bombos'];
+
+const hasBook = (config, dungeons, state) => !!state['book'];
+
+const hasBoom = (config, dungeons, state) => !!(state['blue-boomerang'] || state['red-boomerang']);
+
+const hasBoots = (config, dungeons, state) => !!state['boots'];
+
+const hasBottle = n => (config, dungeons, state) => state['bottle'] >= (n || 1);
+
+const hasBow = (config, dungeons, state) => !!state['bow'];
+
+const hasBugnet = (config, dungeons, state) => !!state['bugnet'];
+
+const hasByrna = (config, dungeons, state) => !!state['byrna'];
+
+const hasCape = (config, dungeons, state) => !!state['cape'];
+
+const hasFireRod = (config, dungeons, state) => !!state['fire-rod'];
+
+const hasFlute = (config, dungeons, state) => !!state['flute'];
+
+const hasHammer = (config, dungeons, state) => !!state['hammer'];
+
+const hasHookshot = (config, dungeons, state) => !!state['hookshot'];
+
+const hasIceRod = (config, dungeons, state) => !!state['ice-rod'];
+
+const hasLamp = (config, dungeons, state) => !!state['lamp'];
+
+const hasMagicBars = n => (config, dungeons, state) => {
+ let bars = 1 + (state['bottle'] || 0);
+ if (state['half-magic']) {
+ bars *= 2;
+ }
+ if (state['quarter-magic']) {
+ bars *= 2;
+ }
+ return bars >= (n || 1);
+};
+
+const hasMirror = (config, dungeons, state) => !!state['mirror'];
+
+const hasMMMedallion = (config, dungeons, state) =>
+ !!state['mm-medallion'] && !!state[state['mm-medallion']];
+
+const hasMoonpearl = (config, dungeons, state) => !!state['moonpearl'];
+
+const hasMushroom = (config, dungeons, state) => !!state['mushroom'];
+
+const hasPowder = (config, dungeons, state) => !!state['powder'];
+
+const hasQuake = (config, dungeons, state) => !!state['quake'];
+
+const hasShovel = (config, dungeons, state) => !!state['shovel'];
+
+const hasSmall = (dungeon, amount) => (config, dungeons, state) =>
+ !config.wildSmall || state[`${dungeon}-small-key`] >= (amount || 1);
+
+const hasSomaria = (config, dungeons, state) => !!state['somaria'];
+
+const hasSword = n => (config, dungeons, state) => state['sword'] >= (n || 1);
+
+const hasTRMedallion = (config, dungeons, state) =>
+ !!state['tr-medallion'] && !!state[state['tr-medallion']];
+
+// Abilities
+
+const canActivateFlute = () => true;
+
+const canBomb = () => true;
+
+const canBonk = hasBoots;
+
+const canDarkRoom = hasLamp;
+
+const canFlipSwitches = or(
+ canBomb,
+ hasBoom,
+ canShootArrows,
+ hasSword(),
+ hasHammer,
+ hasHookshot,
+ hasSomaria,
+ hasByrna,
+ hasFireRod,
+ hasIceRod,
+);
+
+const canFly = or(hasBird, and(hasFlute, canActivateFlute));
+
+const canGetGoodBee = and(hasBugnet, hasBottle(), or(canBonk, and(hasSword(), hasQuake)));
+
+const canLift = (config, dungeons, state) => state['lift'] >= 1;
+
+const canHeavyLift = (config, dungeons, state) => state['lift'] >= 2;
+
+const canKill = damage => damage && damage < 6
+ ? or(hasBow, hasFireRod, hasHammer, hasSomaria, hasSword(1), canBomb, hasByrna)
+ : or(hasBow, hasFireRod, hasHammer, hasSomaria, hasSword(1));
+
+const canMedallion = hasSword();
+
+const canMeltThings = or(hasFireRod, and(hasBombos, canMedallion));
+
+const canPassCurtains = hasSword();
+
+const canShootArrows = hasBow;
+
+const canSwim = (config, dungeons, state) => !!state['flippers'];
+
+const canTablet = and(hasBook, hasSword(2));
+
+const canTorch = or(hasFireRod, hasLamp);
+
+const canTorchDarkRoom = or(canDarkRoom, canTorch);
+
+// Regions
+
+const westDeathMountain = or(
+ canFly,
+ and(canLift, canDarkRoom),
+);
+
+const eastDeathMountain = and(
+ westDeathMountain,
+ or(
+ hasHookshot,
+ and(hasHammer, hasMirror),
+ ),
+);
+
+const northDeathMountain = and(
+ westDeathMountain,
+ or(
+ hasMirror,
+ and(hasHammer, hasHookshot),
+ ),
+);
+
+const eastDarkDeathMountain = and(
+ eastDeathMountain,
+ canHeavyLift,
+);
+
+const westDarkDeathMountain = westDeathMountain;
+
+const eastDarkWorld = and(
+ hasMoonpearl,
+ or(
+ agaDead,
+ canHeavyLift,
+ and(canLift, hasHammer),
+ ),
+);
+
+const westDarkWorld = and(
+ hasMoonpearl,
+ or(
+ and(canLift, hasHammer),
+ canHeavyLift,
+ and(eastDarkWorld, hasHookshot, or(canSwim, canLift, hasHammer)),
+ ),
+);
+
+const southDarkWorld = or(
+ westDarkWorld,
+ and(eastDarkWorld, hasMoonpearl, hasHammer),
+);
+
+const mireArea = and(canFly, canHeavyLift);
+
+// Bosses
+
+const BOSS_RULES = {
+ aga: or(hasBugnet, hasHammer, hasSword()),
+ armos: or(
+ hasSword(), hasHammer, canShootArrows, hasBoom,
+ and(hasMagicBars(4), or(hasFireRod, hasIceRod)),
+ and(hasMagicBars(2), or(hasByrna, hasSomaria)),
+ ),
+ arrghus: and(hasHookshot, or(
+ hasSword(),
+ hasHammer,
+ and(or(hasMagicBars(2), canShootArrows), or(hasFireRod, hasIceRod)),
+ )),
+ blind: or(hasSword(), hasHammer, hasSomaria, hasByrna),
+ helma: and(or(canBomb, hasHammer), or(hasSword(), canShootArrows)),
+ kholdstare: and(canMeltThings, or(
+ hasHammer,
+ hasSword(),
+ and(hasFireRod, hasMagicBars(3)),
+ and(hasFireRod, hasBombos, hasMagicBars(2), canMedallion),
+ )),
+ lanmolas: or(
+ hasSword(), hasHammer, canShootArrows, hasFireRod, hasIceRod, hasByrna, hasSomaria,
+ ),
+ moldorm: or(hasSword(), hasHammer),
+ mothula: or(
+ hasSword(),
+ hasHammer,
+ and(hasMagicBars(2), or(hasFireRod, hasSomaria, hasByrna)),
+ canGetGoodBee,
+ ),
+ trinexx: and(hasFireRod, hasIceRod, or(
+ hasSword(3),
+ hasHammer,
+ and(hasMagicBars(2), hasSword(2)),
+ and(hasMagicBars(4), hasSword(1)),
+ )),
+ vitreous: or(hasSword(), hasHammer, canShootArrows),
+};
+
+const canKillBoss = dungeon => (config, dungeons, state) => {
+ const boss = getDungeonBoss(state, dungeons.find(d => d.id === dungeon));
+ return BOSS_RULES[boss](config, dungeons, state);
+};
+
+const canKillGTBoss = which => (config, dungeons, state) => {
+ const boss = getGTBoss(state, which);
+ return BOSS_RULES[boss](config, dungeons, state);
+};
+
+// Dungeons
+
+const canEnterCT = or(hasCape, hasSword(2));
+
+const canEnterGT = and(eastDarkDeathMountain, hasGTCrystals, hasMoonpearl);
+
+const canEnterDPFront = or(hasBook, and(mireArea, hasMirror));
+const canEnterDPBack = or(and(canEnterDPFront, canLift), and(mireArea, hasMirror));
+
+const canEnterTH = northDeathMountain;
+
+const canEnterPD = and(eastDarkWorld, hasMoonpearl);
+
+const canEnterSP = and(southDarkWorld, hasMirror, hasMoonpearl, canSwim);
+
+const canEnterSWFront = and(westDarkWorld, hasMoonpearl);
+const canEnterSWMid = and(westDarkWorld, hasMoonpearl);
+const canEnterSWBack = and(westDarkWorld, hasMoonpearl, hasFireRod);
+
+const canEnterTT = and(westDarkWorld, hasMoonpearl);
+
+const canEnterIP = and(canSwim, canHeavyLift, hasMoonpearl, canMeltThings);
+const rightSideIP = or(hasHookshot, hasSmall('ip'));
+
+const canEnterMM = and(
+ mireArea,
+ hasMoonpearl,
+ hasMMMedallion,
+ canMedallion,
+ or(canBonk, hasHookshot),
+ canKill(8),
+);
+
+const canEnterTRFront = and(
+ eastDeathMountain,
+ canHeavyLift,
+ hasHammer,
+ hasMoonpearl,
+ canMedallion,
+ hasTRMedallion,
+);
+const canEnterTRWest = and(canEnterTRFront, canBomb, hasSmall('tr', 2));
+const canEnterTREast = and(canEnterTRWest, or(hasHookshot, hasSomaria));
+const canEnterTRBack = and(
+ or(canEnterTRWest, canEnterTREast),
+ or(canBomb, canBonk),
+ hasBig('tr'),
+ hasSmall('tr', 3),
+ hasSomaria,
+ canDarkRoom,
+);
+const laserBridge = or(
+ and(
+ or(canEnterDPFront, canEnterTRWest, canEnterTREast),
+ canDarkRoom,
+ hasSomaria,
+ hasBig('tr'),
+ hasSmall('tr', 3),
+ ),
+ canEnterTRBack,
+);
+
+// Misc Macros
+
+const paradoxLower = and(eastDeathMountain, or(canBomb, hasBoom, canShootArrows, hasSomaria));
+
+const canBridgeRedBomb = or(
+ and(hasMoonpearl, hasHammer),
+ and(agaDead, hasMirror),
+);
+
+const canRescueSmith = and(westDarkWorld, hasMoonpearl, canHeavyLift);
+
+const Logic = {};
+
+Logic.open = {
+ fallback: () => 'available',
+ aginah: fromBool(canBomb),
+ blacksmith: fromBool(canRescueSmith),
+ 'blinds-hut-top': fromBool(canBomb),
+ 'bombos-tablet': fromBool(and(southDarkWorld, hasMirror, canTablet)),
+ 'bonk-rocks': fromBool(canBonk),
+ brewery: fromBool(and(westDarkWorld, canBomb, hasMoonpearl)),
+ 'bumper-cave': fromBool(and(westDarkWorld, hasMoonpearl, canLift, hasCape)),
+ 'c-house': fromBool(and(westDarkWorld, hasMoonpearl)),
+ catfish: fromBool(and(eastDarkWorld, hasMoonpearl)),
+ 'cave-45': fromBool(and(southDarkWorld, hasMirror)),
+ checkerboard: fromBool(and(mireArea, hasMirror)),
+ 'chest-game': fromBool(and(westDarkWorld, hasMoonpearl)),
+ 'chicken-house': fromBool(canBomb),
+ 'desert-ledge': fromBool(canEnterDPFront),
+ 'digging-game': fromBool(and(southDarkWorld, hasMoonpearl)),
+ 'ether-tablet': fromBool(and(northDeathMountain, canTablet)),
+ 'floating-island': fromBool(
+ and(eastDarkDeathMountain, hasMoonpearl, canLift, canBomb, hasMirror),
+ ),
+ 'flute-spot': fromBool(hasShovel),
+ 'graveyard-ledge': fromBool(and(westDarkWorld, hasMoonpearl, hasMirror)),
+ 'hammer-pegs': fromBool(and(westDarkWorld, hasHammer, hasMoonpearl, canHeavyLift)),
+ hobo: fromBool(canSwim),
+ 'hookshot-cave-tl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)),
+ 'hookshot-cave-tr': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)),
+ 'hookshot-cave-bl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)),
+ 'hookshot-cave-br': fromBool(
+ and(eastDarkDeathMountain, hasMoonpearl, canLift, or(hasHookshot, canBonk)),
+ ),
+ 'hype-cave-npc': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
+ 'hype-cave-top': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
+ 'hype-cave-right': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
+ 'hype-cave-left': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
+ 'hype-cave-bottom': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
+ 'ice-rod-cave': fromBool(canBomb),
+ 'kak-well-top': fromBool(canBomb),
+ 'kings-tomb': fromBool(and(canBonk, or(canHeavyLift, and(westDarkWorld, hasMirror)))),
+ 'lake-hylia-island': fromBool(
+ and(canSwim, hasMirror, hasMoonpearl, or(eastDarkWorld, southDarkWorld)),
+ ),
+ library: fromBool(canBonk),
+ lumberjack: fromBool(and(canBonk, agaDead)),
+ 'magic-bat': fromBool(and(hasPowder,
+ or(hasHammer, and(westDarkWorld, hasMoonpearl, canHeavyLift, hasMirror)),
+ )),
+ 'mimic-cave': fromBool(and(canEnterTREast, hasMirror, hasHammer)),
+ 'mini-moldorm-left': fromBool(canBomb),
+ 'mini-moldorm-right': fromBool(canBomb),
+ 'mini-moldorm-far-left': fromBool(canBomb),
+ 'mini-moldorm-far-right': fromBool(canBomb),
+ 'mini-moldorm-npc': fromBool(canBomb),
+ 'mire-shed-left': fromBool(and(mireArea, hasMoonpearl)),
+ 'mire-shed-right': fromBool(and(mireArea, hasMoonpearl)),
+ 'old-man': fromBool(and(westDeathMountain, canDarkRoom)),
+ 'paradox-lower-far-left': fromBool(paradoxLower),
+ 'paradox-lower-left': fromBool(paradoxLower),
+ 'paradox-lower-right': fromBool(paradoxLower),
+ 'paradox-lower-far-right': fromBool(paradoxLower),
+ 'paradox-lower-mid': fromBool(paradoxLower),
+ 'paradox-upper-left': fromBool(and(eastDeathMountain, canBomb)),
+ 'paradox-upper-right': fromBool(and(eastDeathMountain, canBomb)),
+ pedestal: fromBool(hasPendants(3)),
+ 'potion-shop': fromBool(hasMushroom),
+ 'purple-chest': fromBool(and(canRescueSmith, hasMoonpearl, canHeavyLift)),
+ pyramid: fromBool(eastDarkWorld),
+ 'pyramid-fairy-left': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)),
+ 'pyramid-fairy-right': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)),
+ 'race-game': fromBool(or(canBomb, canBonk)),
+ saha: fromBool(hasGreenPendant),
+ 'saha-left': fromBool(or(canBomb, canBonk)),
+ 'saha-mid': fromBool(or(canBomb, canBonk)),
+ 'saha-right': fromBool(or(canBomb, canBonk)),
+ 'sick-kid': fromBool(hasBottle(1)),
+ 'spec-rock': fromBool(and(westDeathMountain, hasMirror)),
+ 'spec-rock-cave': fromBool(westDeathMountain),
+ 'spike-cave': fromBool(and(
+ westDarkDeathMountain,
+ hasMoonpearl,
+ hasHammer,
+ canLift,
+ or(hasByrna, and(hasCape, hasMagicBars(2))),
+ )),
+ 'spiral-cave': fromBool(eastDeathMountain),
+ stumpy: fromBool(and(southDarkWorld, hasMoonpearl)),
+ 'super-bunny-top': fromBool(and(eastDarkDeathMountain, hasMoonpearl)),
+ 'super-bunny-bottom': fromBool(and(eastDarkDeathMountain, hasMoonpearl)),
+ 'waterfall-fairy-left': fromBool(canSwim),
+ 'waterfall-fairy-right': fromBool(canSwim),
+ zora: fromBool(or(canLift, canSwim)),
+ 'zora-ledge': fromBool(canSwim),
+ 'hc-boom': fromBool(and(hasSmall('hc'), canKill())),
+ 'hc-cell': fromBool(and(hasSmall('hc'), canKill())),
+ 'dark-cross': fromBool(canTorchDarkRoom),
+ 'sewers-left': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))),
+ 'sewers-mid': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))),
+ 'sewers-right': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))),
+ ct: fromBool(canEnterCT),
+ 'ct-1': fromBool(canKill()),
+ 'ct-2': fromBool(and(canKill(), hasSmall('ct'), canDarkRoom)),
+ 'ct-boss-killable': fromBool(and(
+ canKill(), hasSmall('ct', 2), canDarkRoom, canPassCurtains, canKillBoss('ct'),
+ )),
+ gt: fromBool(canEnterGT),
+ 'gt-tile-room': fromBool(hasSomaria),
+ 'gt-compass-tl': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
+ 'gt-compass-tr': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
+ 'gt-compass-bl': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
+ 'gt-compass-br': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
+ 'gt-torch': fromBool(canBonk),
+ 'gt-dm-tl': fromBool(and(hasHammer, hasHookshot)),
+ 'gt-dm-tr': fromBool(and(hasHammer, hasHookshot)),
+ 'gt-dm-bl': fromBool(and(hasHammer, hasHookshot)),
+ 'gt-dm-br': fromBool(and(hasHammer, hasHookshot)),
+ 'gt-map-chest': fromBool(and(hasHammer, or(hasHookshot, canBonk), hasSmall('gt', 4))),
+ 'gt-firesnake': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 3))),
+ 'gt-rando-tl': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
+ 'gt-rando-tr': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
+ 'gt-rando-bl': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
+ 'gt-rando-br': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
+ 'gt-bobs-chest': fromBool(and(
+ or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
+ hasSmall('gt', 3),
+ )),
+ 'gt-ice-left': fromBool(and(
+ or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
+ hasSmall('gt', 3),
+ canKillGTBoss('bot'),
+ )),
+ 'gt-ice-mid': fromBool(and(
+ or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
+ hasSmall('gt', 3),
+ canKillGTBoss('bot'),
+ )),
+ 'gt-ice-right': fromBool(and(
+ or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
+ hasSmall('gt', 3),
+ canKillGTBoss('bot'),
+ )),
+ 'gt-big-chest': fromBool(and(
+ or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
+ hasSmall('gt', 3),
+ hasBig('gt'),
+ )),
+ 'gt-helma-left': fromBool(and(
+ hasBig('gt'),
+ hasSmall('gt', 3),
+ canShootArrows,
+ canTorch,
+ canKillGTBoss('mid'),
+ )),
+ 'gt-helma-right': fromBool(and(
+ hasBig('gt'),
+ hasSmall('gt', 3),
+ canShootArrows,
+ canTorch,
+ canKillGTBoss('mid'),
+ )),
+ 'gt-pre-moldorm': fromBool(and(
+ hasBig('gt'),
+ hasSmall('gt', 3),
+ canShootArrows,
+ canTorch,
+ canKillGTBoss('mid'),
+ )),
+ 'gt-post-moldorm': fromBool(and(
+ hasBig('gt'),
+ hasSmall('gt', 4),
+ canShootArrows,
+ canTorch,
+ canKillGTBoss('mid'),
+ canKillGTBoss('top'),
+ hasHookshot,
+ )),
+ 'gt-boss-killable': fromBool(and(
+ hasBig('gt'),
+ hasSmall('gt', 4),
+ canShootArrows,
+ canTorch,
+ canKillGTBoss('mid'),
+ canKillGTBoss('top'),
+ hasHookshot,
+ canKillBoss('ct'),
+ )),
+ 'ep-big-chest': fromBool(hasBig('ep')),
+ 'ep-big-key-chest': fromBool(canDarkRoom),
+ 'ep-boss-defeated': fromBool(and(
+ canShootArrows, canTorchDarkRoom, hasBig('ep'), canKillBoss('ep'),
+ )),
+ dp: fromBool(or(canEnterDPFront, canEnterDPBack)),
+ 'dp-big-chest': fromBool(and(canEnterDPFront, hasBig('dp'))),
+ 'dp-big-key-chest': fromBool(and(canEnterDPFront, hasSmall('dp'), canKill())),
+ 'dp-compass-chest': fromBool(and(canEnterDPFront, hasSmall('dp'))),
+ 'dp-map-chest': fromBool(canEnterDPFront),
+ 'dp-torch': fromBool(and(canEnterDPFront, canBonk)),
+ 'dp-boss-defeated': fromBool(and(
+ canEnterDPBack,
+ canTorch,
+ hasBig('dp'),
+ hasSmall('dp'),
+ canKillBoss('dp'),
+ )),
+ th: fromBool(canEnterTH),
+ 'th-basement-cage': fromBool(canFlipSwitches),
+ 'th-map-chest': fromBool(canFlipSwitches),
+ 'th-big-key-chest': fromBool(and(canFlipSwitches, hasSmall('th'), canTorch)),
+ 'th-compass-chest': fromBool(and(canFlipSwitches, hasBig('th'))),
+ 'th-big-chest': fromBool(and(canFlipSwitches, hasBig('th'))),
+ 'th-boss-defeated': fromBool(and(
+ canFlipSwitches,
+ hasBig('th'),
+ canKillBoss('th'),
+ )),
+ pd: fromBool(canEnterPD),
+ 'pd-stalfos-basement': fromBool(or(hasSmall('pd', 1), and(canShootArrows, hasHammer))),
+ 'pd-big-key-chest': fromBool(hasSmall('pd', 6)),
+ 'pd-arena-bridge': fromBool(or(hasSmall('pd', 1), and(canShootArrows, hasHammer))),
+ 'pd-arena-ledge': fromBool(canShootArrows),
+ 'pd-map-chest': fromBool(canShootArrows),
+ 'pd-compass-chest': fromBool(hasSmall('pd', 4)),
+ 'pd-basement-left': fromBool(and(canTorchDarkRoom, hasSmall('pd', 4))),
+ 'pd-basement-right': fromBool(and(canTorchDarkRoom, hasSmall('pd', 4))),
+ 'pd-harmless-hellway': fromBool(hasSmall('pd', 6)),
+ 'pd-maze-top': fromBool(and(canDarkRoom, hasSmall('pd', 6))),
+ 'pd-maze-bottom': fromBool(and(canDarkRoom, hasSmall('pd', 6))),
+ 'pd-big-chest': fromBool(and(canDarkRoom, hasBig('pd'), hasSmall('pd', 6))),
+ 'pd-boss-defeated': fromBool(and(
+ canDarkRoom, hasBig('pd'), hasSmall('pd', 6), canShootArrows, hasHammer, canKillBoss('pd'),
+ )),
+ sp: fromBool(canEnterSP),
+ 'sp-map-chest': fromBool(and(hasSmall('sp'), canBomb)),
+ 'sp-big-chest': fromBool(and(hasSmall('sp'), hasHammer, hasBig('sp'))),
+ 'sp-compass-chest': fromBool(and(hasSmall('sp'), hasHammer)),
+ 'sp-west-chest': fromBool(and(hasSmall('sp'), hasHammer)),
+ 'sp-big-key-chest': fromBool(and(hasSmall('sp'), hasHammer)),
+ 'sp-flooded-left': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)),
+ 'sp-flooded-right': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)),
+ 'sp-waterfall': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)),
+ 'sp-boss-defeated': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot, canKillBoss('sp'))),
+ sw: fromBool(or(canEnterSWFront, canEnterSWMid, canEnterSWBack)),
+ 'sw-big-chest': fromBool(and(canEnterSWFront, hasBig('sw'))),
+ 'sw-bridge-chest': fromBool(canEnterSWBack),
+ 'sw-boss-defeated': fromBool(and(
+ canEnterSWBack, canPassCurtains, hasFireRod, hasSmall('sw', 3), canKillBoss('sw'),
+ )),
+ tt: fromBool(canEnterTT),
+ 'tt-attic': fromBool(and(hasBig('tt'), hasSmall('tt'), canBomb)),
+ 'tt-cell': fromBool(hasBig('tt')),
+ 'tt-big-chest': fromBool(and(hasBig('tt'), hasSmall('tt'), hasHammer)),
+ 'tt-boss-defeated': fromBool(and(hasBig('tt'), hasSmall('tt'), canKillBoss('tt'))),
+ ip: fromBool(canEnterIP),
+ 'ip-big-key-chest': fromBool(and(rightSideIP, hasHammer, canLift)),
+ 'ip-map-chest': fromBool(and(rightSideIP, hasHammer, canLift)),
+ 'ip-spike-chest': fromBool(rightSideIP),
+ 'ip-freezor-chest': fromBool(canMeltThings),
+ 'ip-big-chest': fromBool(hasBig('ip')),
+ 'ip-boss-defeated': fromBool(and(
+ hasBig('ip'),
+ hasHammer,
+ canLift,
+ or(hasSmall('ip', 2), and(hasSomaria, hasSmall('ip'))),
+ canKillBoss('ip'),
+ )),
+ mm: fromBool(canEnterMM),
+ 'mm-lobby-chest': fromBool(or(hasBig('mm'), hasSmall('mm'))),
+ 'mm-compass-chest': fromBool(and(canTorch, hasSmall('mm', 3))),
+ 'mm-big-key-chest': fromBool(and(canTorch, hasSmall('mm', 3))),
+ 'mm-big-chest': fromBool(hasBig('mm')),
+ 'mm-map-chest': fromBool(or(hasBig('mm'), hasSmall('mm'))),
+ 'mm-boss-defeated': fromBool(and(hasBig('mm'), canDarkRoom, hasSomaria, canKillBoss('mm'))),
+ tr: fromBool(or(canEnterTRFront, canEnterTRWest, canEnterTREast, canEnterTRBack)),
+ 'tr-roller-left': fromBool(and(hasFireRod, hasSomaria, or(
+ canEnterTRFront,
+ and(or(canEnterTRWest, canEnterTREast), hasSmall('tr', 4)),
+ and(canEnterTRBack, canDarkRoom, hasSmall('tr', 4)),
+ ))),
+ 'tr-roller-right': fromBool(and(hasFireRod, hasSomaria, or(
+ canEnterTRFront,
+ and(or(canEnterTRWest, canEnterTREast), hasSmall('tr', 4)),
+ and(canEnterTRBack, canDarkRoom, hasSmall('tr', 4)),
+ ))),
+ 'tr-compass-chest': fromBool(and(hasSomaria, or(
+ canEnterTRFront,
+ and(or(canEnterTRWest, canEnterTREast), hasSmall('tr', 4)),
+ and(canEnterTRBack, canDarkRoom, hasSmall('tr', 4)),
+ ))),
+ 'tr-chomps': fromBool(or(
+ and(canEnterTRFront, hasSmall('tr')),
+ canEnterTRWest,
+ canEnterTREast,
+ and(canEnterTRBack, canDarkRoom, hasSomaria),
+ )),
+ 'tr-big-key-chest': fromBool(and(hasSmall('tr', 4), or(
+ and(canEnterTRFront, hasSomaria),
+ canEnterTRWest,
+ canEnterTREast,
+ and(canEnterTRBack, canDarkRoom, hasSomaria),
+ ))),
+ 'tr-big-chest': fromBool(canEnterTREast),
+ 'tr-crysta-roller': fromBool(or(
+ and(hasBig('tr'), or(
+ and(canEnterTRFront, hasSomaria, hasSmall('tr', 2)),
+ canEnterTRWest,
+ canEnterTREast,
+ )),
+ and(canEnterTRBack, canDarkRoom, hasSomaria),
+ )),
+ 'tr-laser-bridge-top': fromBool(laserBridge),
+ 'tr-laser-bridge-left': fromBool(laserBridge),
+ 'tr-laser-bridge-right': fromBool(laserBridge),
+ 'tr-laser-bridge-bottom': fromBool(laserBridge),
+ 'tr-boss-defeated': fromBool(and(
+ laserBridge,
+ hasSmall('tr', 4),
+ hasBig('tr'),
+ hasSomaria,
+ canKillBoss('tr'),
+ )),
+};
+
+export default Logic;