diff --git a/.gitignore b/.gitignore index 8e90c14..4b585f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ local.js +node_modules/ +Gruntfile.js +.*.swp +package-lock.json diff --git a/local.js b/local.js deleted file mode 100644 index 82680d2..0000000 --- a/local.js +++ /dev/null @@ -1,5 +0,0 @@ -// Vars specific to shard3. -module.exports = { - ROOMS: ['W5S31'], - ATTACK: false, -}; diff --git a/local_season3.js b/local_season3.js new file mode 100644 index 0000000..72242f8 --- /dev/null +++ b/local_season3.js @@ -0,0 +1,11 @@ +// Season 3 locals. +module.exports = { + ATTACK: false, + homeSpawn: Game.spawns.Home, + // Consider E26S12 with roads. + remoteHarvestRooms: ['E27S12', 'E27S11', 'E23S9'], + defenseRooms: {}, //E24S9: 1}, + // TODO: scout and/or extend this + badRooms: ['E27S14', 'E26S14', 'E26S15', 'E26S16', 'E22S4'], + powerRooms: [], //'E27S10'], +}; diff --git a/main.js b/main.js index 1e7d816..10342af 100644 --- a/main.js +++ b/main.js @@ -1,10 +1,11 @@ const LOCALS = require('local'); -const ROLES = ['harvester', 'defender', 'claimer', 'relocater', 'miner', 'bob']; +const ROLES = ['harvester', 'defender', 'claimer', 'relocater', 'miner', 'bob', 'pcRenew']; const ROLE2S = ['bootstrapper', 'remoteHarvester', 'dropHarvester', 'builder', 'miner', 'combatant', 'dismantler', 'carrier', 'scout', 'hauler', 'storeUpgrader', 'recycle', 'reserver', 'healer', - 'waiter', 'keeperKiller', 'delivery']; + 'waiter', 'keeperKiller', 'delivery', 'roadWorker', 'bankBuster', + 'powerLoader', 'bother']; var role = {}; _.forEach(ROLES, r => { role[r] = require('role.'+r); @@ -22,7 +23,7 @@ _.forEach(ROLE2S, r => { } }) -const PLANNERS = ['claim', 'lab', 'attack', 'room']; +const PLANNERS = ['claim', 'lab', 'attack', 'room', 'remoteHarvest', 'power', 'scout', 'safemode']; var plan = {}; _.forEach(PLANNERS, p => {plan[p] = require('plan.'+p)}); @@ -34,7 +35,7 @@ const UTILS = ['creep', 'pathing', 'stats', 'splay']; var util = {}; _.forEach(UTILS, u => {util[u] = require('util.'+u)}); -const rmtHvst = require('tmp.remoteHarvest'); +const opsPower = require('pc.operator'); const profiler = require('screeps-profiler'); @@ -44,7 +45,7 @@ _.forEach(plan, (m, p) => profiler.registerObject(m, 'plan.'+p)); _.forEach(struct, (m, s) => profiler.registerObject(m, 'struct.'+s)); _.forEach(util, (m, u) => profiler.registerObject(m, 'util.'+u)); -console.log('Reloading'); +console.log(`Reloading at tick ${Game.time}`); function buildingSay(struct, text) { struct.room.visual.text( @@ -58,29 +59,34 @@ const main = { runPlanners: function() { _.forEach(Game.rooms, r => { plan.room.run(r); + if(plan.safemode.check(r)) { + const ret = r.controller.activateSafeMode(); + console.log(`Activating safemode in ${r.name} at ${Game.time} = ${ret}`); + Game.notify(`Activated safemode in ${r.name} at ${Game.time} = ${ret}`); + } }) - - /* - if(Game.time % 100 == 0) { - rmtHvst.run('W5S32'); - } - */ + + plan.scout.update(); plan.lab.test(); plan.claim.test(); if(LOCALS.ATTACK) { plan.attack.test(); + } + for(const r of LOCALS.remoteHarvestRooms) { + plan.remoteHarvest.plan(r); + } + + for(const r of LOCALS.powerRooms) { + plan.power.run(r); } - /* - if(Game.time % 500 == 0) { - let ret = role.hauler.spawnRemote(Game.spawns.Spawn2, 'E13N33', 'E15N31'); - console.log(`Remote hauler spawn: ${ret}`); + if(Game.flags.Power && !LOCALS.powerRooms.includes(Game.flags.Power.pos.roomName)) { + plan.power.run(Game.flags.Power.pos.roomName); } - */ }, cleanup: function() { - var lost = []; - for(var name in Memory.creeps) { + const lost = []; + for(const name in Memory.creeps) { if(!Game.creeps[name]) { const mem = Memory.creeps[name]; let plaque = name; @@ -112,6 +118,11 @@ runCreeps: function() { console.log(`${creep.name} has unknown role (${roleName})`); } }); + _.forEach(Game.powerCreeps, pc => { + if(!pc.ticksToLive) return; + if(pc.className != opsPower.CLASS) return; + opsPower.run(pc); + }); }, runStructs: function() { _.forEach(Game.structures, s => { @@ -124,15 +135,14 @@ profiler.registerObject(main, 'main'); module.exports.loop = () => profiler.wrap(() => { main.runPlanners(); - - if(!Memory.viz) Memory.viz = {}; - for(const roomName in Memory.viz) { - new RoomVisual(roomName).import(Memory.viz[roomName]); - } if(!Memory.viz) Memory.viz = {}; for(const roomName in Memory.viz) { + if(roomName == "map") { + Game.map.visual.import(Memory.viz[roomName]); + } else { new RoomVisual(roomName).import(Memory.viz[roomName]); + } } if(Game.time % 20 == 0) { // Periodic cleanup @@ -153,5 +163,5 @@ module.exports.loop = () => profiler.wrap(() => { // Memory.stats = stats; RawMemory.segments[99] = JSON.stringify(stats); } - if(Game.cpu.bucket == 10000) Game.cpu.generatePixel(); + if(Game.cpu.generatePixel && Game.cpu.bucket >= 10000) Game.cpu.generatePixel(); }) diff --git a/package.json b/package.json index 2714f7d..b1b93a9 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "test": "jest" }, "dependencies": { - "@types/screeps": "^3.2.2" + "@types/screeps": "^3.2.2", + "grunt": "^1.4.1", + "grunt-screeps": "^1.5.0" } } diff --git a/pc.operator.js b/pc.operator.js new file mode 100644 index 0000000..9d245d9 --- /dev/null +++ b/pc.operator.js @@ -0,0 +1,29 @@ +const pcRenew = require('role.pcRenew'); +const haulerRole = require('role2.hauler'); + +module.exports = { +CLASS: POWER_CLASS.OPERATOR, +run: function(pc) { + for(const pwr in pc.powers) { + if(pc.powers[pwr].cooldown > 0) continue; + switch(parseInt(pwr)) { + case PWR_OPERATE_EXTENSION: + const store = pc.room.storage; + // TODO: Base on power level. + if(store && store.store.energy > 2000 && pc.room.energyAvailable < pc.room.energyCapacityAvailable*0.75) { + // Might be too far away, but we can keep spamming it until it works. + const ret = pc.usePower(pwr, store); + } + break; + case PWR_GENERATE_OPS: + const ret = pc.usePower(pwr); + break; + } + } + if(pc.ticksToLive < 2000) { + if(pcRenew.run(pc) != ERR_NOT_FOUND) return; + } + // TODO: Do something more interesting/dynamic. + haulerRole.run(pc); +} +}; diff --git a/plan.claim.js b/plan.claim.js index 84c363f..86c39ec 100644 --- a/plan.claim.js +++ b/plan.claim.js @@ -3,6 +3,7 @@ const builder = require('role2.builder'); const relocater = require('role.relocater'); const bootstrapper = require('role2.bootstrapper'); const scout = require('role2.scout'); +const pathUtil = require('util.pathing'); const VISUALIZE = true; const BOOTSTRAP = true; @@ -16,6 +17,8 @@ function run(claimFlag) { const spawnPos = claimFlag.pos; const roomName = spawnPos.roomName; const room = Game.rooms[roomName]; + + if(!claimable && !room) return ERR_GCL_NOT_ENOUGH; const obs = Game.getObjectById(module.exports.observer); if(obs) { @@ -24,7 +27,7 @@ function run(claimFlag) { console.log(`Unable to observe ${roomName} from ${obs}: ${ret}`); } if(!room) { - // TODO(baptr): If this happens multiple times, the observer msut be in + // TODO(baptr): If this happens multiple times, the observer must be in // use elsewhere... console.log(`No visibility into claim target ${roomName}, trying next tick...`); return ERR_NOT_FOUND; @@ -33,7 +36,7 @@ function run(claimFlag) { if(!scout.exists(roomName)) { return scout.spawn(roomName); } - return ERR_NO_PATH; + return ERR_NOT_OWNER; } const ctrl = room.controller; @@ -49,6 +52,13 @@ function run(claimFlag) { if(claimerRet == OK) return OK; console.log(`Claim(${roomName}): spawn claimer: ${ret}`); } + + /* + var baddies = room.find(FIND_HOSTILE_CREEPS); + if(baddies.length) { + const defender = require('role.defender'); + } + */ var structs = room.find(FIND_HOSTILE_STRUCTURES); if(structs.length) { @@ -66,7 +76,7 @@ function run(claimFlag) { {filter: {structureType: STRUCTURE_SPAWN}}).length) { // Already planned, just send in some bootstrappers to help out. - if(room.energyCapacityAvailable > 700) { + if(room.energyCapacityAvailable > 500) { console.log("Initial bootstrapping of",roomName,"complete"); claimFlag.remove(); return OK; @@ -74,7 +84,7 @@ function run(claimFlag) { // TODO(baptr): This doesn't keep sending bigger ones once the room // is able to build its own, which was kind of the point. - if(room.find(FIND_MY_CREEPS).length > 5) return OK; + if(room.find(FIND_MY_CREEPS).length > 3) return OK; // Ugly workaround for leaving time to spawn/travel, and help save // up for better bodies. @@ -82,14 +92,14 @@ function run(claimFlag) { if(Game.time < next) return OK; room.memory.nextSpawnAttempt = Game.time + 100; - var [spawn, path] = pickSpawn(ctrl.pos, CREEP_LIFE_TIME - 600); + const [spawn, path] = pickSpawn(ctrl.pos, CREEP_LIFE_TIME - 600); if(!spawn) return path; var body = builder.mkBody(spawn.room.energyAvailable); if(!body) return; const name = `relo-bootstrap-${roomName}-${Game.time}`; var ret = spawn.spawnCreep(body, name, { - memory: relocater.setMem({}, roomName, 'bootstrapper'), + memory: relocater.setMem({}, roomName, 'bootstrapper', spawn.room.name), }); if(ret != OK) console.log(`Failed to spawn ${name}: ${ret}`); return ret; @@ -114,21 +124,36 @@ function run(claimFlag) { // possible to the target. // TODO(baptr): Consider Game.map.findRoute... // - easier to filter enemy rooms -function pickSpawn(dstPos, maxCost = CREEP_CLAIM_LIFE_TIME - 50) { - var goals = _.map(_.filter(Game.spawns, s => !s.spawning && s.room.energyCapacityAvailable > 650), s => { - // TODO(baptr): Filter for rooms with a lot of energy? - return {pos: s.pos, range: 1}; - }); - var pathRes = PathFinder.search(dstPos, goals, {maxCost: maxCost, maxOps: 20000}); - var path = pathRes.path; +function pickSpawn(dstPos, maxCost = CREEP_CLAIM_LIFE_TIME - 10) { + const macroRooms = {}; + const spawns = Object.values(Game.spawns).filter(s => s.isActive() && !s.spawning && s.room.energyCapacityAvailable >= 650); + if(!spawns.length) { + return [null, ERR_BUSY]; + } + for(const s of spawns) { + if(macroRooms[s.room.name]) continue; + macroRooms[s.room.name] = true; + const path = pathUtil.macroPath(s.room.name, dstPos.roomName); + if(path == ERR_NO_PATH) continue; + for(const step of path) { + macroRooms[step.room] = true; + } + } + const goals = spawns.map(s => ({pos: s.pos, range: 1})); + const pathRes = PathFinder.search(dstPos, goals, {plainCost: 1, swampCost: 1, maxCost, maxOps: 20000, + roomCallback: name => macroRooms[name] ? undefined : false}); + const path = pathRes.path; if(!path.length) { - //console.log(`Claim: unable to find a src spawn with enough energy`); - return [null, ERR_NO_PATH]; + console.log(`plan.claim: no path: ${JSON.stringify(pathRes)}`); + return [null, ERR_NO_PATH]; } path.reverse(); if(VISUALIZE) visPath(path); - if(pathRes.incomplete) return [null, ERR_NOT_IN_RANGE]; + if(pathRes.incomplete) { + console.log(`Claimer unable to path. Best match has cost=${pathRes.cost}, took ${pathRes.ops}`); + return [null, ERR_NOT_IN_RANGE]; + } var spawn = path[0].findClosestByRange(FIND_MY_SPAWNS); if(!spawn) { @@ -159,14 +184,17 @@ function visPath(path) { module.exports = { observer: '5b51885c8610fd40ae72c7da', run: run, -pickSpawn: pickSpawn, +pickSpawn, test: function() { - var target = Game.flags.Claim; + const target = Game.flags.Claim; if(target) { - let ret = run(target); + const ret = run(target); if(ret != OK) { - console.log(`Claim(${target.pos}) = ${ret}`); + if(ret != ERR_NOT_OWNER && (Game.time%50) == 0) console.log(`Claim(${target.pos}) = ${ret}`); } + return ret; + } else { + return ERR_NOT_FOUND; } } -}; \ No newline at end of file +}; diff --git a/plan.power.js b/plan.power.js new file mode 100644 index 0000000..5bb9f38 --- /dev/null +++ b/plan.power.js @@ -0,0 +1,229 @@ +const BodyBuilder = require('util.bodybuilder'); +const pathUtil = require('util.pathing'); + +const hauler = require('role2.hauler'); +const keeperKiller = require('role2.keeperKiller'); +const bankBuster = require('role2.bankBuster'); +const scout = require('role2.scout'); + +function keepUnique(val, idx, arr) { + return arr.indexOf(val) == idx; +} + +function spacesNear(roomName, pos) { + const terrain = Game.map.getRoomTerrain(roomName); + + let melee = 0; + for(let y = pos.y-1; y <= pos.y+1; y++) { + for(let x = pos.x-1; x <= pos.x+1; x++) { + if(terrain.get(x, y) != TERRAIN_MASK_WALL) melee++; + } + } + + let ranged = -melee-2; // Leave space for new melee to come in + for(let y = pos.y-3; y <= pos.y+3; y++) { + for(let x = pos.x-3; x <= pos.x+3; x++) { + if(terrain.get(x, y) != TERRAIN_MASK_WALL) ranged++; + } + } + return {melee, ranged}; +} + +function spawnDistance(spawn, bank) { + // Dynamic body sizes make this hard, but the current busters tend to be + // slightly better than 1:1 MOVE:other, so default 1:5 tick steps should be + // safe. + const path = pathUtil.longPath(spawn.pos, bank.pos, {range: 1, cache: true}); + if(path == ERR_NO_PATH || path.incomplete) return Infinity; + return path.cost; +} + +function spawnBusters(bank) { + const allSpawns = Object.values(Game.spawns); + // TODO: Decide distance threshold. + // It's a waste of energy to have a big buster spend most of its life + // traveling, but not having one at all is worse. + const availSpawns = allSpawns.filter(s => s.isActive() && !s.spawning && s.room.energyAvailable >= 600 && spawnDistance(s, bank) <= 600); + if(!availSpawns.length) return ERR_NOT_ENOUGH_ENERGY; // TODO: might not actually need more busters + + const roomName = bank.pos.roomName; + const spaces = spacesNear(roomName, bank.pos); + + // How long before we need to fit another melee attacker in? + // Should we spawn another ranged first? + + const ttl = bank.ticksToDecay; + + const meleeAssigned = keeperKiller.assigned(roomName); + const rangedAssigned = bankBuster.assigned(roomName); + + const meleeDmg = bank.pos.findInRange(meleeAssigned, 1).reduce( + (acc, c) => acc+c.getActiveBodyparts(ATTACK)*ATTACK_POWER*Math.min(c.ticksToLive, ttl), 0); + const rangedDmg = bank.pos.findInRange(rangedAssigned, 3).reduce( + (acc, c) => acc+c.getActiveBodyparts(RANGED_ATTACK)*RANGED_ATTACK_POWER*Math.min(c.ticksToLive, ttl), 0); + const remainingHealth = bank.hits - (meleeDmg + rangedDmg); + console.log(`hits=${bank.hits} - ${meleeDmg} - ${rangedDmg} = ${remainingHealth} left`); + + // How long does it take to do bank.hits damage with the nearby creeps? + // We have a total rate right now, and some time at which that will change + // (either because another arrives, or one ages out) + let ticks = 0; + // How long until the first creep dies (or the next arrives)? + // How many hits are left at that point? + // etc + + + if(remainingHealth < 0) return OK; // TODO: Figure out safety buffer + + // TODO: Could save a lot of energy to use more time cycling in melee busters + // rather than rushing ranged. + // Need to solve for how much damage we can reliably do just with melee over + // the full decay time, and only supplement with ranged to cover gaps in + // that. + for(const s of availSpawns) { + const dist = spawnDistance(s, bank); + if(dist == Infinity) continue; + const live = meleeAssigned.filter(c => c.ticksToLive >= dist); + if(live.length < spaces.melee) { + console.log(`${live.length} of ${meleeAssigned.length} vs ${spaces.melee} @ ${dist}, spawning melee`); + if(keeperKiller.spawn(s, roomName) == OK) { + console.log(`Spawning keeperKiller for ${roomName} from ${s.pos.roomName}`); + meleeAssigned.push({ticksToLive: 1500}); + } + // Save up for melee even if it didn't work, if we decided we wanted one. + availSpawns.shift(); + } + } + + for(const s of availSpawns) { + const dist = spawnDistance(s, bank); + const live = rangedAssigned.filter(c => c.ticksToLive >= dist); + if(live.length < spaces.ranged) { + console.log(`${live.length} of ${rangedAssigned.length} vs ${spaces.ranged} @ ${dist}, spawning ranged`); + if(bankBuster.spawn(s, roomName) == OK) { + console.log(`Spawning bankBuster for ${roomName} from ${s.pos.roomName}`); + rangedAssigned.push({ticksToLive: 1500}); + availSpawns.shift(); + } + } + } + return ERR_NOT_ENOUGH_RESOURCES +} + +module.exports = { +inProgress: function() { + return false; +}, +scoutTest: function() { + const baseRooms = Object.values(Game.spawns).map(s => s.room.name).filter(keepUnique); + for(const roomName in Memory.scout) { + const info = Memory.scout[roomName]; + if(!info.powerBank) continue; + + const pb = info.powerBank; + const ttl = pb.expires - Game.time; + + // TODO: make sure we don't abort gathering of a bank we did bust recently + if(ttl < 0) continue; // Stale scout + + const spaces = spacesNear(roomName, pb); + + // TODO: This doesn't seem like exactly the right approach, but have to start somewhere. + const hitsPerTick = Math.ceil(pb.hits / ttl); + const allAttack = Math.ceil(pb.hits / ttl / ATTACK_POWER); + const allRanged = Math.ceil(pb.hits / ttl / RANGED_ATTACK_POWER); + + const attackCost = (BODYPART_COST[ATTACK] + BODYPART_COST[MOVE]) * allAttack; + const rangedCost = (BODYPART_COST[RANGED_ATTACK] + BODYPART_COST[MOVE]) * allRanged; + + console.log(`Have ${ttl} ticks to bust ${roomName}: ${hitsPerTick} min hits/t w/ ${spaces.melee} melee, ${spaces.ranged} ranged spaces`); + console.log(` - could be ${allAttack} ATTACK (${attackCost} energy) or ${allRanged} RANGED_ATTACK (${rangedCost} energy)`); + + const room = Game.rooms[roomName]; + const bodyPower = c => c.getActiveBodyparts(ATTACK)*ATTACK_POWER + c.getActiveBodyparts(RANGED_ATTACK)*RANGED_ATTACK_POWER; + if(room) { // See if we already have anything attacking, figure out if it's enough. + const bank = room.find(FIND_STRUCTURES, {filter: s => s.structureType == STRUCTURE_POWER_BANK}).shift(); + let creeps; + if(bank) { + creeps = bank.pos.findInRange(FIND_MY_CREEPS, 3); + } else { + creeps = room.find(FIND_MY_CREEPS); + } + const dmg = creeps.reduce((acc, c) => acc + bodyPower(c), 0); + const total = creeps.reduce((acc, c) => acc + bodyPower(c) * Math.min(c.ticksToLive, ttl), 0); + console.log(` - dealing ~${dmg}/t now, ${total} before EoL - ${pb.hits - total} uncovered`); + } + + for(const baseName of baseRooms) { + const path = pathUtil.macroPath(baseName, roomName); + if(path == ERR_NO_PATH) continue; + // TODO: Weigh doing low-level pathing (and swamp/road calculations) for + // an accurate tick/step count. + const estDist = path.length * 50; + const base = Game.rooms[baseName]; + console.log(` - spawn[${baseName}] is ${path.length} rooms (~${estDist} steps) away w/ ${base.energyAvailable}/${base.energyCapacityAvailable} energy`); + } + } +}, +run: function(roomName) { + if(Game.time % 20 != 19) return; + + // TODO: calculate and cache travel times from nearby spawns w/ different speed ratios + // TODO: figure out how fast we need to break it + // TODO: look at how many attackers can fit around the bank, spawn those + // TODO: look at how many ranged can fit, spawn those + // TODO: figure out how soon it'll break at current speeds so haulers can be in place. + // TODO: make roads at least part of the way if there are swamps + const room = Game.rooms[roomName]; + if(!room) { + console.log(`plan.power[${roomName}] lost vis!`); + if(!scout.exists(roomName)) scout.spawn(roomName); + return ERR_INVALID_ARGS; + } + + const bank = room.find(FIND_STRUCTURES, {filter: {structureType: STRUCTURE_POWER_BANK}}).shift(); + // XXX existing, space + const needBust = bank; + + if(needBust) { + if(spawnBusters(bank) != OK) return; + } + + // TODO: If haulers are still on the way, and we have time, wait to bust + // until they're closer. + + const res = room.find(FIND_DROPPED_RESOURCES, {filter: {resourceType: RESOURCE_POWER}}).shift(); + let amount; + if(res) { + amount = res.amount + } else if(bank) { + amount = bank.power; + } + const assigned = hauler.assigned(roomName); + const assignedCapacity = assigned.reduce((acc, c) => acc + c.store.getCapacity(), 0); + const needHaul = (res && res.amount > 1000) || (bank && bank.hits < 800e3); + + if(needHaul) { + const spawns = Object.values(Game.spawns).filter(s => s.isActive() && !s.spawning); + for(const spawn of spawns) { + // XXX travel time is important + const body = new BodyBuilder([], spawn.room.energyAvailable); + body.extend([MOVE, CARRY, MOVE, MOVE, MOVE, MOVE, CARRY]); // basis for 2:1 swamp movement while laden + if(!body.count(CARRY)) continue; + const ret = spawn.spawnCreep(body.body, `longHaul-${spawn.room.name}-${Game.time}`, {memory: { + role: hauler.ROLE, + remoteRoom: roomName, + dest: Game.spawns.Home.room.storage.id, + cost: body.cost, + }}); + console.log(`plan.power[${roomName}] spawning longHauler from ${spawn.room.name}: ${ret}`); + } + } + + // TODO: Cleanup: + // - recycle (most) remaining attackers at the nearest spawn. + // - recycle haulers if they finish a trip and won't make another, or if there's nothing left. + // - delete the flag + // - maybe schedule some more scouts? +}, +}; diff --git a/plan.remoteHarvest.js b/plan.remoteHarvest.js new file mode 100644 index 0000000..73816bf --- /dev/null +++ b/plan.remoteHarvest.js @@ -0,0 +1,208 @@ +const pathUtil = require('util.pathing'); +const splay = require('util.splay'); +const resUtil = require('util.resources'); +const powerPlan = require('plan.power'); + +const scout = require('role2.scout'); +const roadWorker = require('role2.roadWorker'); +const keeperKiller = require('role2.keeperKiller'); +const reserver = require('role2.reserver'); +const dropHarvester = require('role2.dropHarvester'); +const hauler = require('role2.hauler'); + +/* +// Empirically, 3:1 strikes a good balance between path length and road maintenance cost. +[room E28S12 pos 18,19] -> [room E29S13 pos 37,36] with plainCost=1, swampCost=1 (1 ratio) takes 301 steps including 177 swamp tiles. ops=1598 +[room E28S12 pos 18,19] -> [room E29S13 pos 37,36] with plainCost=2, swampCost=3 (1.5 ratio) takes 307 steps including 129 swamp tiles. ops=5228 +[room E28S12 pos 18,19] -> [room E29S13 pos 37,36] with plainCost=1, swampCost=2 (2 ratio) takes 310 steps including 123 swamp tiles. ops=4529 +[room E28S12 pos 18,19] -> [room E29S13 pos 37,36] with plainCost=1, swampCost=3 (3 ratio) takes 312 steps including 121 swamp tiles. ops=4893 +[room E28S12 pos 18,19] -> [room E29S13 pos 37,36] with plainCost=1, swampCost=4 (4 ratio) takes 312 steps including 121 swamp tiles. ops=5024 +[room E28S12 pos 18,19] -> [room E29S13 pos 37,36] with plainCost=1, swampCost=5 (5 ratio) takes 323 steps including 118 swamp tiles. ops=5088 +*/ +const testOpts = [ + //[1, 1, '#ffffff'], + //[2, 3, '#ffcccc'], + //[1, 2, '#ffaaaa'], + [2, 6, '#ff7777'], + //[1, 4, '#ff4444'], + //[1, 5, '#ff1111'], +]; + +function roomMatrix(name) { + const room = Game.rooms[name]; + if(!room) return undefined; + + const out = new PathFinder.CostMatrix; + for(const s of room.find(FIND_STRUCTURES)) { + if(s.structureType == STRUCTURE_ROAD) { + out.set(s.pos.x, s.pos.y, 1); + } else if(s.structureType == STRUCTURE_WALL) { + out.set(s.pos.x, s.pos.y, 255); + } else if(s.structureType == STRUCTURE_RAMPART && !s.my) { + out.set(s.pos.x, s.pos.y, 255); + } + } + for(const s of room.find(FIND_CONSTRUCTION_SITES)) { + if(s.structureType == STRUCTURE_ROAD) { + out.set(s.pos.x, s.pos.y, 1); + } + } + return out; +} + +function swampRoads(src, dst, range=0, opts={}) { + const macroPath = pathUtil.macroPath(src.roomName, dst.roomName, opts.badRooms); + if(!macroPath) { + console.log(`unable to find map route between ${src.roomName} and ${dst.roomName}`); + } + const macroRooms = macroPath.map(e => e.room); + macroRooms.push(src.roomName); + + const p = opts.plainCost || 2; + const s = opts.swampCost || 6; + + const ret = PathFinder.search(src, {pos: dst, range}, {plainCost: p, swampCost: s, maxOps: 10000, + roomCallback: name => macroRooms.includes(name) ? roomMatrix(name) : false}); + if(opts.drawViz) Memory.viz['map'] = Game.map.visual.poly(ret.path).export(); + let swampSteps = 0; + let roadSteps = 0; + for(const pos of ret.path) { + const room = Game.rooms[pos.roomName]; + let road; + if(room) { + road = room.lookForAt(LOOK_STRUCTURES, pos).find(s => s.structureType == STRUCTURE_ROAD); + if(road) { + roadSteps++; + } + } + // TODO: memoize the terrain blob? + if(Game.map.getRoomTerrain(pos.roomName).get(pos.x, pos.y) == TERRAIN_MASK_SWAMP) { + swampSteps++; + if(opts.build && !ret.incomplete && !road) { + if(Game.rooms[pos.roomName]) pos.createConstructionSite(STRUCTURE_ROAD); + } + } + } + if(ret.incomplete) { + console.log(JSON.stringify(ret)); + } + console.log(`${src} -> ${dst} with plainCost=${p}, swampCost=${s} (${s/p} ratio) takes ${ret.path.length} steps including ${swampSteps} swamp tiles over ${roadSteps} existing visible roads. ops=${ret.ops}`); + const roomLines = ret.path.reduce( + (out, pos) => { + if(pos.roomName == out.lastRoom) { + out.cur.push(pos); + } else { + if(out.cur) { + out.lines.push(out.cur); + } + out.cur = [pos]; + out.lastRoom = pos.roomName; + } + return out; + }, + {lines: [], swampSteps: 0} + ); + if(roomLines.cur) roomLines.lines.push(roomLines.cur); + + if(opts.drawViz) { + const roomViz = {}; + for(const l of roomLines.lines) { + const r = l[0].roomName; + let viz = roomViz[r]; + if(!viz) { + viz = new RoomVisual(r); //.clear(); + roomViz[r] = viz; + } + viz.poly(l, {opacity: 0.25}); + } + for(const r in roomViz) { + Memory.viz[r] = roomViz[r].export(); + } + } + return macroPath; +} + +function plan(roomName) { + if(Game.time % 10 != 0) return; + if(powerPlan.inProgress()) return; + if(!splay.isTurn('plan.remoteHarvest', roomName, Game.time/10)) return; + + const room = Game.rooms[roomName]; + if(!room) { + if(Game.time % 100 == 0) console.log(`Lost visibility to ${roomName} for remote harvesting`); + // TODO: probably better to use a combatant or a harvester, but scouts are cheap enough... + if(!scout.exists(roomName)) scout.spawn(roomName); + return + } + + const activeSpawns = Object.values(Game.spawns).filter(s => s.isActive()); + const [anySpawn, anyPath] = pathUtil.macroClosest(roomName, activeSpawns.filter(s => !s.spawning), {flipPath: true}); + + // XXX make the returns of macroClosest easier to use. + + // TODO There's probably a better place for invader core elimination to live, + // but harvested rooms are the most annoying so far. + const blockers = room.find(FIND_HOSTILE_STRUCTURES); + if(anyPath && blockers.length && keeperKiller.assigned(roomName) < 3) { + const ret = keeperKiller.spawn(anySpawn, roomName); + if(ret != ERR_NOT_ENOUGH_ENERGY) console.log(`remoteHarvest.plan[${roomName}] trying to spawn keeperKiller for ${blockers}: ${ret}`); + return ret; + } + + // These don't work very well if they have to travel a long way from the spawn. + const [spawn, closePath] = pathUtil.macroClosest(roomName, activeSpawns, {flipPath: true}); + if(!closePath || spawn.spawning) return; + + // A: do we need to unreserve so we can use it? + const ctrl = room.controller; + const rsvs = reserver.assigned(roomName).filter(c => c.ticksToLive > 100); + if(ctrl.reservation && ctrl.reservation.username != 'baptr') { + if(rsvs.length < 2 && ctrl.reservation.ticksToEnd > 100) { + const ret = reserver.spawn(spawn, roomName); + if(ret != ERR_NOT_ENOUGH_ENERGY) console.log(`remoteHarvest.plan[${roomName}] spawning reserver over ${ctrl.reservation.username}: ${ret}`); + return ret; + } + } + + const harvesters = dropHarvester.assigned(roomName); + + // B: do we need to positively reserve to keep the workers busy? + if(rsvs.length < 1) { + // TODO: count work parts in harvesters, compare to base energy regen rate + // for the room, decide if we should reserve... + // TODO: store this decision? (but update as workers get bigger..) + // XXX... finish decision/setup for reserving + const ret = reserver.spawn(spawn, roomName); + return ret; + } + + // Spawn roadWorker remote bootstrappers to do initial harvesting and build + // roads with it. + // TODO: plan out the roads automatically + const srcs = room.find(FIND_SOURCES); + for(const src of srcs) { + const exist = roadWorker.assigned(src.pos); + if(!exist.length) { + return roadWorker.spawn(spawn, src.pos); + } + } + + if(harvesters.filter(c => c.ticksToLive > 100).length < srcs.length) { + return dropHarvester.spawnRemote(spawn, roomName); + } + + // spawn dedicated haulers for overflow... + const hauls = hauler.assigned(roomName); + const dist = closePath.length; + const availRes = resUtil.roomResource(room); + if(hauls.length < dist*2 && (hauls.length+1) * 850 < availRes) { + const ret = hauler.spawnRemote(spawn, roomName); + console.log(`remoteHarvest[${roomName}] had ${hauls.length} haulers to go ${dist} away, for ${availRes} energy: ${ret}`); + return ret + } +} + +module.exports = { + swampRoads, + plan, +}; diff --git a/plan.room.js b/plan.room.js index d3a0fe1..3ec87ac 100644 --- a/plan.room.js +++ b/plan.room.js @@ -7,9 +7,15 @@ const bob = require('role.bob'); const hauler = require('role2.hauler'); const storeUpgrader = require('role2.storeUpgrader'); const remoteHarvester = require('role2.remoteHarvester'); +const relocator = require('role.relocater'); +const powerLoader = require('role2.powerLoader'); +const local = require('local'); const splay = require('util.splay'); -const pathing = require('util.pathing'); +const pathUtil = require('util.pathing'); +const BodyBuilder = require('util.bodybuilder'); + +const powerPlan = require('plan.power'); /* TODOs: - Add additional spawners at some point to avoid spawn time bottlenecks. @@ -30,7 +36,7 @@ function planRoads(room) { filter: s => ROAD_DESTS.includes(s.structureType), }); - const pathMatrix = pathing.roomCallback(room.name); + const pathMatrix = pathUtil.roomCallback(room.name); // pairwise among all of them is probably best... var all = sources.concat(dests); var ret = []; @@ -43,7 +49,7 @@ function planRoads(room) { if(from instanceof StructureController) { [from, to] = [to, from]; } - ret.push(...pathing.swampCheck(from.pos, to.pos, pathMatrix)); + ret.push(...pathUtil.swampCheck(from.pos, to.pos, pathMatrix)); } } _.forEach(ret, p => room.createConstructionSite(p, STRUCTURE_ROAD)); @@ -68,6 +74,7 @@ function numBuilding(room, type) { // - Maybe leave a good path there (best path from spawn?) // - Or maybe diamond around the sources themselves? // TODO(baptr): Memoize this in room memory until controller level changes. +// TODO: Fill in the spaces between buildings around the spawn with roads if swampy. function planBuildings(pos, types) { const room = Game.rooms[pos.roomName]; if(!room) { @@ -138,44 +145,77 @@ function countCPU(id, f) { return ret; } +const SCARY_PARTS = [ATTACK, RANGED_ATTACK, CLAIM]; + module.exports = { run: function(room) { if(!room) { if((Game.time/40) % 10 == 0) console.log("No room provided to planner!"); return; } - var spawns = room.find(FIND_MY_SPAWNS, {filter: s => s.isActive}); - if(!spawns.length) { - if((Game.time/40) % 10 == 0) console.log("Awaiting spawn in", room.name); + + const localSpawns = room.find(FIND_MY_SPAWNS); + const availSpawns = localSpawns.filter(s => s.isActive() && !s.spawning); + + // Don't try to defend rooms we're just passing through. + const ctrl = room.controller; + const isMine = ctrl && (ctrl.my || (ctrl.reservation && ctrl.reservation.username == 'baptr') || room.find(FIND_MY_STRUCTURES) > 0 || room.find(FIND_MY_CREEPS) > 2) || local.remoteHarvestRooms.includes(room.name); + + const hostiles = room.find(FIND_HOSTILE_CREEPS, {filter: c => c.body.some(b => b.hits && SCARY_PARTS.includes(b.type))}); + // XXX base life check on distance + const existingDef = roleDefender.assigned(room.name).filter(c => c.ticksToLive > 300); + // Defenders sack in-room if there's nothing to attack, so we only want to + // leave standing ones for rooms without their own spawns... + let needDefense = isMine && hostiles.length > existingDef.length; + if(!needDefense && isMine && localSpawns.length == 0) { + let wantDefs = 0; // XXX decide if standby defenders are helpful + if(local.defenseRooms && room.name in local.defenseRooms) { + wantDefs = local.defenseRooms[room.name]; + } + needDefense = existingDef.length < wantDefs; + //console.log(`plan.room[${room}] existingDefs=${existingDef.length} < ${wantDefs}?`); + } + if(needDefense) { + if(Game.time % 20 == 0) console.log(`${room} needs defenses from: ${hostiles}`); + if(availSpawns.length) { + if(roleDefender.spawn(availSpawns[0], {}) == OK) availSpawns.shift(); + } else { + const allSpawns = Object.values(Game.spawns).filter(s => s.isActive() && !s.spawning); + const [remSpawn, path] = pathUtil.macroClosest(room.name, allSpawns, {flipPath: true}); + if(remSpawn != ERR_NO_PATH) { + const body = new BodyBuilder([MOVE, MOVE, MOVE, MOVE, MOVE, RANGED_ATTACK, HEAL], remSpawn.room.energyAvailable); + body.extend([MOVE, MOVE, MOVE, MOVE, MOVE, RANGED_ATTACK, RANGED_ATTACK]); + const ret = remSpawn.spawnCreep(body.body, `relo-defender-${room.name}-${Game.time}`, { + memory: relocator.setMem({roomPath: path, targetRoom: room.name, cost: body.cost}, room.name, roleDefender.ROLE)}); + // Return early if we're saving up energy. + // XXX may end up making it harder to gather energy? + if(ret == ERR_NOT_ENOUGH_ENERGY) return ret; + console.log(`Spawning remote relo defender for ${room.name}: ${ret}`); + } + } return; } - var spawn = _.find(spawns, s => !s.spawning); - if(!spawn) { return } - var hostiles = room.find(FIND_HOSTILE_CREEPS, {filter: c => { - var body = _.groupBy(c.body, 'type'); - if(body[ATTACK] || body[RANGED_ATTACK] || body[CLAIM]) return true; - return false; - }}); - if(hostiles.length > 0) { - if(Game.time % 20 == 0) console.log(`${room} under attack: ${hostiles}`); - roleDefender.spawn(spawn, {}); + + if(!availSpawns.length) { + if(room.controller && (Game.time/40) % 10 == 0) console.log("Awaiting spawn in", room.name); return; } + // TODO(baptr): Only do this when room control level changes, // or scale out the time further. // XXX just splay this instead of the whole room? if(room.controller.level > (room.memory.level || 0) || splay.isTurn('room', room.name, Game.time/500)) { - planBuildings(spawn.pos, [STRUCTURE_EXTENSION, STRUCTURE_TOWER]); + planBuildings(availSpawns[0].pos, [STRUCTURE_EXTENSION, STRUCTURE_TOWER]); planRoads(room); planMining(room); room.memory.level = room.controller.level; } - _.forEach(spawns, s => { - // TOOD(baptr): Let the spawn functions pick spawn so they don't have to - // recalculate all the room stuff. - if(!s.spawning) spawnCreeps(s, room); - }); + for(const s of availSpawns) { + // TOOD(baptr): Let the spawn functions pick spawn so they don't have to + // recalculate all the room stuff. + if(!s.spawning) spawnCreeps(s, room); + } }, planRoads, }; @@ -191,38 +231,37 @@ function couldSpawn(room, limit=1) { // creep if there are multiple spawns in a room. None of the enqueued changes // effect the checks for the next spawner in the caller's loop... function spawnCreeps(spawn, room) { + if(spawn.spawning) return; + var creeps = room.find(FIND_MY_CREEPS); var kinds = _.groupBy(creeps, c => c.memory.role); var numRole = r => (kinds[r] || []).length; // TODO(baptr): Make this more dynamic based on available harvest spots. - if(numRole(bootstrapper.ROLE) > 3 && numRole(dropHarvester.ROLE) < 2) { + if(numRole(bootstrapper.ROLE) > 2 && numRole(dropHarvester.ROLE) < 2) { dropHarvester.spawn(spawn); + if(spawn.spawning) return; } - if(spawn.spawning) return; - - // XXX super hack - if(Game.time % 1500 == 840 && spawn.room.name == 'E16N27') { - if(remoteHarvester.spawn(spawn, 'E15N27') == OK) return; - } - if(spawn.spawning) return; if(bootstrapper.spawnCondition(room, numRole(bootstrapper.ROLE))) { bootstrapper.spawn(spawn); + if(spawn.spawning) return; } - if(spawn.spawning) return; - - // XXX If this stops running every tick, this sort of delay tactic will fail horribly. - if(Game.time % 100 == 0) { // bleh - dropHarvester.spawn(spawn); + + if(powerLoader.spawnCondition(room)) { + powerLoader.spawn(spawn); + if(spawn.spawning) return; } - if(spawn.spawning) return; - if(room.energyAvailable == room.energyCapacityAvailable && room.energyAvailable >= 800) { + // RCL 2 seems early enough to start trying to use storeUpgraders + if(room.energyAvailable >= room.energyCapacityAvailable*0.75 && room.energyAvailable > 500) { if(hauler.spawnCondition(room, numRole(hauler.ROLE))) { hauler.spawn(spawn); } if(spawn.spawning) return; + + // TODO: better ways to save up. + if(powerPlan.inProgress()) return; if(storeUpgrader.spawnCondition(room, numRole(storeUpgrader.ROLE))) { storeUpgrader.spawn(spawn); @@ -234,6 +273,8 @@ function spawnCreeps(spawn, room) { } if(spawn.spawning) return; } + + if(powerPlan.inProgress()) return; var minerNeeded = room.memory.needMiner; if(minerNeeded) { @@ -244,8 +285,8 @@ function spawnCreeps(spawn, room) { // TODO(baptr): improve delete room.memory.needMiner; } + if(spawn.spawning) return; } - if(spawn.spawning) return; var bobNeeded = room.memory.needBob; if(bobNeeded && Game.time % 100 == 32) { @@ -254,5 +295,6 @@ function spawnCreeps(spawn, room) { if(bob.spawn(spawn, bobNeeded.res, bobNeeded.src, bobNeeded.dest) == OK) { delete room.memory.needBob; } + if(spawn.spawning) return; } -} \ No newline at end of file +} diff --git a/plan.safemode.js b/plan.safemode.js new file mode 100644 index 0000000..d8e684e --- /dev/null +++ b/plan.safemode.js @@ -0,0 +1,51 @@ +const creepUtil = require('util.creep'); + +module.exports = { +check: function(room) { + const ctrl = room.controller; + if(!ctrl || !ctrl.my || !ctrl.safeModeAvailable || ctrl.safeModeCooldown) return false; + + const hostiles = room.find(FIND_HOSTILE_CREEPS); + if(!hostiles.length) return false; + + // TODO: Is it safe to splay this? If I'm counting incoming damage from the + // event log, we don't want to miss it.. but it seems expensive. + + // TODO: diminish danger by travel time necessary to demonstrate it? + // TODO: and ticks of life left. + let danger = 0; + for(const c of hostiles) { + danger += creepUtil.bodyPower(c, CLAIM, CONTROLLER_CLAIM_DOWNGRADE); + danger += creepUtil.bodyPower(c, ATTACK, ATTACK_POWER); + danger += creepUtil.bodyPower(c, RANGED_ATTACK, RANGED_ATTACK_POWER); + } + + // Seems like little enough that we should be able to survive it..? + // Just 5 attack, or 15 ranged. + if(danger < 300) return false; + + let dmg = 0; + // TODO: Check attackController events too. + for(const l of room.getEventLog()) { + if(l.event != EVENT_ATTACK) return; + const src = Game.getObjectById(l.objectId); + if(!src || src.my) return; + const dest = Game.getObjectById(l.data.targetId); + if(dest && dest.my) { + dmg += l.data.damage; + } + } + // TODO: Maybe use a lower threshold for actual damage? + // TODO: Or wait for closer to wall/rampart breach? + if(dmg < 300) return false; + + // TODO: Something else? + // maybe when towers are empty, or we've run out of ways to get energy for defenders? + + if(ctrl.safeModeAvailable < 2) { + console.log(`Would recommend safemode in ${room.name} at tick ${Game.time}, but refusing to spend the last few`); + return false; + } + return true; +}, +} diff --git a/plan.scout.js b/plan.scout.js new file mode 100644 index 0000000..d61ee84 --- /dev/null +++ b/plan.scout.js @@ -0,0 +1,76 @@ +const scoutUtil = require('util.scout'); +const scoutRole = require('role2.scout'); + +function keepUnique(val, idx, arr) { + return arr.indexOf(val) == idx; +} + +function parseRoom(name) { + const parsed = /^([WE])([0-9]+)([NS])([0-9]+)$/.exec(name); + return { + name: name, + sector: parsed[1]+parsed[3], + x: parseInt(parsed[2], 10), + y: parseInt(parsed[4], 10), + }; +} + +function update() { + for(const name in Game.rooms) { + if(scoutUtil.needsUpdate(name)) { + scoutUtil.update(Game.rooms[name]); + } + } +} + +function run() { + // TODO: do macro-level searching to decide where else to go. + const baseRooms = Object.values(Game.spawns).map(s => s.room.name).filter(keepUnique); + const worldSize = Game.map.getWorldSize(); + const maxDim = worldSize/2 - 1; + const targetRooms = new Set(); + + for(const src of baseRooms) { + const p = parseRoom(src); + // Find the closest highway intersection as the base point. + const baseX = Math.round(p.x/10)*10; + const baseY = Math.round(p.y/10)*10; + + console.log(`plan.scout[${src}] - start (${baseX}, ${baseY})`); + + // Scout towards the next nearest intersection in each direction. + for(let [x, y] of [[baseX+10, baseY], [baseX-10, baseY], [baseX, baseY+10], [baseX, baseY-10]]) { + if(x > maxDim) continue; + if(y > maxDim) continue; + let ew = p.sector[0]; + let ns = p.sector[1]; + if(x < 0) { + ew = (ew == 'W' ? 'E' : 'W'); + x = -x + 1; + } + if(y < 0) { + ns = (ns == 'N' ? 'S' : 'N'); + y = -y + 1; + } + const dest = `${ew}${x}${ns}${y}`; + if(Game.map.getRoomStatus(dest).status == 'closed') continue; + targetRooms.add(dest); + } + } + // TODO: Strike out destinations that we'd hit via other scouts. + // TODO: Notice if we'd miss intermediate highway rooms (generally the + // closest ones :-\) + for(const r of targetRooms) { + if(!scoutRole.exists(r)) { + // TODO: This doesn't optimize to always use the spawner closest to the + // target, it just choses the closer ones of those *currently available*. + // XXX return hack to not fight with ourselves to use the spawners. + if(scoutRole.spawn(r) == OK) return; + } + } +} + +module.exports = { + update, + run, +}; diff --git a/role.claimer.js b/role.claimer.js index 6aaf476..02544aa 100644 --- a/role.claimer.js +++ b/role.claimer.js @@ -2,6 +2,8 @@ const roleUpgrader = require('role.upgrader'); const roleBootstrapper = require('role2.bootstrapper'); const BodyBuilder = require('util.bodybuilder'); const util = require('util.creep'); +const pathUtil = require('util.pathing'); +const local = require('local'); function sack(creep) { spawn = creep.pos.findClosestByPath(FIND_MY_SPAWNS); @@ -27,50 +29,72 @@ module.exports = { return !_.find(Memory.creeps, c => c.role == ROLE && c.targetRoom == room.name) }, - spawn: function(spawn, room) { - if(spawn.room.findExitTo(room) < 0) { - console.log(`Unable to spawn claimer for room ${room}, no exit`); - return false; - } - - var builder = new BodyBuilder([CLAIM, MOVE], spawn.room.energyAvailable); - builder.extend([MOVE, MOVE, CARRY, WORK], limit=3); - // TODO(baptr): Add some tough if they go through hostile rooms. + spawn: function(spawn, room, destPos=null) { + if(spawn.spawning) return ERR_BUSY; - var mem = {role: ROLE, targetRoom: room}; + const body = new BodyBuilder([MOVE, MOVE, MOVE, MOVE, MOVE, CLAIM], spawn.room.energyAvailable); + //body.extend([MOVE, MOVE, CARRY, WORK], limit=3); + // TODO(baptr): Add some tough if they go through hostile rooms. + + if(!body.valid()) return ERR_NOT_ENOUGH_ENERGY; - return spawn.spawnCreep(builder.sort(), `${ROLE}-${room}`, {memory: mem}); + const macroPath = pathUtil.macroPath(spawn.pos.roomName, room, undefined, undefined, 1); + if(macroPath == ERR_NO_PATH) { + console.log(`Unable to spawn claimer for room ${room}, no macro path to goal`); + return ERR_NO_PATH; + } + + // TODO: decide whether it's worth serializing and using the full path + const macroRooms = macroPath.map(e => e.room).concat([spawn.pos.roomName]); + if(!destPos) destPos = new RoomPosition(25, 25, room); + const path = PathFinder.search(spawn.pos, destPos, { + maxOps: 10000, + swampCost: 1, + plainCost: 1, + maxCost: 590, // Leave a little time to travel to the controller and actually claim. + roomCallback: name => macroRooms.includes(name) ? pathUtil.roomCallback : false}); + if(path.incomplete) { + console.log(`Unable to spawn claimer for room ${room}, no low level path to goal`); + return ERR_NO_PATH; + } + const exitPath = pathUtil.exitPath(path); + + const mem = {role: ROLE, targetRoom: room, exitPath}; + + return spawn.spawnCreep(body.sort(), `${ROLE}-${room}`, {memory: mem}); }, run: function(creep) { - // It can happen that the path to the controller ends up leaving the room. - // Without remembering the controller position, the creep might go right - // back in to spot it again, and get stuck on the border. - const targetRoom = creep.memory.targetRoom; - - var room = Game.rooms[targetRoom]; - if(room) { - var ctrl = room.controller; - if(ctrl.my) return sack(creep); - if(!creep.pos.isNearTo(ctrl)) { - return creep.moveTo(ctrl); - } - var ret = creep.claimController(ctrl); - switch(ret) { - case ERR_GCL_NOT_ENOUGH: - if(Game.time % 20 == 0) console.log(`Too soon to claim ${targetRoom}`); - break; - case OK: - console.log(`Welcome to ${targetRoom}!!`); - if(creep.getActiveBodyparts(WORK) > 0) { - creep.memory.role = roleBootstrapper.ROLE; - } - break; - default: - console.log(`Unrecognized claim(${targetRoom}) ret: ${ret}`); - return ret; + // It can happen that the path to the controller ends up leaving the room. + // Without remembering the controller position, the creep might go right + // back in to spot it again, and get stuck on the border. + const targetRoom = creep.memory.targetRoom; + + if(creep.memory.exitPath) return pathUtil.macroMove(creep, {plainCost: 1, swampCost: 1}); + + var room = Game.rooms[targetRoom]; + if(room) { + var ctrl = room.controller; + if(ctrl.my) return sack(creep); + if(!creep.pos.isNearTo(ctrl)) { + return creep.moveTo(ctrl); + } + var ret = creep.claimController(ctrl); + switch(ret) { + case ERR_GCL_NOT_ENOUGH: + if(Game.time % 20 == 0) console.log(`Too soon to claim ${targetRoom}`); + break; + case OK: + console.log(`Welcome to ${targetRoom}!!`); + if(creep.getActiveBodyparts(WORK) > 0) { + creep.memory.role = roleBootstrapper.ROLE; } - } else { - return creep.moveTo(new RoomPosition(25, 25, targetRoom)); + break; + default: + console.log(`Unrecognized claim(${targetRoom}) ret: ${ret}`); + return ret; } + } else { + return creep.moveTo(new RoomPosition(25, 25, targetRoom)); + } } -}; \ No newline at end of file +}; diff --git a/role.defender.js b/role.defender.js index 90d8d6d..3d12390 100644 --- a/role.defender.js +++ b/role.defender.js @@ -1,4 +1,30 @@ +const BodyBuilder = require('util.bodybuilder'); +const local = require('local'); + +// TODO: Finish modernization, but it's hard to match the same algorithm with +// BodyBuilder. +function mkBody(spawn) { + const body = new BodyBuilder([], spawn.room.energyAvailable); + body.extend([MOVE, MOVE, RANGED_ATTACK], limit=3); + body.extend([MOVE, MOVE, HEAL], limit=2); + body.extend([MOVE, MOVE, RANGED_ATTACK], limit=3); + body.extend([MOVE, MOVE, HEAL], limit=2); + body.extend([MOVE, RANGED_ATTACK]); + return body; +} + +const ROLE = 'defender'; + module.exports = { + ROLE, + assigned: function(roomName) { + return Object.values(Game.creeps).filter(c => { + if(c.memory.reloNextRole == ROLE && c.memory.reloRoom == roomName) return true; + if(c.memory.role != ROLE) return false; + if(c.memory.targetRoom == roomName) return true; + return c.pos.roomName == roomName; + }); + }, // spawn a defender creep using as much of the available energy as possible. spawn: function(spawn, mem={}) { var available = spawn.room.energyAvailable; @@ -35,38 +61,93 @@ module.exports = { // body cost is a reasonable sort for TOUGH < MOVE < ATTACK // TODO(baptr): Though MOVE should be spent first, probably... body.sort((a,b) => BODYPART_COST[a] - BODYPART_COST[b]); - mem.role = 'defender'; + mem.role = ROLE; return spawn.spawnCreep(body, 'defender_'+Game.time, {memory: mem}); }, run: function(creep) { + let enemy = Game.getObjectById(creep.memory.enemy); + if(!enemy) { + enemy = creep.pos.findClosestByPath(FIND_HOSTILE_CREEPS); + if(enemy) creep.memory.enemy = enemy.id; + } + if(enemy) creep.moveTo(enemy); + + if(creep.hits < creep.hitsMax) { + creep.rangedMassAttack(); + creep.heal(creep); + } else { + const friend = creep.pos.findClosestByRange(FIND_MY_CREEPS, {filter: c => c.hits < c.hitsMax}); + if(friend) { + const range = creep.pos.getRangeTo(friend); + if(range == 1) { + creep.rangedMassAttack(); + creep.heal(friend); + } else if(!enemy) { + creep.rangedHeal(friend); + } + creep.moveTo(friend); + } + } + + if(!enemy) { if(creep.memory.targetRoom && creep.memory.targetRoom != creep.room.name) { - var dir = creep.room.findExitTo(creep.memory.targetRoom); - var exit = creep.pos.findClosestByPath(dir); - creep.moveTo(exit); - return; + return creep.moveTo(new RoomPosition(25, 25, creep.memory.targetRoom)); + } else { + creep.moveTo(25, 25, {range: 3}); + } + } + + const enemySites = creep.room.find(FIND_CONSTRUCTION_SITES).filter(cs => !cs.my); + const tower = enemySites.sort((a, b) => b.progress - a.progress).find(s => s.structureType == STRUCTURE_TOWER && s.progress); + if(tower) { + creep.moveTo(tower); + if(creep.pos.isEqualTo(tower.pos)) creep.move(0,0); // Hack to not stay on it if it was placed under us. + creep.rangedMassAttack(); + return; + } + + // TODO(baptr): Prioritize healers. + if(!enemy) { + enemy = creep.pos.findClosestByPath(FIND_HOSTILE_STRUCTURES); + } + if(!enemy) { + const spawn = creep.pos.findClosestByPath(FIND_MY_SPAWNS); + // TODO: Only keep the N~=3? best, and move them out of the way. + if(spawn && !local.defenseRooms[creep.room.name]) { + creep.say("🔙 to dust!"); + creep.moveTo(spawn); + spawn.recycleCreep(creep); + creep.heal(creep); + return false; } - - // TODO(baptr): Prioritize healers. - var enemy = creep.pos.findClosestByPath(FIND_HOSTILE_CREEPS); - if(!enemy) { - spawn = creep.pos.findClosestByPath(FIND_MY_SPAWNS); - if(!spawn) { - creep.say("No more!"); - creep.suicide(); - return false; - } - creep.say("🔙 to dust!"); - creep.moveTo(spawn); - spawn.recycleCreep(creep); - return false; + const gather = Game.flags.gather; + if(gather && gather.pos.roomName == creep.pos.roomName) { + creep.moveTo(gather.pos, {range: 3}); } + } else { creep.say('Yarrrrr', true); - if(creep.rangedAttack(enemy) == ERR_NOT_IN_RANGE) { - creep.moveTo(enemy, {reusePath: 0}); + const range = creep.pos.getRangeTo(enemy); + if(range > 3) { + creep.moveTo(enemy, {reusePath: 0}); + } + const baddies = creep.pos.findInRange(FIND_HOSTILE_CREEPS, 5); + const scary = baddies.filter(c => c.getActiveBodyparts(ATTACK) + c.getActiveBodyparts(RANGED_ATTACK)); + const nearBy = creep.pos.findInRange(baddies, 3); + if(nearBy.length > 2) { + // TODO: calculate damage? + creep.rangedMassAttack(); + creep.heal(creep); + } else { + if(creep.rangedAttack(enemy) == OK) { + creep.memory.delivered += creep.getActiveBodyparts(RANGED_ATTACK) * RANGED_ATTACK_POWER; + } } - - if(creep.hits < creep.hitsMax) { - creep.heal(creep); + if(scary.length) { + const path = PathFinder.search(creep.pos, scary.map(b => ({range: 3, pos: b.pos})), {flee: true, maxCost: 100}); + if(path.path.length) { + creep.move(creep.pos.getDirectionTo(path.path[0])); + } } - } -}; \ No newline at end of file + } + } +}; diff --git a/role.pcRenew.js b/role.pcRenew.js new file mode 100644 index 0000000..2f766ec --- /dev/null +++ b/role.pcRenew.js @@ -0,0 +1,35 @@ +const pathUtil = require('util.pathing'); + +module.exports = { + run: function(pc) { + // TODO: memoize the renewal spot in a way that notices if we pass a better + // candidate. + + const target = pc.pos.findClosestByPath(FIND_STRUCTURES, { + filter: s => s.structureType == STRUCTURE_POWER_BANK || s.structureType == STRUCTURE_POWER_SPAWN}); + if(target) { + pc.moveTo(target, {swampCost: 1, plainCost: 1}); + pc.renew(target); + return; + } + + let options = [] + // XXX look in scouted rooms too once ready + for(const name in Game.rooms) { + const r = Game.rooms[name]; + options = options.concat(r.find(FIND_STRUCTURES, {filter: s => { + if(s.structureType == STRUCTURE_POWER_BANK) return true; + if(s.structureType == STRUCTURE_POWER_SPAWN && s.my) return true; + return false; + }})); + } + // console.log(`PC spawn options len=${options.length}: ${JSON.stringify(options)}`); + const [remoteTarget, path] = pathUtil.macroClosest(pc.room.name, options); + if(!path) { + console.log(`PC[${pc.name}] unable to find renewal bank/spawn!`); + // TODO: Start exploring nearby highways or something? + return ERR_NOT_FOUND; + } + pc.moveTo(remoteTarget, {plainCost: 1, swampCost: 1}); + }, +}; diff --git a/role.relocater.js b/role.relocater.js index 755a467..94af1a2 100644 --- a/role.relocater.js +++ b/role.relocater.js @@ -1,30 +1,50 @@ +const pathUtil = require('util.pathing'); + // dummy role that moves between rooms before switching to a new role // example: // Game.spawns.Spawn2.spawnCreep([WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE, MOVE], 'relo-builder', {memory: relocator.setMem({}, 'W4N8', 'builder')}) module.exports = { - setMem: function(mem, roomName, nextRole) { - mem.role = 'relocater'; - mem.reloRoom = roomName; - mem.reloNextRole = nextRole; - return mem; + setMem: function(mem, roomName, nextRole, srcRoom=null) { + mem.role = 'relocater'; + mem.reloRoom = roomName; + mem.reloNextRole = nextRole; + if(srcRoom) { + srcPos = Game.rooms[srcRoom].find(FIND_MY_SPAWNS)[0].pos; + dstPos = new RoomPosition(25, 25, roomName); + return pathUtil.setMem(mem, srcPos, dstPos); + } + return mem; }, run: function(creep) { - if(creep.store.getFreeCapacity(RESOURCE_ENERGY) > 0) { - const res = creep.pos.findClosestByPath(FIND_DROPPED_RESOURCES, {filter: {resourceType: RESOURCE_ENERGY}}); - if(res) { - creep.moveTo(res); - let ret = creep.pickup(res); - if(ret != ERR_FULL) return; + // Run any heal parts on the way. + if(creep.body.some(b => b.hits && b.type == HEAL)) { + if(creep.hits < creep.hitsMax) { + creep.heal(creep); + } else { + const friend = creep.pos.findClosestByRange(FIND_MY_CREEPS, {filter: c => c.hits < c.hitsMax}); + if(friend) { + const range = creep.pos.getRangeTo(friend.pos); + if(range <= 1) { + creep.heal(friend); + } else if(range <= 3) { + creep.rangedHeal(friend); } + } } - var tgt = creep.memory.reloRoom; + } + + // XXX + if(creep.memory.roomPath) return pathUtil.macroMove(creep); + if(creep.memory.exitPath) return pathUtil.macroMove(creep); + + const tgt = creep.memory.reloRoom; if(!tgt) { console.log(`${creep.name} has invalid target room ${tgt}`); return; } - creep.moveTo(new RoomPosition(25,25,tgt)); if(creep.room.name == tgt) { + creep.moveTo(25, 25); // XXX is there some risk this leaves the room before converting? var newRole = creep.memory.reloNextRole; creep.say('Now '+newRole) creep.memory.role = newRole; @@ -33,5 +53,17 @@ module.exports = { delete creep.memory.reloExit; return; } + + if(creep.store.getFreeCapacity(RESOURCE_ENERGY) > 0) { + const res = creep.pos.findClosestByPath(FIND_DROPPED_RESOURCES, {filter: {resourceType: RESOURCE_ENERGY}}); + if(res) { + creep.say("Chasing resources..."); + creep.moveTo(res); + let ret = creep.pickup(res); + if(ret != ERR_FULL) return; + } + } + + creep.moveTo(new RoomPosition(25,25,tgt)); } -}; \ No newline at end of file +}; diff --git a/role2.bankBuster.js b/role2.bankBuster.js new file mode 100644 index 0000000..2ea5fb1 --- /dev/null +++ b/role2.bankBuster.js @@ -0,0 +1,57 @@ +const BodyBuilder = require('util.bodybuilder'); +const util = require('util.creep'); + +const ROLE = 'bankBuster'; + +module.exports = { + ROLE, + assigned: function(roomName) { + return Object.values(Game.creeps).filter(c => c.memory.role == ROLE && c.memory.targetRoom == roomName); + }, + spawn: function(spawn, targetRoom) { + const body = new BodyBuilder([], spawn.room.energyAvailable); + body.extend([MOVE, RANGED_ATTACK]); + body.extend([MOVE]); + + if(body.count(RANGED_ATTACK) < 3) return ERR_NOT_ENOUGH_ENERGY; + + return spawn.spawnCreep(body.sort(), `${ROLE}-${spawn.room.name}-${Game.time}`, {memory: { + role: ROLE, + targetRoom, + cost: body.cost, + life: {}, + }}); + }, + run: function(creep) { + util.track(creep, 'alive'); + + if(creep.room.find(FIND_FLAGS).find(f => f.name == 'wait')) return; + + // XXX Look for/shoot back at nearby enemies on the way. + + let target = Game.getObjectById(creep.memory.target); + if(!target) { + if(creep.pos.roomName != creep.memory.targetRoom) { + return util.track(creep, 'move', creep.moveTo(new RoomPosition(25, 25, creep.memory.targetRoom))); + } + + target = creep.pos.findClosestByRange(FIND_HOSTILE_CREEPS); + if(!target) { + target = creep.pos.findClosestByRange(FIND_HOSTILE_STRUCTURES); + } + if(!target) { + // For broken banks, maybe flee the resources but stay closer by? + return util.track(creep, 'idle move', creep.moveTo(25, 25)); + } + } + + // TODO: Make space for melee replacements to get through + const range = creep.pos.getRangeTo(target); + if(range > 3) creep.moveTo(target, {range: 3}); + if(range <= 4) { + const ret = util.track(creep, 'ranged_attack', creep.rangedAttack(target)); + if(ret == OK) creep.memory.delivered += creep.getActiveBodyparts(RANGED_ATTACK)*RANGED_ATTACK_POWER; + return ret + } + }, +}; diff --git a/role2.bootstrapper.js b/role2.bootstrapper.js index a24b168..4443034 100644 --- a/role2.bootstrapper.js +++ b/role2.bootstrapper.js @@ -1,4 +1,5 @@ const util = require('util.creep'); +const pathUtil = require('util.pathing'); const resources = require('util.resources'); const BodyBuilder = require('util.bodybuilder'); @@ -21,6 +22,11 @@ const BUILD_STRUCTS = [ STRUCTURE_ROAD, STRUCTURE_EXTRACTOR, ]; +const PRIORITY_BUILD_TYPES = [ + STRUCTURE_TOWER, + STRUCTURE_EXTENSION, + STRUCTURE_CONTAINER, +]; function trace(creep, msg) { if(!creep.memory.trace) return; @@ -38,10 +44,10 @@ spawnCondition: function(room, numBoots) { // Hardcoded 6/4/2 was too much for a single-source room. // TODO(baptr): Scale down when first starting and getting assistance from another room. - if(numBoots >= numSources*4) { return false } + if(numBoots >= numSources*3) { return false } // TODO(baptr): Tune limit before leaving more room for other types. - if(energyCap > 1000 && numBoots >= numSources*2 && spareEnergy < numSources*3000) { + if(energyCap > 1000 && numBoots >= numSources*2.5 && spareEnergy < numSources*3000) { return false; } if(numBoots >= numSources*1.5 && energyAvail < 0.9*energyCap) { @@ -62,7 +68,6 @@ spawn: function(spawn, extMem={}) { builder.extend([WORK, CARRY, MOVE, MOVE], limit=2); builder.extend([CARRY, MOVE], limit=3) builder.extend([WORK, MOVE], limit=2); - builder.extend([CARRY, MOVE]); builder.sort(); @@ -79,6 +84,20 @@ spawn: function(spawn, extMem={}) { // - Deliver for spawning, then build extensions only, then upgrade run: function(creep) { util.track(creep, 'alive'); + +/* + if(util.flee(creep, 6, 1) == OK) { + console.log(`${creep.name} is running away from attackers`); + return; + } + */ + + if(creep.carryCapacity == 0) { + // XXX place (a) MOVE last so it can flee for help, or something? + creep.say("Help :-("); + return; + } + if(creep.carry.energy == creep.carryCapacity) creep.memory.filling = false; if(creep.carry.energy == 0) { delete creep.memory.src; @@ -106,6 +125,7 @@ run: function(creep) { ret = creep.withdraw(src, RESOURCE_ENERGY); util.track(creep, 'withdraw', ret); pickupPower = src.store.energy; + delete creep.memory.src; } else { ret = creep.harvest(src); util.track(creep, 'harvest', ret); @@ -151,6 +171,23 @@ run: function(creep) { creep.memory.dest = dest.id; creep.memory.stuck = 0; } + + // XXX Tried building some roads along the way, but it means we never + // progress on the actual goal and just do lots of trips. + const range = creep.pos.getRangeTo(dest); + if(range > 3 && creep.fatigue > 0) { + const cPos = creep.pos; + const roadCS = cPos.findInRange(FIND_CONSTRUCTION_SITES, 3, {filter: s => s.structureType == STRUCTURE_ROAD}); + roadCS.sort((a, b) => (b.progress == a.progress) ? cPos.getRangeTo(a) - cPos.getRangeTo(b) : b.progress - a.progress); + const cs = roadCS.shift(); + if(cs) { + if(creep.build(cs) == OK) { + util.track(creep, 'build', ret); + trace(creep, `passing build ${dest}`); + creep.memory.delivered += creep.getActiveBodyparts(WORK)*BUILD_POWER; + } + } + } var effort; var ret; @@ -168,6 +205,10 @@ run: function(creep) { util.track(creep, 'transfer', ret); trace(creep, `transfer ${dest} ret: ${ret}`); effort = Math.min(creep.carry.energy, dest.energyCapacity-dest.energy); + if(ret == OK) { + const next = findDest(creep); + if(next) creep.moveTo(next); + } break; case STRUCTURE_CONTROLLER: // TODO(baptr): Consider dropping some energy instead of sitting @@ -256,6 +297,9 @@ function findSrc(creep) { src = creep.pos.findClosestByPath(FIND_RUINS, {filter: s => s.store.energy > 0}); if(src) return done(src); + + src = creep.pos.findClosestByPath(FIND_TOMBSTONES, {filter: t => t.store.energy > 0}); + if(src) return done(src); // XXX only pick up from a container near the controller if you're going to upgrade src = creep.pos.findClosestByPath(FIND_STRUCTURES, {filter: @@ -292,13 +336,14 @@ function findSrc(creep) { if(creep.carry.energy > 0) { creep.memory.filling = false; } + return null; } function findDest(creep) { // Make sure we don't downgrade. var ctrl = creep.room.controller; - if(ctrl && ctrl.my && ctrl.ticksToDowngrade < CONTROLLER_DOWNGRADE[ctrl.level] * 0.5) { + if(ctrl && ctrl.my && ctrl.ticksToDowngrade < CONTROLLER_DOWNGRADE[ctrl.level] * 0.6) { return ctrl; } @@ -316,18 +361,15 @@ function findDest(creep) { } var dest; - // If we're under attack, keep towers supplied. - // TODO(baptr): Load leveling - if(creep.room.find(FIND_HOSTILE_CREEPS).length) { - dest = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {filter: s => { - return s.isActive && s.structureType == STRUCTURE_TOWER && s.energy < 750; + // Keep towers supplied. + dest = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {filter: s => { + return s.isActive() && s.structureType == STRUCTURE_TOWER && s.energy < 950; }}); - if(dest) return dest; - } + if(dest) return dest; // Supply spawning structures. dest = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {filter: s => { - if(!s.isActive) return false; + if(!s.isActive()) return false; switch(s.structureType) { case STRUCTURE_SPAWN: case STRUCTURE_EXTENSION: @@ -338,7 +380,7 @@ function findDest(creep) { if(dest) { return dest; } dest = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {filter: s => { - return s.isActive && s.structureType == STRUCTURE_TOWER && s.energy < 500 + return s.isActive() && s.structureType == STRUCTURE_TOWER && s.energy < 500 }}) if(dest) return dest; @@ -354,6 +396,10 @@ function findDest(creep) { }}) dest = creep.pos.findClosestByPath(sites, {filter: s => s.progress > 0}); if(dest) { return dest; } + for(const t of PRIORITY_BUILD_TYPES) { + dest = creep.pos.findClosestByPath(sites, {filter: s => s.structureType == t}); + if(dest) return dest; + } dest = creep.pos.findClosestByPath(sites); if(dest) { return dest; } @@ -363,5 +409,10 @@ function findDest(creep) { if(ctrl && ctrl.my) { return ctrl; } - return null; -} \ No newline at end of file + + // If we've fallen through all of this, maybe we ended up out of the room? + const [nearBy, path] = pathUtil.macroClosest(creep.pos.roomName, Object.values(Game.spawns)); + console.log(`${creep.name} last ditch spawn search: ${nearBy}`); + if(nearBy == ERR_NO_PATH) return null; + return nearBy; +} diff --git a/role2.bother.js b/role2.bother.js new file mode 100644 index 0000000..eff962f --- /dev/null +++ b/role2.bother.js @@ -0,0 +1,70 @@ +const BodyBuilder = require('util.bodybuilder'); + +const ROLE = 'bother'; +module.exports = { + ROLE, + spawn: function(spawn, targetRoom) { + const body = new BodyBuilder([ATTACK, ATTACK, RANGED_ATTACK, HEAL], spawn.room.energyAvailable); + body.extend([MOVE], limit=11); // enough for 1/2 in swamps, +1. + if(body.count(MOVE) < 7) { // 1/3 in swamps + return ERR_NOT_ENOUGH_ENERGY; + } + spawn.spawnCreep(body.sort(), `bother-${targetRoom}-${Game.time}`, {memory: { + role: ROLE, + targetRoom, + cost: body.cost, + life: {}, + }}); + }, + run: function(creep) { + if(creep.hits < creep.hitsMax) creep.heal(creep); + + if(creep.pos.room.name != creep.memory.targetRoom) { + const enemies = creep.pos.findInRange(FIND_HOSTILE_CREEPS, 3); + if(enemies.length) { + const target = creep.pos.findClosestByPath(enemies); + if(target) { + const range = creep.pos.getRangeTo(target); + if(range == 1) { + creep.attack(target); + creep.rangedMassAttack(); + } else { + if(enemies.length > 1) { + creep.heal(creep); + creep.rangedMassAttack(); + } else { + creep.rangedAttack(target); + } + } + } + } + // TODO else: heal nearby friends + + return creep.moveTo(25, 25, creep.memory.targetRoom); + } + + const enemySites = creep.room.find(FIND_CONSTRUCTION_SITES).filter(cs => !cs.my); + const tower = enemySites.sort((a, b) => b.progress - a.progress).find(s => s.structureType == STRUCTURE_TOWER && s.progress); + if(tower) { + creep.moveTo(tower); + if(creep.pos.isEqualTo(tower.pos)) creep.move(0,0); // Hack to not stay on it if it was placed under us. + creep.rangedMassAttack(); + return; + } + + const room = Game.rooms[creep.memory.targetRoom] || creep.room; + // XXX do value checks to priotize healers and damage sources + let target = Game.getObjectById(creep.memory.target); + if(!target) { + target = creep.pos.findClosestByPath(FIND_HOSTILE_CREEPS); + if(target) creep.memory.target = target.id; + } + // TODO: stomp important CSes + // TODO: towers first, probably? + if(!target) { + target = creep.pos.findClosestByPath(FIND_HOSTILE_STRUCTURES); + if(target) creep.memory.target = target.id; + } + if(target) creep.moveTo(target); + }, +}; diff --git a/role2.builder.js b/role2.builder.js index d80919e..9d63dbf 100644 --- a/role2.builder.js +++ b/role2.builder.js @@ -53,7 +53,7 @@ function findTarget(creep) { switch(s.structureType) { case STRUCTURE_RAMPART: case STRUCTURE_WALL: - return s.hits/(s.hitsMax/1000); + return s.hits/300e3; // Normalize walls and ramparts to keep defenses flat default: return s.hits/s.hitsMax; } @@ -89,9 +89,8 @@ spawnCondition: function(room, numExisting) { // underplay them significantly. // TODO(baptr): Large wall banks are still going to blow this out. case STRUCTURE_RAMPART: - return 50 * (1 - s.hits/s.hitsMax); case STRUCTURE_WALL: - return 10 * (1 - s.hits/s.hitsMax); + return 50 * (1 - s.hits/s.hitsMax); default: return dmg; } @@ -107,16 +106,13 @@ spawnCondition: function(room, numExisting) { }, // mkBody is exported for plan.claim mkBody: function(energyAvailable) { - var builder = new BodyBuilder(MIN_BODY, energyAvailable); + const builder = new BodyBuilder(MIN_BODY, energyAvailable); builder.extend([WORK, MOVE, CARRY, MOVE], limit=3); builder.extend([CARRY, MOVE], limit=2); builder.extend([WORK, MOVE], limit=2); - builder.extend([CARRY, MOVE], limit=5); - builder.extend([WORK, MOVE], limit=5); - - builder.extend([CARRY, MOVE]); + builder.extend([MOVE], limit=5); if(builder.count(WORK) < 2) return null; @@ -201,7 +197,9 @@ run: function(creep) { } if(creep.moveTo(source) == ERR_NO_PATH) { creep.memory.blocked++; - if(creep.memory.blocked > 5) { + // TODO: Find a better way not to stay latched on to a source + // that's blocked by dropHarvesters. + if(creep.memory.blocked > 2) { console.log(`${creep.name} unable to reach ${source}`); delete creep.memory.source; } diff --git a/role2.combatant.js b/role2.combatant.js index 8d6806b..31e1044 100644 --- a/role2.combatant.js +++ b/role2.combatant.js @@ -96,9 +96,9 @@ spawn: function(spawn, gather, target) { const bodySort = {}; bodySort[TOUGH] = 1; - bodySort[RANGED_ATTACK] = 2; - bodySort[MOVE] = 3; - bodySort[ATTACK] = 4; + bodySort[MOVE] = 2; + bodySort[ATTACK] = 3; + bodySort[RANGED_ATTACK] = 4; body.sort((a, b) => bodySort[a] - bodySort[b]); spawn.spawnCreep(body, `${ROLE}-${spawn.name}-${Game.time}`, {memory: { @@ -109,4 +109,4 @@ spawn: function(spawn, gather, target) { }}); }, run: run, -}; \ No newline at end of file +}; diff --git a/role2.delivery.js b/role2.delivery.js index 27742f8..893f694 100644 --- a/role2.delivery.js +++ b/role2.delivery.js @@ -56,7 +56,6 @@ function fill(creep) { avail = src.amount; ret = creep.pickup(src); } else { - console.log(src, src.store); avail = src.store.energy; ret = creep.withdraw(src, RESOURCE_ENERGY); } @@ -69,47 +68,43 @@ function fill(creep) { } function deliver(creep) { - var dst = Game.getObjectById(creep.memory.dest); - if(dst && dst.carryCapacity - _.sum(dst.carry) < 20) dst = null; + let dst = Game.getObjectById(creep.memory.dest); + if(dst && dst.store.getFreeCapacity() < 50) dst = null; + if(!dst) { + dst = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {filter: s => { + if(s.structureType == STRUCTURE_EXTENSION || s.structureType == STRUCTURE_SPAWN) { + return s.energy < s.energyCapacity; + } + return false; + }}) if(!dst) { - dst = creep.pos.findClosestByPath(FIND_MY_CREEPS, {filter: c => { - if(c.id == creep.id) return false; - const total = _.sum(c.carry); - // TODO(baptr): Figure out how to identify creeps that need energy. - if(c.carryCapacity - total > 20) { - return true; - } - return false; - }}); - if(!dst) { - dst = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {filter: s => { - if(s.structureType != STRUCTURE_EXTENSION || s.structureType != STRUCTURE_SPAWN) { - return false; - } - return s.energy < s.energyCapacity; - }}) - } - if(!dst) { - if(creep.carry.energy < creep.carryCapacity) { - creep.memory.filling = true; - } - return ERR_NOT_FOUND; - } - creep.memory.dest = dst.id; + dst = creep.pos.findClosestByPath(FIND_MY_CREEPS, {filter: c => { + if(c.id == creep.id) return false; + // TODO(baptr): Figure out how to identify creeps that need energy. + return c.store.getFreeCapacity() > 20; + }}); } - const dist = creep.pos.getRangeTo(dst); - if(dist >= 1) { - creep.moveTo(dst); + if(!dst) { + if(creep.carry.energy < creep.carryCapacity) { + creep.memory.filling = true; + } + return ERR_NOT_FOUND; } - if(dist <= 1) { - var dstSpace = 0; - if(dst instanceof Creep) { - dstSpace = dst.carryCapacity - _.sum(dst.carry); - } else { - dstSpace = dst.storeCapacity - _.sum(dst.store); - } - if(creep.transfer(dst, RESOURCE_ENERGY) == OK) { - creep.memory.delivered += Math.min(dstSpace, creep.carry.energy); - } + creep.memory.dest = dst.id; + } + const dist = creep.pos.getRangeTo(dst); + if(dist >= 1) { + creep.moveTo(dst); + } + if(dist <= 1) { + var dstSpace = 0; + if(dst instanceof Creep) { + dstSpace = dst.carryCapacity - _.sum(dst.carry); + } else { + dstSpace = dst.storeCapacity - _.sum(dst.store); } -} \ No newline at end of file + if(creep.transfer(dst, RESOURCE_ENERGY) == OK) { + creep.memory.delivered += Math.min(dstSpace, creep.carry.energy); + } + } +} diff --git a/role2.dropHarvester.js b/role2.dropHarvester.js index 59ab4af..3afa4e9 100644 --- a/role2.dropHarvester.js +++ b/role2.dropHarvester.js @@ -13,21 +13,38 @@ const BodyBuilder = require('util.bodybuilder'); // remember 5 limit) const ROLE = 'dropHarvester'; module.exports = { +assigned: function(targetRoom) { + return Object.values(Game.creeps).filter(c => c.memory.role == ROLE && ( + c.memory.remoteRoom == targetRoom || c.pos.roomName == targetRoom)); +}, spawn: function(spawn) { - var room = spawn.room; + const room = spawn.room; const availEnergy = room.energyAvailable; if(availEnergy < MIN_HARVESTER_COST || spawn.spawning) { return ERR_BUSY; } + // TODO: Scale for reserved/unreserved, seasonal single/double rooms. var builder = new BodyBuilder([WORK, WORK, MOVE], availEnergy); builder.extend([WORK, WORK, MOVE], limit=(HARVESTER_WORKS - builder.count(WORK))/2); builder.extend([CARRY, MOVE], limit=1); - builder.sort(); - // TODO(baptr): Base on available locatiosn near the spawn. - if(builder.count(WORK) < 4) return ERR_NOT_ENOUGH_RESOURCES; + // TODO(baptr): Base on available locations near the spawn, and total room tier. + // Small is fine early on, but we'd want to save up later + if(builder.count(WORK) < 3) return ERR_NOT_ENOUGH_RESOURCES; - const targetSource = pickSource(room); + let targetSource; + if(!module.exports.assigned(room.name).length) { + targetSource = spawn.pos.findClosestByPath(FIND_SOURCES); + } else { + targetSource = pickSource(room); + } if(!targetSource) { return ERR_NOT_FOUND; } + + const workDesired = targetSource.energyCapacity / ENERGY_REGEN_TIME / HARVEST_POWER; + if(builder.count(WORK) < workDesired && builder.energyRemaining > BODYPART_COST[WORK]) { + builder.extend([WORK, WORK, MOVE], limit=(workDesired-builder.count(WORK))/2) + } + builder.extend([WORK], limit=1); + builder.sort(); const name = `${ROLE}-${room.name}-${Game.time}` var ret = spawn.spawnCreep(builder.body, name, {memory: { @@ -46,32 +63,33 @@ spawnRemote: function(spawn, srcRoom, srcID=null) { console.log('Need srcRoom and srcID if possible'); return ERR_INVALID_ARGS; } + // TODO: Would be nice to start with a source near the exit, but it's a bit awkward. if(srcRoom && !srcID) { - var room = Game.rooms[srcRoom]; + const room = Game.rooms[srcRoom]; if(!room) { - console.log('Need srcID if srcRoom is not visible'); - return ERR_INVALID_ARGS; - } - var src = pickSource(room); - if(!src) { - console.log(`Unable to find source in ${srcRoom}`); - return ERR_NOT_FOUND; + console.log('No srcID for remote dropHarvester, will try to figure it out when I get there...'); + } else { + const src = pickSource(room); + if(!src) { + console.log(`Unable to find source in ${srcRoom}`); + return ERR_NOT_FOUND; + } + srcID = src.id; } - srcID = src.id; } - var builder = new BodyBuilder([WORK, WORK, MOVE], spawn.room.energyAvailable); - builder.extend([CARRY, CARRY, MOVE], limit=1); - builder.extend([WORK, WORK, MOVE]); - if(builder.count(WORK) < 6) { // randomly chosen. TODO: math - console.log(`Not worth remote dropHarvesting for ${builder.count(WORK)} WORK at ${builder.cost} energy`); - return ERR_NOT_ENOUGH_ENERGY; + const body = new BodyBuilder([WORK, CARRY, MOVE], spawn.room.energyAvailable); + body.extend([WORK, WORK, MOVE], limit=5); + body.extend([WORK, MOVE], limit=1); + if(body.count(WORK) < 4) { // randomly chosen. TODO: math + return ERR_NOT_ENOUGH_ENERGY; } const name = `${ROLE}-${srcRoom}-${Game.time}`; - var ret = spawn.spawnCreep(builder.sort(), name, {memory: { + var ret = spawn.spawnCreep(body.sort([MOVE, CARRY, WORK]), name, {memory: { role: ROLE, src: srcID, - cost: builder.cost, + cost: body.cost, remoteRoom: srcRoom, + life: {}, }}) if(ret != OK) { console.log(`Failed to spawn ${name}: ${ret}`); @@ -80,9 +98,9 @@ spawnRemote: function(spawn, srcRoom, srcID=null) { }, run: function(creep) { util.track(creep, 'alive'); - var src = Game.getObjectById(creep.memory.src); + let src = Game.getObjectById(creep.memory.src); if(!src) { - if(creep.memory.remoteRoom) { + if(creep.memory.remoteRoom && creep.room.name != creep.memory.remoteRoom) { return creep.moveTo(new RoomPosition(25, 25, creep.memory.remoteRoom)); } src = pickSource(creep.room); @@ -96,13 +114,31 @@ run: function(creep) { if(!creep.pos.isNearTo(src)) { // XXX need to prevent repositioning for a different container from // flapping against this. - var ret = creep.moveTo(src); + // TODO: Bump blockers + const ret = creep.moveTo(src); util.track(creep, 'move', ret); return ret; } // In range... const cont = planContainer(creep, src); + + if(cont && cont.hits < cont.hitsMax/4 && creep.store.energy) creep.repair(cont); + + // Try to turtle if we're under attack + if(creep.hits < creep.hitsMax && creep.room.controller.my && creep.store.energy) { + const rampart = creep.pos.lookFor(LOOK_STRUCTURES).find(s => s.structureType == STRUCTURE_RAMPART); + if(rampart) { + creep.repair(rampart); + } else { + const cs = creep.pos.lookFor(LOOK_CONSTRUCTION_SITES).find(s => s.structureType == STRUCTURE_RAMPART); + if(cs) { + creep.build(cs); + } else { + creep.pos.createConstructionSite(STRUCTURE_RAMPART); + } + } + } var ret = creep.harvest(src); util.track(creep, 'harvest', ret); @@ -141,27 +177,26 @@ run: function(creep) { } break; case ERR_NOT_ENOUGH_RESOURCES: - if(creep.carryCapacity && cont) { - if(cont instanceof ConstructionSite) { - creep.build(cont); - } else { - if(cont.hits < cont.hitsMax) { - creep.repair(cont); - } else { - const structs = _.filter(creep.pos.lookFor(LOOK_STRUCTURES), { - structureType: STRUCTURE_RAMPART - }); - if(structs.length) { - creep.repair(structs[0]); - } - } - } - const res = creep.pos.lookFor(LOOK_RESOURCES, {filter: {resourceType: RESOURCE_ENERGY}}); - if(res.length) creep.pickup(res[0]); + if(creep.carryCapacity) { + const sites = creep.pos.lookFor(LOOK_CONSTRUCTION_SITES).sort((a, b) => a.progress - b.progress); + if(sites.length) { + creep.build(sites[0]); + } else { + const rep = creep.pos.lookFor(LOOK_STRUCTURES).find(s => s.hits < s.hitsMax); + if(rep) { + creep.repair(rep); + } + // TODO: build static ramparts, or turtle on demand...? :-/ } - break; + const res = creep.pos.lookFor(LOOK_RESOURCES, {filter: {resourceType: RESOURCE_ENERGY}}); + if(res.length) creep.pickup(res[0]); + } + break; + case ERR_NOT_OWNER: + if(Game.time % 20 == 0) console.log(`${creep.name} unable to harvest in ${creep.pos.roomName}: NOT OWNER`); + break; default: - console.log(`${creep.name} unhandled harvest ret: ${ret}`); + console.log(`${creep.name} unhandled harvest ret: ${ret}`); } }, ROLE, @@ -171,6 +206,7 @@ pickSource, // XXX this feels expensive... // TODO(baptr): memoize the time to pay attention again? function pickSource(room) { + // TODO: Check memory instead of in-room creeps for remote harvesters. var harvesters = room.find(FIND_MY_CREEPS, {filter: c => c.memory.role == ROLE}); var workParts = {}; @@ -190,18 +226,19 @@ function pickSource(room) { const assigned = function(id) { return workParts[id] || 0 }; const existing = function(id) { return numHarvesters[id] || 0 }; - var sources = room.find(FIND_SOURCES); + const sources = room.find(FIND_SOURCES); sources.sort((a,b) => assigned(a.id)-assigned(b.id)); - for(var i = 0; i < sources.length; i++) { - var s = sources[i]; - if(assigned(s.id) >= HARVESTER_WORKS) { - continue; - } - if(existing(s.id) >= pathing.spacesNear(s.pos).length) { - continue; - } - return s; + for(const s of sources) { + const target = s.energyCapacity / ENERGY_REGEN_TIME / HARVEST_POWER; + if(assigned(s.id) >= target) { + continue; + } + // TODO: Unless they're lower part density than we'd spawn now... + if(existing(s.id) >= pathing.spacesNear(s.pos).length) { + continue; + } + return s; } return null; } @@ -213,10 +250,12 @@ function planContainer(creep, src) { // Wait until we're there to figure it out. return null; } + /* // Only think about containers in a controlled (owned or reserved) room. let ctrl = creep.room.controller; // TODO(baptr): Consider latching 'no' if appropriate. if(!ctrl || !ctrl.reservation || !ctrl.reservation.username == 'baptr') return null; + */ } if(!cont) { var conts = src.pos.findInRange(FIND_STRUCTURES, 1, { diff --git a/role2.hauler.js b/role2.hauler.js index 10ab239..b23a54f 100644 --- a/role2.hauler.js +++ b/role2.hauler.js @@ -1,16 +1,14 @@ const BodyBuilder = require('util.bodybuilder'); - -function findCtrlStore(room) { - const opts = room.controller.pos.findInRange(FIND_STRUCTURES, 4, { - filter: s => {return s.store && s.store.getFreeCapacity(RESOURCE_ENERGY) > 0} - }); - if(!opts.length) return null; - const pref = _.sortBy(opts, [s => s.store.energy]) - return pref[0]; -} +const resutil = require('util.resources'); +const util = require('util.creep'); +const pathUtil = require('util.pathing'); +const recycle = require('role2.recycle'); const ROLE = 'hauler'; module.exports = { +assigned: function(srcRoom) { + return Object.values(Game.creeps).filter(c => c.memory.role == ROLE && c.memory.remoteRoom == srcRoom); +}, spawnCondition: function(room, existing=0) { const loose = _.sum(_.map(room.find(FIND_DROPPED_RESOURCES, { filter: {resourceType: RESOURCE_ENERGY} @@ -21,18 +19,21 @@ spawnCondition: function(room, existing=0) { }, spawn: function(spawn, dest=false) { const room = spawn.room; - if(!dest) dest = findCtrlStore(room); + // TODO: Should also carry to Spawn/extensions. + if(!dest) dest = resutil.roomCtrlStores(room).sort( + (a, b) => a.store[RESOURCE_ENERGY] - b.store[RESOURCE_ENERGY]).find( + s => s.store.getFreeCapacity(RESOURCE_ENERGY) > 0); if(!dest) dest = room.storage; if(!dest) return false; var body = new BodyBuilder([], room.energyAvailable); - body.extend([CARRY, MOVE]); + body.extend([CARRY, MOVE], limit=10); // Not worth it. Save up - if(body.count(CARRY) < 5) return false; + if(body.count(CARRY) < 3) return false; // TODO(baptr): Allow some specialization? - var mem = {role: ROLE, cost: body.cost, dest: dest.id}; + var mem = {role: ROLE, cost: body.cost, dest: dest.id, life: {}}; const name = `${ROLE}-${room.name}-${Game.time}`; const ret = spawn.spawnCreep(body.body, name, {memory: mem}); if(ret != OK) { @@ -41,21 +42,21 @@ spawn: function(spawn, dest=false) { return ret; }, spawnRemote: function(spawn, remoteRoom, destRoom=null) { - var storage = spawn.room.storage; - if(destRoom) { - storage = Game.rooms[destRoom].storage; - } + const room = destRoom ? Game.rooms[destRoom] : spawn.room; + const storage = resutil.roomCtrlStores(room).find(s => s.store.getFreeCapacity(RESOURCE_ENERGY)); if(!storage) return ERR_RCL_NOT_ENOUGH; + var body = new BodyBuilder([], spawn.room.energyAvailable); - body.extend([CARRY, MOVE]); + body.extend([CARRY, MOVE], limit=15); - if(body.count(CARRY) < 10) return ERR_NOT_ENOUGH_ENERGY; + if(body.count(CARRY) < 5) return ERR_NOT_ENOUGH_ENERGY; var mem = { role: ROLE, remoteRoom, dest: storage.id, cost: body.cost, + life: {}, } const name = `${ROLE}-${storage.room.name}-${remoteRoom}-${Game.time}`; const ret = spawn.spawnCreep(body.body, name, {memory: mem}); @@ -67,8 +68,11 @@ spawnRemote: function(spawn, remoteRoom, destRoom=null) { // TODO(baptr): In controlled rooms, leave some dropHarvested energy near the // source for other types. run: function(creep) { + util.track(creep, 'alive'); if(!_.sum(creep.carry)) creep.memory.filling = true; + if(creep.memory.exitPath) return pathUtil.macroMove(creep, {swampCost: 4}); + if(creep.memory.filling) { var src = findSrc(creep); // TODO(baptr): If there's already enough energy onboard, or lifetime is @@ -76,15 +80,24 @@ run: function(creep) { if(!src) { // If there's no where else to draw from, take it home. if(creep.carry.energy) creep.memory.filling = false; + if(creep.memory.remoteRoom && creep.room.name == creep.memory.remoteRoom) creep.moveTo(15, 15); return false; } + if(creep.pos.roomName != src.pos.roomName) { + const ret = pathUtil.setMem(creep.memory, creep.pos, src.pos, {cache: false, swampCost: 4, ttl: creep.ticksToLive/2}); + if(ret == ERR_NO_PATH || ret == ERR_NOT_IN_RANGE) { + console.log(`Would take too long for ${creep.name} to round trip ${creep.pos} to ${src.pos} (ttl=${creep.ticksToLive}, recycling`); + return recycle.convert(creep); + } + return pathUtil.macroMove(creep, {swampCost: 4}); + } if(!creep.pos.inRangeTo(src, 1)) { - creep.moveTo(src); - return + return util.track(creep, 'move', creep.moveTo(src)) } let ret; if(src instanceof Resource) { - ret = creep.pickup(src); + ret = util.track(creep, 'pickup', creep.pickup(src)); + creep.move(BOTTOM_RIGHT); } else { ret = ERR_NOT_ENOUGH_RESOURCES; _.forEach(src.store, (v, t) => { @@ -93,6 +106,7 @@ run: function(creep) { return false; } }) + util.track(creep, 'withdraw', ret); } switch(ret) { case OK: @@ -119,9 +133,12 @@ run: function(creep) { var dest = findDest(creep); if(!dest) return false; + if(creep.pos.roomName != dest.pos.roomName) { + pathUtil.setMem(creep.memory, creep.pos, dest.pos, {cache: false, swampCost: 4}); + return pathUtil.macroMove(creep, {swampCost: 4}); + } if(!creep.pos.inRangeTo(dest.pos, 1)) { - creep.moveTo(dest); - return + return util.track(creep, 'haul', creep.moveTo(dest)); } let ret = ERR_NOT_ENOUGH_RESOURCES; var resType; @@ -132,15 +149,16 @@ run: function(creep) { return false; } }) + util.track(creep, 'transfer', ret); switch(ret) { case OK: // Hard to be accurate, so let's just be quick. - creep.memory.delivered += creep.carry[resType]; + creep.memory.delivered += creep.store[resType]; // XXX set filling? if we have anything left, the delivery was a lie, // and we'll just hit FULL next time anyway... probably? break; case ERR_FULL: - // XXX wait? look for nearby creeps with capacity? + // XXX wait? look for nearby creeps with capacity? drop it on the ground? if(creep.carry[resType] < creep.carryCapacity * 0.10) { creep.memory.filling = true; return; @@ -182,16 +200,19 @@ function findSrc(creep) { return src; } } - if(creep.memory.remoteRoom) { - const remoteRoom = creep.memory.remoteRoom; - if(creep.pos.roomName != remoteRoom) { - // TODO check visibilty and try picking a source anyway. - creep.moveTo(new RoomPosition(25, 25, remoteRoom)); - return null; + let room = creep.room; + if(creep.memory.remoteRoom && creep.pos.roomName != creep.memory.remoteRoom) { + const remoteRoom = Game.rooms[creep.memory.remoteRoom]; + if(remoteRoom) { + room = remoteRoom; + } else { + creep.moveTo(new RoomPosition(25, 25, creep.memory.remoteRoom)); + return null; } } - var ground = creep.room.find(FIND_DROPPED_RESOURCES); - ground.push(...creep.room.find(FIND_TOMBSTONES, {filter: t => _.sum(t.store) > 0})); + var ground = room.find(FIND_DROPPED_RESOURCES); + ground.push(...room.find(FIND_TOMBSTONES, {filter: t => _.sum(t.store) > 0})); + // XXX does findClosest do something reasonable/cheap when out of room? var res = creep.pos.findClosestByPath(ground); if(res) { creep.memory.src = res.id; @@ -217,21 +238,24 @@ function findSrc(creep) { return src; } -function findControllerContainer(room) { - const conts = room.controller.pos.findInRange(FIND_STRUCTURES, 4, { - filter: {structureType: STRUCTURE_CONTAINER} - }); - if(conts.length) { - return conts[0]; - } - return null; -} function findDest(creep) { var dest = Game.getObjectById(creep.memory.dest); if(dest) { + // Hack to not wait around slowly draining to a basically full but in use container. + if(dest.store.getFreeCapacity() >= creep.store.getUsedCapacity()) { // TODO(baptr): Check space? or do we want to travel anyway assuming // there will be space when we get there? return dest; + } + if(dest.room.storage && dest.room.storage.my) return dest.room.storage; + if(creep.store.energy) { + for(const spawn of dest.room.find(FIND_MY_SPAWNS)) { + const cont = spawn.pos.findClosestByRange(FIND_STRUCTURES, {filter: s => s.store && s.store.getFreeCapacity(RESOURCE_ENERGY)}); + if(cont) { + return cont; + } + } + } } // XXX this won't work for remote haulers! dest = creep.room.storage; diff --git a/role2.keeperKiller.js b/role2.keeperKiller.js index a61e6e6..fb15f06 100644 --- a/role2.keeperKiller.js +++ b/role2.keeperKiller.js @@ -1,42 +1,63 @@ const BodyBuilder = require('util.bodybuilder'); +const util = require('util.creep'); const ROLE = 'keeperKiller'; module.exports = { ROLE, +assigned: function(roomName) { + return Object.values(Game.creeps).filter(c => c.memory.role == ROLE && c.memory.destRoom == roomName); +}, spawn: function(spawn, destRoom) { builder = new BodyBuilder([], spawn.room.energyAvailable); builder.extend([ATTACK, MOVE], limit=10) builder.extend([HEAL, MOVE], limit=5) - builder.extend([TOUGH, MOVE]) + builder.extend([MOVE], limit=15) builder.sort() if(builder.count(ATTACK) < 5) return ERR_NOT_ENOUGH_ENERGY; - if(builder.count(HEAL) < 2) return ERR_NOT_ENOUGH_ENERGY; + // Not as useful for l0 rooms.. + // if(builder.count(HEAL) < 2) return ERR_NOT_ENOUGH_ENERGY; const name = `${ROLE}-${destRoom}-${Game.time}`; const mem = { role: ROLE, destRoom: destRoom, cost: builder.cost, + life: {}, } return spawn.spawnCreep(builder.body, name, {memory: mem}); }, run: function(creep) { - if(creep.hits < creep.hitsMax) creep.heal(creep); - var target = Game.getObjectById(creep.memory.target); + util.track(creep, 'alive'); + if(creep.hits < creep.hitsMax) { + creep.heal(creep); + } else { + const friend = creep.pos.findInRange(FIND_MY_CREEPS, 1, {filter: c => c.hits < c.hitsMax}).shift(); + if(friend) creep.heal(friend); + } + + if(creep.room.find(FIND_FLAGS).find(f => f.name == 'wait')) return; + + let target = Game.getObjectById(creep.memory.target); if(!target) { - if(creep.pos.roomName != creep.memory.destRoom) { - return creep.moveTo(new RoomPosition(25, 25, creep.memory.destRoom)); - } - // XXX Path necessary? - target = creep.pos.findClosestByRange(FIND_HOSTILE_CREEPS); - if(!target) { - return creep.moveTo(new RoomPosition(25, 25, creep.memory.destRoom)); - } - creep.memory.target = target.id; + if(creep.pos.roomName != creep.memory.destRoom) { + return util.track(creep, 'move', creep.moveTo(new RoomPosition(25, 25, creep.memory.destRoom))); + } + target = creep.pos.findClosestByPath(FIND_HOSTILE_CREEPS); + if(!target) { + target = creep.pos.findClosestByPath(FIND_HOSTILE_STRUCTURES); + } + if(!target) { + return util.track(creep, 'idle move', creep.moveTo(new RoomPosition(25, 25, creep.memory.destRoom))); + } + creep.memory.target = target.id; + } + if(!creep.pos.isNearTo(target)) { + util.track(creep, 'advance', creep.moveTo(target)); } - creep.moveTo(target); if(creep.pos.inRangeTo(target, 2)) { - creep.attack(target); + const ret = util.track(creep, 'attack', creep.attack(target)); + if(ret == OK) creep.memory.delivered += creep.getActiveBodyparts(ATTACK)*ATTACK_POWER; + return ret; } } -}; \ No newline at end of file +}; diff --git a/role2.powerLoader.js b/role2.powerLoader.js new file mode 100644 index 0000000..fa4711a --- /dev/null +++ b/role2.powerLoader.js @@ -0,0 +1,55 @@ +const BodyBuilder = require('util.bodybuilder'); +const resUtil = require('util.resources'); + +const ROLE = 'powerLoader'; +module.exports = { + ROLE, + spawnCondition: function(room) { + if(!room.storage || !room.storage.store.power) return false; + if(room.find(FIND_MY_CREEPS, {filter: c => c.memory.role == ROLE}).length) return false; + return true; + }, + spawn: function(spawn) { + // TODO: could get away with very little move since we *should* be on roads. + const body = new BodyBuilder([MOVE, CARRY], spawn.room.energyAvailable) + body.extend([MOVE, CARRY], limit=2); + return spawn.spawnCreep(body.sort(), `powerLoader-${spawn.room.name}-${Game.time}`, {memory: { + role: ROLE, + cost: body.cost, + }}); + }, + run: function(creep) { + const bank = creep.room.find(FIND_MY_STRUCTURES, {filter: {structureType: STRUCTURE_POWER_SPAWN}}).shift(); + if(!bank) return; + bank.processPower(); + + const store = creep.room.storage; + if(!store) return; + // XXX If the creep is holding 48/50 energy, this can get blocked + // transfering 2 power at a time :-( + if(bank.store.power < 50 && (creep.store.power || creep.store.getFreeCapacity())) { // XXX handle running out of power + if(creep.store.power == 0) { + creep.moveTo(store); + const want = Math.min(creep.store.getFreeCapacity(), 100-bank.store.power, store.store.power); + creep.withdraw(store, RESOURCE_POWER, want); + } else { + creep.moveTo(bank); + if(creep.transfer(bank, RESOURCE_POWER) == OK) { + creep.memory.delivered += creep.store.power; + } + } + } else { + if(creep.store.energy) { + creep.moveTo(bank); + if(creep.transfer(bank, RESOURCE_ENERGY) == OK) { + creep.memory.delivered += creep.store.energy; + } + } else { + const src = resUtil.findSrc(creep); + if(!src) return; + creep.moveTo(src); + creep.withdraw(src, RESOURCE_ENERGY); + } + } + }, +}; diff --git a/role2.recycle.js b/role2.recycle.js index 417c47b..0dc62e5 100644 --- a/role2.recycle.js +++ b/role2.recycle.js @@ -1,26 +1,33 @@ +const pathUtil = require('util.pathing'); + const ROLE = 'recycle'; module.exports = { ROLE, -convert: function(creep) { - var spawn = creep.pos.findClosestByPath(FIND_MY_SPAWNS); - if(!spawn) { - console.log(`No where for ${creep.name} to recycle`); - return false; - } - creep.memory.role = ROLE; - creep.memory.spawn = spawn.id; - return true; +convert: function(creep, spawn=null) { + // TODO: Check other rooms. + if(!spawn) spawn = creep.pos.findClosestByPath(FIND_MY_SPAWNS); + if(!spawn) { + console.log(`No where for ${creep.name} to recycle`); + return false; + } + if(creep.pos.roomName != spawn.pos.roomName) { + pathUtil.setMem(creep.memory, creep.pos, spawn.pos, {cache: false}); + } + creep.memory.role = ROLE; + creep.memory.spawn = spawn.id; + return true; }, run: function(creep) { - var spawn = Game.getObjectById(creep.memory.spawn); - if(!spawn) { - console.log(`${creep.name} has no recycle dest, giving up`); - creep.suicide(); - } - if(!creep.pos.isNearTo(spawn)) { - creep.moveTo(spawn) - } else { - spawn.recycleCreep(creep); - } + if(creep.memory.exitPath) return pathUtil.macroMove(creep); + const spawn = Game.getObjectById(creep.memory.spawn); + if(!spawn) { + console.log(`${creep.name} has no recycle dest, giving up`); + creep.say("lost"); + } + if(!creep.pos.isNearTo(spawn)) { + creep.moveTo(spawn) + } else { + spawn.recycleCreep(creep); + } } -}; \ No newline at end of file +}; diff --git a/role2.reserver.js b/role2.reserver.js index 213c957..0f1efa6 100644 --- a/role2.reserver.js +++ b/role2.reserver.js @@ -14,9 +14,13 @@ const ROLE = 'reserver'; module.exports = { ROLE, +assigned: function(targetRoom) { + return Object.values(Game.creeps).filter(c => c.memory.role == ROLE && c.memory.targetRoom == targetRoom); +}, spawn: function(spawn, targetRoom) { var body = new BodyBuilder([CLAIM, MOVE], spawn.room.energyAvailable); body.extend([CLAIM, MOVE]); + body.extend([MOVE], limit=body.count(CLAIM)); if(body.count(CLAIM) < 1) { // TODO originally wanted 2 console.log(`${spawn} can't afford ${ROLE}: ${body.count(CLAIM)} CLAIM costs ${body.cost}, have ${spawn.room.energyAvailable}`); @@ -42,12 +46,18 @@ run: function(creep) { case 2: creep.moveTo(ctrl); case 1: + if(ctrl.reservation && ctrl.reservation.username != 'baptr') { + if(creep.attackController(ctrl) == OK) { + creep.memory.delivered += creep.getActiveBodyparts(CLAIM); + } + } else { if(creep.reserveController(ctrl) == OK) { creep.memory.delivered += creep.getActiveBodyparts(CLAIM); } - break; + } + break; default: return creep.moveTo(ctrl); } }, -}; \ No newline at end of file +}; diff --git a/role2.roadWorker.js b/role2.roadWorker.js new file mode 100644 index 0000000..6d3771c --- /dev/null +++ b/role2.roadWorker.js @@ -0,0 +1,188 @@ +const BodyBuilder = require('util.bodybuilder'); +const creepUtil = require('util.creep'); +const roadUtil = require('util.road'); + +const ROLE = 'roadWorker'; + +function resourcePos(src) { + const loose = src.findInRange(FIND_DROPPED_RESOURCES, 2, {filter: r => r.resourceType == RESOURCE_ENERGY}); + const conts = src.findInRange(FIND_STRUCTURES, 2, {filter: s => s.structureType == STRUCTURE_CONTAINER && s.store.energy > 0}); + + const out = {}; + for(const r of loose) { + out[r.pos] = {pos: r.pos, amount: r.amount, loose: r}; + } + for(const c of conts) { + if(!out[c.pos]) out[c.pos] = {pos: c.pos, amount: 0}; + out[c.pos].amount += c.store.energy; + out[c.pos].cont = c; + } + return Object.values(out).sort((a, b) => b.amount - a.mount).shift(); +} + +function startBuilding(creep) { + creep.memory.building = true; + const cs = creep.pos.findClosestByPath(FIND_CONSTRUCTION_SITES); // maybe filter for roads only, but meh + if(cs) { + creep.memory.build = cs.id; + if(creep.pos.getRangeTo(cs.pos) <= 3) { + const ret = creep.build(cs); + if(ret == OK) { + creep.memory.delivered += creep.getActiveBodyparts(WORK)*BUILD_POWER; + } + return ret; + } else { + return roadUtil.move(creep, cs.pos); + } + } else if(!creep.memory.dump) { + // Start heading home, hopefully there's something to build in another room + // along the way. + const spawn = Game.getObjectById(creep.memory.spawn); + if(!creep.pos.isNearTo(spawn)) { + return roadUtil.move(creep, spawn); + } + + if(creep.store.energy > 0) { + // Try to leave any energy that made it. + if(creep.transfer(spawn, RESOURCE_ENERGY) != OK) { + // If the spawn is full, try to dump in storage. + const store = creep.room.storage; + if(store) { + creep.memory.dump = true; + return creep.moveTo(store); + } + } else { + creep.memory.delivered += creep.store.energy; + } + } + + // Head back out. + creep.memory.building = false; + } else { + const store = creep.room.storage; + if(store && !creep.pos.isNearTo(store)) { + return creep.moveTo(store); + } else { + // If this doesn't work and there's some energy left, maybe we'll repair + // on the way. + if(creep.transfer(store, RESOURCE_ENERGY) == OK) { + creep.memory.delivered += creep.store.energy; + } + delete creep.memory.dump; + creep.memory.building = false; + } + } +} + +module.exports = { +assigned: function(srcPos) { + // XXX this missed ones currently spawning. Could check Memory? + return Object.values(Game.creeps).filter(c => { + if(c.memory.role != ROLE) return false; + const target = c.memory.srcPos; + if(target.roomName != srcPos.roomName) return false; + return srcPos.isEqualTo(target.x, target.y); + }); +}, +spawn: function(spawn, srcPos) { + const body = new BodyBuilder([], spawn.room.energyAvailable); + body.extend([MOVE, MOVE, WORK, CARRY]); + + if(body.count(WORK) < 2) return ERR_NOT_ENOUGH_ENERGY; + + const name = `${ROLE}-${spawn.room.name}-${Game.time}`; + spawn.spawnCreep(body.body, name, {memory: { + role: ROLE, + cost: body.cost, + spawn: spawn.id, + srcPos, + }}); +}, +run: function(creep) { + if(!creep.memory.building && creep.store.getFreeCapacity() == 0) { + creep.memory.building = true; + } else if(creep.memory.building && creep.store.energy == 0) { + creep.memory.building = false; + } + + if(creep.memory.building) { // build + // TODO: Maybe try to avoid travelling far with little energy when you + // could go a different way to get more first. + + let site = Game.getObjectById(creep.memory.build); + if(!site) { + startBuilding(creep); + } else { + if(creep.pos.getRangeTo(site.pos) > 3) { + roadUtil.move(creep, site.pos); + } else { + // Return here to avoid also trying to repair this tick. + const ret = creep.build(site); + if(ret == OK) { + creep.memory.delivered += creep.getActiveBodyparts(WORK)*BUILD_POWER; + } + return ret; + } + } + + // Try some repairs on the way. + const repairPower = creepUtil.bodyPower(creep, WORK, REPAIR_POWER); + const rep = creep.pos.findInRange(FIND_STRUCTURES, 3, { + filter: s => s.structureType == STRUCTURE_ROAD && s.hits + repairPower <= s.hitsMax}).shift(); + if(rep) { + if(creep.repair(rep) == OK) creep.memory.delivered += repairPower; + } + return OK; + } else { // fill + const rawPos = creep.memory.srcPos; // only ever the actual Source + if(!rawPos) { + console.log(`${creep.name} lost src??? mem dump: ${JSON.stringify(creep.memory)}`); + return; + } + const srcPos = new RoomPosition(rawPos.x, rawPos.y, rawPos.roomName); + + if(creep.pos.roomName != srcPos.roomName) { + const loose = creep.pos.findClosestByPath(FIND_DROPPED_RESOURCES, {filter: r => r.resourceType == RESOURCE_ENERGY}); + if(loose) { + if(creep.pos.isNearTo(loose)) { + creep.pickup(loose); + } else { + creep.moveTo(loose); + } + } + return roadUtil.move(creep, srcPos); + } + const srcRange = creep.pos.getRangeTo(srcPos); + if(srcRange > 2) { + return roadUtil.move(creep, srcPos); + } + + let space = creep.store.getFreeCapacity(); + const resLoc = resourcePos(srcPos); + if(!resLoc) { // Try to harvest if there's room. + if(srcRange > 1) return roadUtil.move(creep, srcPos); + const src = creep.room.lookForAt(LOOK_SOURCES, srcPos).shift(); + return creep.harvest(src); + } + if(creep.pos.getRangeTo(resLoc.pos) > 1) { + return roadUtil.move(creep, resLoc.pos); + } + if(resLoc.loose) { + if(creep.pickup(resLoc.loose) == OK) { + space -= Math.min(resLoc.loose.amount); + } + } + if(space > 0 && resLoc.cont) { + const draw = Math.min(resLoc.cont.store.energy, space); + // XXX does this error if you withdraw more than is available? + if(creep.withdraw(resLoc.cont, RESOURCE_ENERGY, draw) == OK) { + space -= draw; + } + } + if(space == 0) { + return startBuilding(creep); + } + return OK; + } +} +}; diff --git a/role2.scout.js b/role2.scout.js index b80f218..e4c23ce 100644 --- a/role2.scout.js +++ b/role2.scout.js @@ -1,57 +1,52 @@ const util = require('util.creep'); +const pathUtil = require('util.pathing'); +const scoutUtil = require('util.scout'); const ROLE = 'scout'; -const BODY = [TOUGH, MOVE, TOUGH, MOVE]; +const BODY = [MOVE, MOVE]; const BODY_COST = util.bodyCost(BODY); module.exports = { ROLE, exists: function(room) { - var found = false; - _.forEach(Memory.creeps, c => { - if(c.role == ROLE && c.target == room) { - found = true; - return false; - } - }); - return found; + return Object.values(Memory.creeps).find(c => c.role == ROLE && c.target == room); }, spawn: function(targetRoom) { const name = `${ROLE}-${targetRoom}-${Game.time}`; // find an idle spawner as near the room as possible.. - var minPath = Number.POSITIVE_INFINITY; - var spawn; - _.forEach(Game.rooms, r => { - if(!r.controller || !r.controller.my) return; - if(r.energyAvailable < BODY_COST) return; - - let spawns = r.find(FIND_MY_SPAWNS, {filter: s => !s.spawning}); - if(!spawns.length) return; - - let path = Game.map.findRoute(r, targetRoom); - if(path == ERR_NO_PATH) return; - if(path.length >= minPath) return; - minPath = path.length; - - // TODO(baptr): Pick the closer spawn to the exit? - spawn = spawns[0]; - }); - if(!spawn) return ERR_NO_PATH; - console.log(`Spawning ${name} from ${spawn}`); - return spawn.spawnCreep([TOUGH, MOVE, TOUGH, MOVE], name, { - memory: { - target: targetRoom, - role: ROLE - } + const spawns = Object.values(Game.spawns).filter(s => s.isActive() && !s.spawning && s.room.energyAvailable >= BODY_COST); + const [spawn, path] = pathUtil.macroClosest(targetRoom, spawns, {flipPath: true}); + if(!path) return ERR_NO_PATH; + const longPath = pathUtil.longPath(spawn.pos, new RoomPosition(25, 25, targetRoom), { + macroPath: path, + swampCost: 1, + range: 10, }); + if(longPath == ERR_NO_PATH) return ERR_NO_PATH; + + const ret = spawn.spawnCreep(BODY, name, {memory: { + target: targetRoom, + exitPath: pathUtil.exitPath(longPath), + role: ROLE, + cost: BODY_COST, + }}); + if(ret == OK) console.log(`Spawning ${name} from ${spawn}`); + return ret; }, run: function(creep) { const target = creep.memory.target; if(!target) { - console.log(`${creep.name} has nothing to do, good day!`); - creep.suicide(); + console.log(`${creep.name} has nothing to do, good day!`); + creep.suicide(); + } + if(scoutUtil.needsUpdate(creep.pos.roomName)) { + scoutUtil.update(creep.room); + } + + if(creep.memory.roomPath || creep.memory.exitPath) { + return pathUtil.macroMove(creep, {plainCost: 1, swampCost: 1}); } - creep.moveTo(new RoomPosition(25, 25, target), {visualizePathStyle: {}, reusePath: 100}); + return creep.moveTo(new RoomPosition(25, 25, target), {visualizePathStyle: {}, reusePath: 100}); } -}; \ No newline at end of file +}; diff --git a/role2.storeUpgrader.js b/role2.storeUpgrader.js index b318ff6..7f8725c 100644 --- a/role2.storeUpgrader.js +++ b/role2.storeUpgrader.js @@ -7,8 +7,7 @@ const CONTROLLER_UPGRADE_RANGE = 3; function findSrc(room) { const opts = room.controller.pos.findInRange(FIND_STRUCTURES, CONTROLLER_UPGRADE_RANGE+1, { - filter: s => {return s.store && s.store.energy > 0;} - }); + filter: s => s.store && s.store.energy > 0 && s.structureType != STRUCTURE_POWER_SPAWN}); if(!opts.length) return null; const pref = _.sortBy(opts, [s => -s.store.energy]) return pref[0]; @@ -17,7 +16,7 @@ function findSrc(room) { module.exports = { spawnCondition: function(room, numExisting=0) { const src = findSrc(room); - return src && numExisting < Math.floor(src.store.energy/1000) && numExisting < 3; + return src && numExisting < Math.floor(src.store.energy/900) && numExisting < 3; }, spawn: function(spawn, mem={}) { const room = spawn.room; @@ -39,13 +38,18 @@ spawn: function(spawn, mem={}) { } var body = new BodyBuilder([WORK, CARRY, MOVE], room.energyAvailable); - body.extend([WORK, WORK, MOVE], limit=7); + // TODO size these more dynamically. 11 feels like it's often too much, + // especially if multiple spawn. + body.extend([WORK], limit=10); body.extend([MOVE], limit=8); // XXX worth it? // Not worth it at small WORK sizes, save up for a bigger body. if(body.count(WORK) < 3) return false; - const mem = {role: ROLE, cost: body.cost, src: src.id}; + mem.role = ROLE; + mem.cost = body.cost; + mem.src = src.id; + mem.life = {}; const name = `${ROLE}-${room.name}-${Game.time}`; const ret = spawn.spawnCreep(body.sort([MOVE, WORK, CARRY]), name, {memory: mem}); if(ret != OK) { @@ -54,7 +58,8 @@ spawn: function(spawn, mem={}) { return ret; }, run: function(creep) { - const src = Game.getObjectById(creep.memory.src); + util.track(creep, 'alive'); + let src = Game.getObjectById(creep.memory.src); const ctrl = creep.room.controller; if(!src || !ctrl.my) { console.log(`${creep.name} lost storage!!`); @@ -66,10 +71,11 @@ run: function(creep) { const delivery = creep.getActiveBodyparts(WORK)*UPGRADE_CONTROLLER_POWER; switch(ret) { case OK: + util.track(creep, 'upgrade'); creep.memory.delivered += delivery; break; case ERR_NOT_IN_RANGE: - creep.moveTo(ctrl); + util.track(creep, 'move', creep.moveTo(ctrl)); return; case ERR_NOT_ENOUGH_ENERGY: // filling is below @@ -81,7 +87,7 @@ run: function(creep) { if(!link && !creep.memory.hasOwnProperty('link')) { if(creep.room.memory.links) { link = Game.getObjectById(creep.room.memory.links.controller); - creep.moveTo(link); + util.track(creep, 'move', creep.moveTo(link)); } else { link = {}; } @@ -100,22 +106,32 @@ run: function(creep) { const mv = Math.min(creep.carryCapacity/2, link.energy); let transferRet = creep.transfer(src, RESOURCE_ENERGY, mv-delivery); let drawRet = creep.withdraw(link, RESOURCE_ENERGY, mv); + util.track(creep, 'linkTap', drawRet); // console.log(`${creep.name} had ${creep.carry.energy} energy, ${delivery} delivery. ${mv} mv. upgradeRet: ${ret} drawRet: ${drawRet} transferRet: ${transferRet}`); return; } } + if(src && src.store.energy == 0) { + src = findSrc(creep.room); + if(src) { + creep.memory.src = src.id; + } else { + return; + } + } // CPU_CLEANUP: Only grab every CARRY*50/WORK ticks. ret = creep.withdraw(src, RESOURCE_ENERGY, delivery); switch(ret) { case OK: + util.track(creep, 'withdraw'); break; case ERR_NOT_IN_RANGE: // TODO(baptr): Should be more careful about positioning at the start // so this can't happen. - creep.moveTo(src); + util.track(creep, 'move', creep.moveTo(src)); return case ERR_NOT_ENOUGH_RESOURCES: - creep.withdraw(src, RESOURCE_ENERGY); + util.track(creep, 'withdraw', creep.withdraw(src, RESOURCE_ENERGY)); break; } }, diff --git a/struct.link.js b/struct.link.js index 0186615..6c74f6a 100644 --- a/struct.link.js +++ b/struct.link.js @@ -1,12 +1,40 @@ module.exports = { run: function(link) { - var links = link.room.memory.links; - if(!links) return; // TODO(baptr): Plan these. - var dest = Game.getObjectById(links.controller); - if(!dest) return; - if(dest.id == link.id) return; - if(link.energy > dest.energy+1) { - link.transferEnergy(dest); + if(link.cooldown > 0) return ERR_TIRED; + + if(!link.memory.source && !link.memory.sink) { + if(link.pos.findInRange(FIND_SOURCES, 2).length) { // harvest source + link.memory.source = true; } + if(link.pos.getRangeTo(link.room.controller) <= 4) { // upgrade sink + link.memory.sink = true; + } + if(link.pos.findInRange(FIND_MY_SPAWNS, 3).length) { // spawn sink + link.memory.sink = true; + } + } + + if(link.memory.source && link.energy > 200) { + const sinks = link.room.find(FIND_MY_STRUCTURES, {filter: s => s.structureType == STRUCTURE_LINK && s.memory.sink && s.energy+1 < s.energyCapacity}); + const dest = sinks.sort((a, b) => a.energy - b.energy).shift(); + if(dest) { + link.transferEnergy(dest); + } + } } -}; \ No newline at end of file +}; + +if(!StructureLink.prototype.hasOwnProperty('memory')) Object.defineProperty(StructureLink.prototype, 'memory', { + get: function() { + if(this.id && !this.my) return undefined; + if(!Memory.structures) Memory.structures = {}; // XXX struct or tower? + return Memory.structures[this.id] = Memory.structures[this.id] || {}; + }, + set: function(value) { + if(this.id && !this.my) { + throw new Error("Could not set other player's tower memory"); + } + if(!Memory.structures) Memory.structures = {}; + Memory.structures[this.id] = value; + } +}); diff --git a/struct.tower.js b/struct.tower.js index 7d0bc7e..185dd44 100644 --- a/struct.tower.js +++ b/struct.tower.js @@ -3,18 +3,18 @@ const splay = require('util.splay'); module.exports = { TYPE: STRUCTURE_TOWER, // XXX use this, or just rely on the module name? run: function(tower) { - var t = tower.memory.target; // indicates attack recently if not now - var target = Game.getObjectById(t); + const t = tower.memory.target; // indicates attack recently if not now + let target = Game.getObjectById(t); if(target) { - var ret = tower.attack(target); + let ret = tower.attack(target); if(ret == OK) return; if(ret == ERR_INVALID_TARGET) { target = null; }; } if(!target && (t || splay.isTurn('tower', tower.id))) { - var targets = tower.room.find(FIND_HOSTILE_CREEPS); - var highValue = _.filter(targets, c => { + let targets = tower.room.find(FIND_HOSTILE_CREEPS); + const highValue = _.filter(targets, c => { var body = _.groupBy(c.body, 'type'); // TODO(baptr): Go for healers first. if(body[ATTACK] || body[RANGED_ATTACK] || body[CLAIM] || body[HEAL]) return true; @@ -29,22 +29,15 @@ run: function(tower) { } else { delete tower.memory.target; } - let friend = tower.pos.findClosestByRange(FIND_MY_CREEPS, {filter: c => c.hits < c.hitsMax}); + const pc = tower.pos.findClosestByRange(FIND_MY_POWER_CREEPS, {filter: pc => pc.hits < pc.hitsMax}); + if(pc) { + return tower.heal(pc); + } + const friend = tower.pos.findClosestByRange(FIND_MY_CREEPS, {filter: c => c.hits < c.hitsMax}); if(friend) { return tower.heal(friend); } } - - if(!splay.isTurn('tower', tower.id)) return; - - if(tower.energy > 700) { // Save up some for defense. - var closestDamagedStructure = tower.pos.findClosestByRange(FIND_STRUCTURES, { - filter: (structure) => structure.hits / structure.hitsMax < 0.95 - }); - if(closestDamagedStructure) { - tower.repair(closestDamagedStructure); - } - } } }; @@ -61,4 +54,4 @@ if(!StructureTower.prototype.hasOwnProperty('memory')) Object.defineProperty(Str if(!Memory.structures) Memory.structures = {}; Memory.structures[this.id] = value; } -}); \ No newline at end of file +}); diff --git a/tmp.remoteHarvest.js b/tmp.remoteHarvest.js index 2eaee72..715c05d 100644 --- a/tmp.remoteHarvest.js +++ b/tmp.remoteHarvest.js @@ -4,17 +4,13 @@ const harvester = require('role2.dropHarvester') const relocater = require('role.relocater'); const builder = require('role2.builder'); const hauler = require('role2.hauler'); +const local = require('local'); +const resUtil = require('util.resources'); -const SPAWN = Game.spawns.Spawn1; +const SPAWN = local.homeSpawn; const destRoom = ((SPAWN || {}).room || {}).name; module.exports = { -reserve: function(roomName, spawn=SPAWN) { - return reserver.spawn(spawn, roomName); -}, -harvest: function(roomName, spawn=SPAWN) { - return harvester.spawnRemote(spawn, roomName); -}, build: function(roomName, spawn=SPAWN) { var mem = {}; relocater.setMem(mem, roomName, builder.ROLE); @@ -22,9 +18,6 @@ build: function(roomName, spawn=SPAWN) { const name = `${builder.ROLE}-${roomName}-${Game.time}`; return spawn.spawnCreep(body, name, {memory: mem}); }, -haul: function(roomName, spawn=SPAWN) { - return hauler.spawnRemote(spawn, roomName, destRoom); -}, roads: function(roomName, spawn=SPAWN) { // XXX calculate this stuff. /* @@ -47,8 +40,8 @@ run: function(roomName) { // so assume this is only run every 500 ticks or so... // if(Game.time % 500 != 0) return false; - var spawns = _.filter(Game.spawns, s => {return !s.spawning && s.room.energyAvailable > 1000}); - if(spawns.length == 0) return; + const spawns = _.filter(Game.spawns, s => {return !s.spawning && s.room.energyAvailable > 600}); + if(spawns.length == 0) return ERR_BUSY; const room = Game.rooms[roomName]; if(room) { @@ -59,31 +52,46 @@ run: function(roomName) { }), c => c.memory.role); const numRole = role => (kinds[role] || []).length; console.log(`remoteHarvest ${roomName} state:`, JSON.stringify(_.mapValues(kinds, (v, k) => numRole(k)))); - const ctrl = room.controller; - if(numRole(reserver.ROLE) < 1 && !ctrl.reservation || ctrl.reservation.ticksToEnd < 300) { - let spawn = spawns.shift(); - let ret = module.exports.reserve(roomName, spawn); - console.log(`${spawn} spawning more reservers: ${ret}`); + + const hauls = hauler.assigned(roomName); + const dist = Game.map.findRoute(destRoom, roomName).length; + const availRes = resUtil.roomResource(room); + console.log(`remoteHarvest(${roomName}) has ${hauls.length} haulers to go ${dist} away, for ${availRes} energy`); + if(hauls.length < dist && (hauls.length+1) * 850 < availRes) { + const spawn = spawns.shift(); + const ret = hauler.spawnRemote(spawn, roomName, destRoom); + console.log(`${spawn} spawning more haulers: ${ret}`); if(ret != OK) spawns.push(spawn); - if(spawns.length == 0) return; + if(spawns.length == 0) return ret; } - // would be nice to send (especially) harvesters in before the old ones expire. - if(numRole(harvester.ROLE) < 2) { - let spawn = spawns.shift(); - let ret = module.exports.harvest(roomName, spawn); - console.log(`${spawn} spawning more harvesters: ${ret}`); + + // TODO: Calculate whether we'll drain the unreserved source fast + // enough to warrant this. + const ctrl = room.controller; + const rsvs = reserver.assigned(roomName).filter(c => c.ticksToLive > 100); + if(!rsvs.length && (!ctrl.reservation || ctrl.reservation.uername != 'baptr' || ctrl.reservation.ticksToEnd < 100)) { + const spawn = spawns.shift(); + const ret = reserver.spawn(spawn, roomName); + // XXX Debugging how I ended up with 3 reservers. + console.log(`${spawn} spawning more reservers (had ${rsvs}, ${JSON.stringify(ctrl.reservation)}): ${ret}`); if(ret != OK) spawns.push(spawn); - if(spawns.length == 0) return; + if(spawns.length == 0) return ret; } - // TODO(baptr): Check these from Memory.creeps instead of room.creeps - if(numRole(hauler.ROLE) < 2) { - let spawn = spawns.shift(); - let ret = module.exports.haul(roomName, spawn); - console.log(`${spawn} spawning more haulers: ${ret}`); + + // TODO: don't necessarily need a harvester yet if road workers are keeping the sources empty.. + // TODO: or even to spawn more (yet) if there's already a backlog of energy around... + const hvsts = harvester.assigned(roomName).filter(c => c.ticksToLive > 100); + if(hvsts.length < room.find(FIND_SOURCES).length) { + const spawn = spawns.shift(); + const ret = harvester.spawnRemote(spawn, roomName); + console.log(`${spawn} spawning more harvesters: ${ret}`); if(ret != OK) spawns.push(spawn); - if(spawns.length == 0) return; + if(spawns.length == 0) return ret; } + + /* + // TODO: See if there's enough to build/repair for this to be useful. if(numRole(builder.ROLE) < 1) { let spawn = spawns.shift(); let ret = module.exports.build(roomName, spawn); @@ -93,7 +101,11 @@ run: function(roomName) { } */ } else { - module.exports.reserve(roomName); + if(!harvester.assigned(roomName).length) { + return harvester.spawnRemote(SPAWN, roomName); + } else { + console.log("Waiting for remoteHarvester to arrive in " + roomName); + } } } -}; \ No newline at end of file +}; diff --git a/util.bodybuilder.js b/util.bodybuilder.js index 4e07cdc..62058dd 100644 --- a/util.bodybuilder.js +++ b/util.bodybuilder.js @@ -6,6 +6,10 @@ class BodyBuilder { this.cost = util.bodyCost(base); this.energyRemaining = energyAvailable - this.cost; } + + valid() { + return this.energyRemaining >= 0; + } extend(parts, limit=0) { let c = util.bodyCost(parts); @@ -38,4 +42,4 @@ class BodyBuilder { } } -module.exports = BodyBuilder; \ No newline at end of file +module.exports = BodyBuilder; diff --git a/util.creep.js b/util.creep.js index 3c78dca..27b5244 100644 --- a/util.creep.js +++ b/util.creep.js @@ -11,6 +11,15 @@ function renewCost(creep) { module.exports = { bodyCost, renewCost, +bodyPower: function(creep, type, mult) { + let out = 0; + for(const b of creep.body) { + if(b.hits == 0) continue; + if(b.type != type) continue; + out += mult; + } + return out; +}, hasBody: function(creep, type) { var body = _.groupBy(creep.body, 'type'); return body[type]; @@ -32,9 +41,13 @@ recycle: function(creep) { return recycle.convert(creep); }, track: function(creep, action, ret = OK) { - if(creep.memory.life && ret == OK) { - creep.memory.life[action] = (creep.memory.life[action]+1) || 1; + if(!creep.memory.life) return ret; + if(ret == OK) { + creep.memory.life[action] = (creep.memory.life[action]+1) || 1; + } else if(ret == ERR_TIRED) { + creep.memory.life['tired'] = (creep.memory.life['tired']+1) || 1; } + return ret; }, renew: function(creep, start=100, stop=600) { // It's generally slightly more expensive to renew a creep than spawn it @@ -82,5 +95,24 @@ renew: function(creep, start=100, stop=600) { } } return false; -} +}, +flee: function(creep, minRange=4, swampCost=10) { + // TODO: If the chaser(s) are less fast, then swamps are preferred. If + // they're equal speed, finding paths that make them move through swamps + // while we don't let us break away... + + // TODO: Split away from other fleeing friends so someone gets to survive. + + // TODO: try to flee in a target direction maybe? + const baddies = creep.pos.findInRange(FIND_HOSTILE_CREEPS, minRange+1, { + filter: c => c.body.some(b => b.hits && SCARY_PARTS.includes(b.type))}); + if(!baddies.length) return ERR_NOT_IN_RANGE; + const path = PathFinder.search(creep.pos, baddies.map(b => ({range: minRange, pos: b.pos})), {flee: true, maxCost: 100, swampCost}); + if(path.path.length) { + return creep.move(creep.pos.getDirectionTo(path.path[0])); + } + return ERR_NO_PATH; +}, }; + +const SCARY_PARTS = [ATTACK, RANGED_ATTACK]; diff --git a/util.defense.js b/util.defense.js index e48776c..64e9620 100644 --- a/util.defense.js +++ b/util.defense.js @@ -178,7 +178,6 @@ class Queue { const idx = this.data.findIndex(f => n.x == f.x && n.y == f.y); if(idx >= 0) { const f = this.data[idx]; - // console.log(`Trying to re-insert (${n.x}, ${n.y}). Was {cost=${f.cost}, est=${f.est}, via=(${f.prev.x}, ${f.prev.y})}, now {cost=${n.cost}, est=${n.est}, via=(${n.prev.x}, ${n.prev.y})}`); this.data[idx] = n; } else { this.data.push(n); diff --git a/util.pathing.js b/util.pathing.js index af67fcd..27d5ed0 100644 --- a/util.pathing.js +++ b/util.pathing.js @@ -1,26 +1,97 @@ +const local = require('local'); /* TODOs: - Treat planned roads as cheaper during multiple passes. */ +function longPath(srcPos, dstPos, opts={}) { + opts = Object.assign({}, {range: 1, cache: true, plainCost: 1, swampCost: 5, maxCost: CREEP_LIFE_TIME, maxOps: 10000}, opts); + let cacheKey; + if(opts.cache) { + if(!global.longPath) { + console.log("Lost longPath cache, initializing"); + global.longPath = {}; + } + cacheKey = `${srcPos.roomName}:${srcPos.x},${srcPos.y}-${dstPos.roomName}:${dstPos.x},${dstPos.y}:${opts.plainCost},${opts.swampCost}`; + // TTL, pruning? + if(global.longPath[cacheKey]) return global.longPath[cacheKey]; + } + let macro = opts.macroPath; + if(!macro) { + macro = macroPath(srcPos.roomName, dstPos.roomName, undefined, undefined, opts.swampCost); + if(macro == ERR_NO_PATH) { + if(opts.cache) global.longPath[cacheKey] = ERR_NO_PATH; + return ERR_NO_PATH; + } + } + const rooms = macro.map(step => step.room).concat(srcPos.roomName); + const path = PathFinder.search(srcPos, {pos: dstPos, range: opts.range}, { + maxCost: opts.maxCost, + maxOps: opts.maxOps, + plainCost: opts.plainCost, + swampCost: opts.swampCost, + // TODO: use scouted/visible matricies? + roomCallback: name => rooms.includes(name) ? undefined : false, + }); + if(opts.cache) { + global.longPath[cacheKey] = path; + } + return path; +} + +function travelCost(creep) { + // TODO: Handle MOVE/CARRY boosts. + // TODO: Better planning for laden/unladen. + let weight = Math.ceil(creep.store.getUsedCapacity() / CARRY_CAPACITY) + let strength = 0; + for(const b of creep.body) { + if(b.hits == 0) continue; + if(b.type == MOVE) { + strength += 2; + } else if(b.type != CARRY) { + weight++; + } + } + return { + plainCost: Math.max(Math.ceil(2*weight/strength), 1), + swampCost: Math.max(Math.ceil(5*weight/strength), 1), + }; +} + +function macroPath(srcRoom, dstRoom, badRooms=local.badRooms||[], badCost=Infinity, swampCost=3) { + return Game.map.findRoute(srcRoom, dstRoom, { + routeCallback: name => { + if(badRooms.includes(name)) return badCost; + if(name.includes('0')) return 1; + return swampCost; // maybe even higher in season since there are so many swamps + }}); +} + function roomCallback(roomName) { const room = Game.rooms[roomName]; - var out = new PathFinder.CostMatrix(); - if(!room) { - return out; - } - var obstacles = room.find(FIND_STRUCTURES).concat(room.find(FIND_CONSTRUCTION_SITES)); - _.forEach(obstacles, s => { - if(OBSTACLE_OBJECT_TYPES.includes(s.structureType)) { - out.set(s.pos.x, s.pos.y, 255); - } else if(s.structureType == STRUCTURE_ROAD) { - out.set(s.pos.x, s.pos.y, 1); - } - }); + if(!room) return undefined; + + const out = new PathFinder.CostMatrix(); + const buildings = room.find(FIND_STRUCTURES); + for(const s of buildings) { + if(OBSTACLE_OBJECT_TYPES.includes(s.structureType)) { + out.set(s.pos.x, s.pos.y, 255); + } else if(s.structureType == STRUCTURE_ROAD) { + out.set(s.pos.x, s.pos.y, 1); + } else if(s.structureType == STRUCTURE_RAMPART && !s.my) { + out.set(s.pos.x, s.pos.y, 255); + } + }; return out; } +function exitPath(fullPath) { + // TODO: Only remember the *outbound* step, not the inbound ones. + const path = fullPath.path; + return path.filter(pos => pos.x == 0 || pos.x == 49 || pos.y == 0 || pos.y == 49).concat(path[path.length-1]); +} + module.exports = { -roomCallback: roomCallback, +roomCallback, swampCheck: function(src, dest, roomMatrix=null) { // XXX will break if this ever tries to span a room. // TODO(baptr): Presumably there could be cases where leaving the room @@ -143,5 +214,82 @@ visPath: function(path) { } roomVis.line(prev, cur); } -} -}; \ No newline at end of file +}, +macroPath, +macroMove: function(creep, moveOpts = {}) { + // XXX compare pathfinding the whole route with available rooms limited to the macroPath. + // Or maybe always path 2 rooms ahead? + const roomPath = creep.memory.roomPath; + if(roomPath) { // XXX deprecated + let hop = roomPath[0]; + if(creep.room.name == hop.room) { + roomPath.shift(); + hop = roomPath[0]; + if(!hop) delete creep.memory.roomPath; + } + if(hop) { + const exit = creep.pos.findClosestByPath(hop.exit); + // TODO: Handle it being cheaper to leave the room and come back through a + // different entrance. + return creep.moveTo(exit, Object.assign({visualizePathStyle: {}, reusePath: 50, maxRooms: 1}, moveOpts)); + } + return creep.moveTo(25, 25, Object.assign({maxRooms: 1}, moveOpts)); + } + + const exitPath = creep.memory.exitPath; + if(exitPath) { + let hop = exitPath[0]; + if(creep.pos.roomName == hop.roomName) { + exitPath.shift(); + hop = exitPath[0]; + if(!hop) delete creep.memory.exitPath; + } + if(hop) { + return creep.moveTo(new RoomPosition(hop.x, hop.y, hop.roomName), + Object.assign({visualizePathStyle: {}, reusePath: 50, maxRooms: 1}, moveOpts)); + } + console.log(`${creep.name} arrived in ${creep.pos.roomName} with ${creep.ticksToLive} ticks remaining!`); + return creep.moveTo(25, 25, Object.assign({maxRooms: 1}, moveOpts)); + } + + console.log(`${creep.name} trying to macroMove with no stored path`); + return ERR_NO_PATH; +}, +macroClosest: function(srcRoom, goals, opts={}) { + let goal = ERR_NO_PATH; + let bestPath; + for(const g of goals) { + const path = macroPath(srcRoom, g.pos.roomName, opts.badRooms, opts.badCost); + if(bestPath && path.length >= bestPath.length) continue; + bestPath = path; + goal = g; + } + if(bestPath && opts.flipPath) { + // [src], -down->mid, -down->close, -right->dest + // [dest], -left->close, -up->mid, -up->src + const len = bestPath.length; + const out = new Array(len); + bestPath.reverse().forEach((step, idx) => { + out[idx] = {exit: (step.exit + 4) % 8}; + if(idx > 0) out[idx-1].room = step.room; + }); + out[len-1].room = srcRoom; + bestPath = out; + } + return [goal, bestPath]; +}, +exitPath, +setMem: function(mem, srcPos, dstPos, opts={}) { + // TODO: better position info + const path = longPath(srcPos, dstPos, opts); + if(path == ERR_NO_PATH) { + console.log(`Unable to find remote path from ${srcPos} to ${dstPos}`); + return ERR_NO_PATH; + } + if(opts.ttl && path.cost > opts.ttl) return ERR_NOT_IN_RANGE; + mem.exitPath = exitPath(path); + return mem; +}, +travelCost, +longPath, +}; diff --git a/util.resources.js b/util.resources.js index 3820260..4661848 100644 --- a/util.resources.js +++ b/util.resources.js @@ -24,6 +24,13 @@ function harvest(creep, src, type=RESOURCE_ENERGY) { return creep.harvest(src); } +const DEPOSIT_ONLY_STORES = [ + STRUCTURE_SPAWN, + STRUCTURE_EXTENSION, + STRUCTURE_TOWER, + STRUCTURE_POWER_SPAWN, +]; + function findSrc(creep, type=RESOURCE_ENERGY, resMin=25) { var src = Game.getObjectById(creep.memory.source); if(resAvail(src, type) > resMin) { @@ -31,17 +38,21 @@ function findSrc(creep, type=RESOURCE_ENERGY, resMin=25) { } // find closest of res+store structs - var structs = creep.room.find(FIND_STRUCTURES, { - filter: s => (s.store || {})[type] > resMin + const structs = creep.room.find(FIND_STRUCTURES, { + filter: s => (s.store || {})[type] > resMin && !DEPOSIT_ONLY_STORES.includes(s.structureType) }); - var res = creep.room.find(FIND_DROPPED_RESOURCES, { + const res = creep.room.find(FIND_DROPPED_RESOURCES, { filter: r => r.resourceType == type && r.amount > resMin }); - src = creep.pos.findClosestByPath(structs.concat(res)); + const bodies = creep.room.find(FIND_TOMBSTONES, {filter: t => t.store[type] > resMin}); + src = creep.pos.findClosestByPath(structs.concat(res).concat(bodies)); if(src) { creep.memory.source = src.id; return src; } + + // TODO: Consider if it's acceptable to raid the spawn energy. + // fall back on trying to harvest if(type == RESOURCE_ENERGY && creep.getActiveBodyparts(WORK)) { // memoize or not? @@ -54,8 +65,28 @@ function findSrc(creep, type=RESOURCE_ENERGY, resMin=25) { return null; } +const CTRL_UPGRADE_RANGE = 3; +function roomCtrlStores(room) { + return room.controller.pos.findInRange(FIND_STRUCTURES, CTRL_UPGRADE_RANGE+1, { + filter: s => s.store + }); +} + +function roomResource(room, type=RESOURCE_ENERGY) { + let out = 0; + for(const s of room.find(FIND_STRUCTURES)) { + if(s.store) out += s.store[type]; + } + for(const r of room.find(FIND_DROPPED_RESOURCES)) { + if(r.resourceType == type) out += r.amount; + } + return out +} + module.exports = { harvest, findSrc, resAvail, + roomCtrlStores, + roomResource, }; diff --git a/util.road.js b/util.road.js new file mode 100644 index 0000000..91651c2 --- /dev/null +++ b/util.road.js @@ -0,0 +1,11 @@ +module.exports = { +move: function(creep, dest) { + // XXX make sure to stay to roads as much as possible: + // - ignore friendly creeps + // - reuse paths for a long time. + // - could probably even path find the whole road once and use it for everything + // - stay spaced at least 1 apart for passing + // - pull/don't block the road if you stop to work + return creep.moveTo(dest); +} +}; diff --git a/util.scout.js b/util.scout.js new file mode 100644 index 0000000..56d75d4 --- /dev/null +++ b/util.scout.js @@ -0,0 +1,184 @@ +module.exports = { + dumpRoom, + needsUpdate: function(roomName) { + const now = Game.time; + // TODO: explicitly splay? + if(now % 20 != 0) return false; + if(!Memory.scout) Memory.scout = {}; + if(Memory.scout[roomName] && Memory.scout[roomName].lastSeen > now-500) return false; + return true; + }, + update: function(room) { + if(!Memory.scout) Memory.scout = {}; + Memory.scout[room.name] = dumpRoom(room); + }, + test: function(iterations=1, dim=10, room='E28S12') { + // TODO: Consider compression. lz-string, custom indexed RLE, just better bit packing? + // matrix.serialize turns 2500 uint8s (locally limited to 6 values) into 6500 chars as a json Uint32Array + // The loose data for a populated room is less than 300 chars more. + // RawMemory segments are at most 100kb, so room for ~14 rooms worth of data per segment. + const start = Game.cpu.getUsed(); + const raw = dumpRoom(Game.rooms[room]); + const big = {}; + for(let x = 0; x < dim; x++) { + for(let y = 0; y < dim; y++) { + big[`E${x}N${y}`] = raw; + } + } + const gen = Game.cpu.getUsed(); + const str = JSON.stringify(big); + const stringed = Game.cpu.getUsed(); + for(let i = 0; i < iterations; i++) { + JSON.parse(str); + } + const end = Game.cpu.getUsed(); + console.log(`dumpRoom(${room}) test done. dump took ${gen-start}ms. serialized ${dim}x${dim} macro grid into ${str.length} chars in ${stringed-gen}ms; deserialized in ${(end-stringed)/iterations}ms each`); + return raw; + } +}; + +/* +{ + "lastSeen": 58840, + "controller": { "x": 18, "y": 15, "rcl": 4, "owner": "baptr" }, + "sources": [ + { "x": 16, "y": 24, "max": 3000 }, + { "x": 16, "y": 28, "max": 3000 } + ], + "mineral": { "x": 8, "y": 21, "type": "L", "density": 2, "expires": null }, + "towers": [ + { "x": 19, "y": 36 } + ], + "rawMatrix": [/*blob* /], +} +*/ +function dumpRoom(room) { + if(!(room instanceof Room)) { + const obj = Game.rooms[room]; + if(!obj) console.log(`scout.dumpRoom(${room}): room not visible`); + room = obj; + } + if(!room) { + return ERR_NOT_FOUND; + } + + const start = Game.cpu.getUsed(); + const out = {lastSeen: Game.time}; + + const ctrl = room.controller + if(ctrl) { + // TODO: safe mode or progress info? + let owner; + if(ctrl.owner) owner = ctrl.owner.username; + if(ctrl.reservation) owner = ctrl.reservation.username; + out.controller = { + x: ctrl.pos.x, + y: ctrl.pos.y, + rcl: ctrl.level, + owner, + }; + } + + out.sources = room.find(FIND_SOURCES).map(s => ({ + x: s.pos.x, y: s.pos.y, + max: s.energyCapacity})); + + const deps = room.find(FIND_DEPOSITS).map(d => ({ + x: d.pos.x, y: d.pos.y, + type: d.depositType, + expires: Game.time+d.ticksToDecay})); + if(deps.length) { // TODO: decide when to do empty absent. Just based on prevalence? + out.deposits = deps; + } + + const minerals = room.find(FIND_MINERALS).map(m => ({ + x: m.pos.x, y: m.pos.y, + type: m.mineralType, + density: m.density, + expires: Game.time+m.ticksToRegenerate})); + if(minerals.length) { + if(minerals.length > 1) { + console.log(`Room ${room.name} has multiple minerals, dumpRoom doesn't support this: ${JSON.stringify(minerals)}`); + } + out.mineral = minerals[0]; + } + + const structs = room.find(FIND_STRUCTURES); + + for(const s of structs) { + if(s instanceof StructureKeeperLair) { + if(!out.invaderLairs) out.invaderLairs = []; + // XXX figure out what ticksToSpawn looks like when a keeper is still active + // I probably need to look for nearby keeper creeps and add 300 to their TTL. + out.invaderLairs.push({x: s.pos.x, y: s.pos.y, nextSpawn: Game.time+s.ticksToSpawn}); + } else if(s instanceof StructureInvaderCore) { + out.invaderCore = { + x: s.pos.x, y: s.pos.y, + level: s.level, + } + const store = {}; + const conts = structs.filter(s => s.strutureType == STRUCTURE_CONTAINER); + for(const c of conts) { + for(const [t, v] of Object.entries(c.store)) { + store[t] = (store[t] || 0) + v; + } + } + out.invaderCore.store = store; + } else if(s instanceof StructureTower) { + if(!out.towers) out.towers = []; + out.towers.push({x: s.pos.x, y: s.pos.y}); + } else if(s instanceof StructurePowerBank) { + out.powerBank = {x: s.pos.x, y: s.pos.y, power: s.power, hits: s.hits, expires: Game.time+s.ticksToDecay}; + } + } + + // Matrix... + const matrix = new PathFinder.CostMatrix; + + // Note: It looks like pathfinder has pretty efficient access to terrain data + // already, so it seems safe to leave the raw terrain data out. + // TODO: Benchmark the difference sometime. + const terrain = room.getTerrain(); //.getRawBuffer(); + /* + const WALL_WEIGHT = 255; + const SWAMP_WEIGHT = 10; + const PLAINS_WEIGHT = 2; + for(let y = 0; y < 50; y++) { + for(let x = 0; x < 50; x++) { + const tile = terrain[y*50 + x]; + const cost = + tile == TERRAIN_MASK_WALL ? WALL_WEIGHT : + tile == TERRAIN_MASK_SWAMP ? SWAMP_WEIGHT : PLAINS_WEIGHT; + matrix.set(x, y, cost); + } + } + */ + + let set = 0; + for(const s of structs) { + if(OBSTACLE_OBJECT_TYPES.includes(s.structureType)) { + if(terrain.get(s.pos.x, s.pos.x) != TERRAIN_MASK_WALL) { + matrix.set(s.pos.x, s.pos.y, OBSTACLE_WEIGHT); + set++; + } + } else if(s instanceof StructureRampart && !s.my) { + matrix.set(s.pos.x, s.pos.y, RAMPART_WEIGHT); + set++; + } else if(s instanceof StructureRoad) { + if(matrix.get(s.pos.x, s.pos.y) != RAMPART_WEIGHT) { + matrix.set(s.pos.x, s.pos.y, ROAD_WEIGHT); + set++; + } + } + } + if(set) out.rawMatrix = matrix.serialize(); + //console.log(`dumpRoom(${room.name}) took ${Game.cpu.getUsed() - start}ms. rawMatrix is ${out.rawMatrix ? JSON.stringify(out.rawMatrix).length : undefined} chars. vs ${matrix._bits.length} _bits`); + + return out; +} + +// XXX Need to map all obstacles to 255 for use, but this lets us embed some +// possibly useful data fairly cheaply. +const RAMPART_WEIGHT = 253; +const OBSTACLE_WEIGHT = 254; +const ROAD_WEIGHT = 1; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..3871afe --- /dev/null +++ b/yarn.lock @@ -0,0 +1,2510 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + dependencies: + "@babel/highlight" "^7.14.5" + +"@babel/compat-data@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.5.tgz#8ef4c18e58e801c5c95d3c1c0f2874a2680fadea" + integrity sha512-kixrYn4JwfAVPa0f2yfzc2AWti6WRRyO3XjWW5PJAvtE11qhSayrrcrEnee05KAtNaPC+EwehE8Qt1UedEVB8w== + +"@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.5.tgz#d281f46a9905f07d1b3bf71ead54d9c7d89cb1e3" + integrity sha512-RN/AwP2DJmQTZSfiDaD+JQQ/J99KsIpOCfBE5pL+5jJSt7nI3nYGoAXZu+ffYSQ029NLs2DstZb+eR81uuARgg== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-compilation-targets" "^7.14.5" + "@babel/helper-module-transforms" "^7.14.5" + "@babel/helpers" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/generator@^7.14.5", "@babel/generator@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" + integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== + dependencies: + "@babel/types" "^7.14.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" + integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== + dependencies: + "@babel/compat-data" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.16.6" + semver "^6.3.0" + +"@babel/helper-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== + dependencies: + "@babel/helper-get-function-arity" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-get-function-arity@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-hoist-variables@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-member-expression-to-functions@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz#d5c70e4ad13b402c95156c7a53568f504e2fb7b8" + integrity sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-imports@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" + integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-transforms@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" + integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-replace-supers" "^7.14.5" + "@babel/helper-simple-access" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/helper-validator-identifier" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-optimise-call-expression@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" + integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + +"@babel/helper-replace-supers@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" + integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.14.5" + "@babel/helper-optimise-call-expression" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-simple-access@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" + integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-split-export-declaration@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-validator-identifier@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" + integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== + +"@babel/helpers@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.5.tgz#4870f8d9a6fdbbd65e5674a3558b4ff7fef0d9b2" + integrity sha512-xtcWOuN9VL6nApgVHtq3PPcQv5qFBJzoSZzJ/2c0QK/IP/gxVcoWSNQwFEGvmbQsuS9rhYqjILDGGXcTkA705Q== + dependencies: + "@babel/template" "^7.14.5" + "@babel/traverse" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.5.tgz#4cd2f346261061b2518873ffecdf1612cb032829" + integrity sha512-TM8C+xtH/9n1qzX+JNHi7AN2zHMTiPUtspO0ZdHflW8KaskkALhMmuMHb4bCmNdv9VAPzJX3/bXqkVLnAvsPfg== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" + integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/template@^7.14.5", "@babel/template@^7.3.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.5.tgz#c111b0f58afab4fea3d3385a406f692748c59870" + integrity sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" + integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.0.2.tgz#b8eeff8f21ac51d224c851e1729d2630c18631e6" + integrity sha512-/zYigssuHLImGeMAACkjI4VLAiiJznHgAl3xnFT19iWyct2LhrH3KXOjHRmxBGTkiPLZKKAJAgaPpiU9EZ9K+w== + dependencies: + "@jest/types" "^27.0.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.0.2" + jest-util "^27.0.2" + slash "^3.0.0" + +"@jest/core@^27.0.4": + version "27.0.4" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.0.4.tgz#679bf9ac07900da2ddbb9667bb1afa8029038f53" + integrity sha512-+dsmV8VUs1h/Szb+rEWk8xBM1fp1I///uFy9nk3wXGvRsF2lBp8EVPmtWc+QFRb3MY2b7u2HbkGF1fzoDzQTLA== + dependencies: + "@jest/console" "^27.0.2" + "@jest/reporters" "^27.0.4" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^27.0.2" + jest-config "^27.0.4" + jest-haste-map "^27.0.2" + jest-message-util "^27.0.2" + jest-regex-util "^27.0.1" + jest-resolve "^27.0.4" + jest-resolve-dependencies "^27.0.4" + jest-runner "^27.0.4" + jest-runtime "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" + jest-watcher "^27.0.2" + micromatch "^4.0.4" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^27.0.3": + version "27.0.3" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.3.tgz#68769b1dfdd213e3456169d64fbe9bd63a5fda92" + integrity sha512-pN9m7fbKsop5vc3FOfH8NF7CKKdRbEZzcxfIo1n2TT6ucKWLFq0P6gCJH0GpnQp036++yY9utHOxpeT1WnkWTA== + dependencies: + "@jest/fake-timers" "^27.0.3" + "@jest/types" "^27.0.2" + "@types/node" "*" + jest-mock "^27.0.3" + +"@jest/fake-timers@^27.0.3": + version "27.0.3" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.3.tgz#9899ba6304cc636734c74478df502e18136461dd" + integrity sha512-fQ+UCKRIYKvTCEOyKPnaPnomLATIhMnHC/xPZ7yT1Uldp7yMgMxoYIFidDbpSTgB79+/U+FgfoD30c6wg3IUjA== + dependencies: + "@jest/types" "^27.0.2" + "@sinonjs/fake-timers" "^7.0.2" + "@types/node" "*" + jest-message-util "^27.0.2" + jest-mock "^27.0.3" + jest-util "^27.0.2" + +"@jest/globals@^27.0.3": + version "27.0.3" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.0.3.tgz#1cf8933b7791bba0b99305cbf39fd4d2e3fe4060" + integrity sha512-OzsIuf7uf+QalqAGbjClyezzEcLQkdZ+7PejUrZgDs+okdAK8GwRCGcYCirHvhMBBQh60Jr3NlIGbn/KBPQLEQ== + dependencies: + "@jest/environment" "^27.0.3" + "@jest/types" "^27.0.2" + expect "^27.0.2" + +"@jest/reporters@^27.0.4": + version "27.0.4" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.0.4.tgz#95609b1be97afb80d55d8aa3d7c3179c15810e65" + integrity sha512-Xa90Nm3JnV0xCe4M6A10M9WuN9krb+WFKxV1A98Y4ePCw40n++r7uxFUNU7DT1i9Behj7fjrAIju9oU0t1QtCg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.0.2" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^27.0.2" + jest-resolve "^27.0.4" + jest-util "^27.0.2" + jest-worker "^27.0.2" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^7.0.0" + +"@jest/source-map@^27.0.1": + version "27.0.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.1.tgz#2afbf73ddbaddcb920a8e62d0238a0a9e0a8d3e4" + integrity sha512-yMgkF0f+6WJtDMdDYNavmqvbHtiSpwRN2U/W+6uztgfqgkq/PXdKPqjBTUF1RD/feth4rH5N3NW0T5+wIuln1A== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.0.2.tgz#0451049e32ceb609b636004ccc27c8fa22263f10" + integrity sha512-gcdWwL3yP5VaIadzwQtbZyZMgpmes8ryBAJp70tuxghiA8qL4imJyZex+i+USQH2H4jeLVVszhwntgdQ97fccA== + dependencies: + "@jest/console" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.0.4": + version "27.0.4" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.0.4.tgz#976493b277594d81e589896f0ed21f198308928a" + integrity sha512-6UFEVwdmxYdyNffBxVVZxmXEdBE4riSddXYSnFNH0ELFQFk/bvagizim8WfgJTqF4EKd+j1yFxvhb8BMHfOjSQ== + dependencies: + "@jest/test-result" "^27.0.2" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.2" + jest-runtime "^27.0.4" + +"@jest/transform@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.0.2.tgz#b073b7c589e3f4b842102468875def2bb722d6b5" + integrity sha512-H8sqKlgtDfVog/s9I4GG2XMbi4Ar7RBxjsKQDUhn2XHAi3NG+GoQwWMER+YfantzExbjNqQvqBHzo/G2pfTiPw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.0.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.2" + jest-regex-util "^27.0.1" + jest-util "^27.0.2" + micromatch "^4.0.4" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^27.0.2": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.2.tgz#e153d6c46bda0f2589f0702b071f9898c7bbd37e" + integrity sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@sinonjs/commons@^1.7.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^7.0.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.1.14" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.14.tgz#faaeefc4185ec71c389f4501ee5ec84b170cc402" + integrity sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" + integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" + integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.1.tgz#654f6c4f67568e24c23b367e947098c6206fa639" + integrity sha512-Vs0hm0vPahPMYi9tDjtP66llufgO3ST16WXaSTtDGEl9cewAl3AibmxWw6TINOqHPT9z0uABKAYjT9jNSg4npw== + dependencies: + "@babel/types" "^7.3.0" + +"@types/graceful-fs@^4.1.2": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/node@*": + version "15.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" + integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww== + +"@types/prettier@^2.1.5": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.0.tgz#2e8332cc7363f887d32ec5496b207d26ba8052bb" + integrity sha512-hkc1DATxFLQo4VxPDpMH1gCkPpBbpOoJ/4nhuXw4n63/0R6bCpQECj4+K226UJ4JO/eJQz+1mC2I7JsWanAdQw== + +"@types/screeps@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@types/screeps/-/screeps-3.2.2.tgz#d7a4cccac98e063f37dd16c546be229bfc849536" + integrity sha512-oI3zKXVL1HDkZ/x0xUhjD9CfAXmQPe/1kNkyKg5Wtd092/N5E5fYbNSH+Hgd5v6LEizYCLo+4drozp82j4Z9zw== + +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== + +"@types/yargs-parser@*": + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== + +"@types/yargs@^16.0.0": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.3.tgz#4b6d35bb8e680510a7dc2308518a80ee1ef27e01" + integrity sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ== + dependencies: + "@types/yargs-parser" "*" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.2.4: + version "8.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.0.tgz#af53266e698d7cffa416714b503066a82221be60" + integrity sha512-ULr0LDaEqQrMFGyQ3bhJkLsbtrQ8QibAseGZeaSUiT/6zb9IvIkomWHJIvgvwad+hinRAgsI51JcWk2yvwyL+w== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +babel-jest@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.2.tgz#7dc18adb01322acce62c2af76ea2c7cd186ade37" + integrity sha512-9OThPl3/IQbo4Yul2vMz4FYwILPQak8XelX4YGowygfHaOl5R5gfjm4iVx4d8aUugkW683t8aq0A74E7b5DU1Q== + dependencies: + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^27.0.1" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.0.1.tgz#a6d10e484c93abff0f4e95f437dad26e5736ea11" + integrity sha512-sqBF0owAcCDBVEDtxqfYr2F36eSHdx7lAVGyYuOBRnKdD6gzcy0I0XrAYCZgOA3CRrLhmR+Uae9nogPzmAtOfQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.0.1.tgz#7a50c75d16647c23a2cf5158d5bb9eb206b10e20" + integrity sha512-nIBIqCEpuiyhvjQs2mVNwTxQQa2xk70p9Dd/0obQGBf8FBzbnI8QhQKzLsWMN2i6q+5B0OcWDtrboBX5gmOLyA== + dependencies: + babel-plugin-jest-hoist "^27.0.1" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserslist@^4.16.6: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== + dependencies: + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +caniuse-lite@^1.0.30001219: + version "1.0.30001237" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz#4b7783661515b8e7151fc6376cfd97f0e427b9e5" + integrity sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw== + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +ci-info@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== + +cjs-module-lexer@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.1.tgz#2fd46d9906a126965aa541345c499aaa18e8cd73" + integrity sha512-jVamGdJPDeuQilKhvVn1h3knuMOZzr8QDnpk+M9aMlCaMkTDd6fBWPhiDqFvFZ07pL0liqabAiuy8SY4jGHeaw== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +decimal.js@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.1.tgz#9c9801d52ed5f576ff0a20e3022a13ee6e297e7c" + integrity sha512-XPLijkfJUh/PIBnfkcSHgvD6tlYixmcMAn3osTk6jt+H0v/mgURto1XUiD9DKuGX5NDoVS6dSlA23gd9FUaCFg== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +electron-to-chromium@^1.3.723: + version "1.3.752" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09" + integrity sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A== + +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expect@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.0.2.tgz#e66ca3a4c9592f1c019fa1d46459a9d2084f3422" + integrity sha512-YJFNJe2+P2DqH+ZrXy+ydRQYO87oxRUonZImpDodR1G7qo3NYd3pL+NQ9Keqpez3cehczYwZDBC3A7xk3n7M/w== + dependencies: + "@jest/types" "^27.0.2" + ansi-styles "^5.0.0" + jest-get-type "^27.0.1" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-regex-util "^27.0.1" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-ci@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" + +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.2.tgz#997253042b4a032950fc5f56abf3c5d1f8560801" + integrity sha512-eMeb1Pn7w7x3wue5/vF73LPCJ7DKQuC9wQUR5ebP9hDPpk5hzcT/3Hmz3Q5BOFpR3tgbmaWhJcMTVgC8Z1NuMw== + dependencies: + "@jest/types" "^27.0.2" + execa "^5.0.0" + throat "^6.0.1" + +jest-circus@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.0.4.tgz#3b261514ee3b3da33def736a6352c98ff56bb6e6" + integrity sha512-QD+eblDiRphta630WRKewuASLs/oY1Zki2G4bccntRvrTHQ63ljwFR5TLduuK4Zg0ZPzW0+8o6AP7KRd1yKOjw== + dependencies: + "@jest/environment" "^27.0.3" + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.0.2" + is-generator-fn "^2.0.0" + jest-each "^27.0.2" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-runtime "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + pretty-format "^27.0.2" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.0.4.tgz#491b12c754c0d7c6873b13a66f26b3a80a852910" + integrity sha512-E0T+/i2lxsWAzV7LKYd0SB7HUAvePqaeIh5vX43/G5jXLhv1VzjYzJAGEkTfvxV774ll9cyE2ljcL73PVMEOXQ== + dependencies: + "@jest/core" "^27.0.4" + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + jest-config "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" + prompts "^2.0.1" + yargs "^16.0.3" + +jest-config@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.0.4.tgz#c4f41378acf40ca77860fb4e213b12109d87b8cf" + integrity sha512-VkQFAHWnPQefdvHU9A+G3H/Z3NrrTKqWpvxgQz3nkUdkDTWeKJE6e//BL+R7z79dXOMVksYgM/z6ndtN0hfChg== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^27.0.4" + "@jest/types" "^27.0.2" + babel-jest "^27.0.2" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + is-ci "^3.0.0" + jest-circus "^27.0.4" + jest-environment-jsdom "^27.0.3" + jest-environment-node "^27.0.3" + jest-get-type "^27.0.1" + jest-jasmine2 "^27.0.4" + jest-regex-util "^27.0.1" + jest-resolve "^27.0.4" + jest-runner "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" + micromatch "^4.0.4" + pretty-format "^27.0.2" + +jest-diff@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.0.2.tgz#f315b87cee5dc134cf42c2708ab27375cc3f5a7e" + integrity sha512-BFIdRb0LqfV1hBt8crQmw6gGQHVDhM87SpMIZ45FPYKReZYG5er1+5pIn2zKqvrJp6WNox0ylR8571Iwk2Dmgw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.0.1" + jest-get-type "^27.0.1" + pretty-format "^27.0.2" + +jest-docblock@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.1.tgz#bd9752819b49fa4fab1a50b73eb58c653b962e8b" + integrity sha512-TA4+21s3oebURc7VgFV4r7ltdIJ5rtBH1E3Tbovcg7AV+oLfD5DcJ2V2vJ5zFA9sL5CFd/d2D6IpsAeSheEdrA== + dependencies: + detect-newline "^3.0.0" + +jest-each@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.0.2.tgz#865ddb4367476ced752167926b656fa0dcecd8c7" + integrity sha512-OLMBZBZ6JkoXgUenDtseFRWA43wVl2BwmZYIWQws7eS7pqsIvePqj/jJmEnfq91ALk3LNphgwNK/PRFBYi7ITQ== + dependencies: + "@jest/types" "^27.0.2" + chalk "^4.0.0" + jest-get-type "^27.0.1" + jest-util "^27.0.2" + pretty-format "^27.0.2" + +jest-environment-jsdom@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.0.3.tgz#ed73e913ddc03864eb9f934b5cbabf1b63504e2e" + integrity sha512-5KLmgv1bhiimpSA8oGTnZYk6g4fsNyZiA/6gI2tAZUgrufd7heRUSVh4gRokzZVEj8zlwAQYT0Zs6tuJSW/ECA== + dependencies: + "@jest/environment" "^27.0.3" + "@jest/fake-timers" "^27.0.3" + "@jest/types" "^27.0.2" + "@types/node" "*" + jest-mock "^27.0.3" + jest-util "^27.0.2" + jsdom "^16.6.0" + +jest-environment-node@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.3.tgz#b4acb3679d2552a4215732cab8b0ca7ec4398ee0" + integrity sha512-co2/IVnIFL3cItpFULCvXFg9us4gvWXgs7mutAMPCbFhcqh56QAOdKhNzC2+RycsC/k4mbMj1VF+9F/NzA0ROg== + dependencies: + "@jest/environment" "^27.0.3" + "@jest/fake-timers" "^27.0.3" + "@jest/types" "^27.0.2" + "@types/node" "*" + jest-mock "^27.0.3" + jest-util "^27.0.2" + +jest-get-type@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.1.tgz#34951e2b08c8801eb28559d7eb732b04bbcf7815" + integrity sha512-9Tggo9zZbu0sHKebiAijyt1NM77Z0uO4tuWOxUCujAiSeXv30Vb5D4xVF4UR4YWNapcftj+PbByU54lKD7/xMg== + +jest-haste-map@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.0.2.tgz#3f1819400c671237e48b4d4b76a80a0dbed7577f" + integrity sha512-37gYfrYjjhEfk37C4bCMWAC0oPBxDpG0qpl8lYg8BT//wf353YT/fzgA7+Dq0EtM7rPFS3JEcMsxdtDwNMi2cA== + dependencies: + "@jest/types" "^27.0.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^27.0.1" + jest-serializer "^27.0.1" + jest-util "^27.0.2" + jest-worker "^27.0.2" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.0.4.tgz#c669519ccf4904a485338555e1e66cad36bb0670" + integrity sha512-yj3WrjjquZwkJw+eA4c9yucHw4/+EHndHWSqgHbHGQfT94ihaaQsa009j1a0puU8CNxPDk0c1oAPeOpdJUElwA== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^27.0.3" + "@jest/source-map" "^27.0.1" + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^27.0.2" + is-generator-fn "^2.0.0" + jest-each "^27.0.2" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-runtime "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + pretty-format "^27.0.2" + throat "^6.0.1" + +jest-leak-detector@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.0.2.tgz#ce19aa9dbcf7a72a9d58907a970427506f624e69" + integrity sha512-TZA3DmCOfe8YZFIMD1GxFqXUkQnIoOGQyy4hFCA2mlHtnAaf+FeOMxi0fZmfB41ZL+QbFG6BVaZF5IeFIVy53Q== + dependencies: + jest-get-type "^27.0.1" + pretty-format "^27.0.2" + +jest-matcher-utils@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.0.2.tgz#f14c060605a95a466cdc759acc546c6f4cbfc4f0" + integrity sha512-Qczi5xnTNjkhcIB0Yy75Txt+Ez51xdhOxsukN7awzq2auZQGPHcQrJ623PZj0ECDEMOk2soxWx05EXdXGd1CbA== + dependencies: + chalk "^4.0.0" + jest-diff "^27.0.2" + jest-get-type "^27.0.1" + pretty-format "^27.0.2" + +jest-message-util@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.2.tgz#181c9b67dff504d8f4ad15cba10d8b80f272048c" + integrity sha512-rTqWUX42ec2LdMkoUPOzrEd1Tcm+R1KfLOmFK+OVNo4MnLsEaxO5zPDb2BbdSmthdM/IfXxOZU60P/WbWF8BTw== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.0.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.4" + pretty-format "^27.0.2" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.0.3: + version "27.0.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.3.tgz#5591844f9192b3335c0dca38e8e45ed297d4d23d" + integrity sha512-O5FZn5XDzEp+Xg28mUz4ovVcdwBBPfAhW9+zJLO0Efn2qNbYcDaJvSlRiQ6BCZUCVOJjALicuJQI9mRFjv1o9Q== + dependencies: + "@jest/types" "^27.0.2" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.1.tgz#69d4b1bf5b690faa3490113c47486ed85dd45b68" + integrity sha512-6nY6QVcpTgEKQy1L41P4pr3aOddneK17kn3HJw6SdwGiKfgCGTvH02hVXL0GU8GEKtPH83eD2DIDgxHXOxVohQ== + +jest-resolve-dependencies@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.4.tgz#a07a242d70d668afd3fcf7f4270755eebb1fe579" + integrity sha512-F33UPfw1YGWCV2uxJl7wD6TvcQn5IC0LtguwY3r4L7R6H4twpLkp5Q2ZfzRx9A2I3G8feiy0O0sqcn/Qoym71A== + dependencies: + "@jest/types" "^27.0.2" + jest-regex-util "^27.0.1" + jest-snapshot "^27.0.4" + +jest-resolve@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.0.4.tgz#8a27bc3f2f00c8ea28f3bc99bbf6f468300a703d" + integrity sha512-BcfyK2i3cG79PDb/6gB6zFeFQlcqLsQjGBqznFCpA0L/3l1L/oOsltdUjs5eISAWA9HS9qtj8v2PSZr/yWxONQ== + dependencies: + "@jest/types" "^27.0.2" + chalk "^4.0.0" + escalade "^3.1.1" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^27.0.2" + jest-validate "^27.0.2" + resolve "^1.20.0" + slash "^3.0.0" + +jest-runner@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.0.4.tgz#2787170a9509b792ae129794f6944d27d5d12a4f" + integrity sha512-NfmvSYLCsCJk2AG8Ar2NAh4PhsJJpO+/r+g4bKR5L/5jFzx/indUpnVBdrfDvuqhGLLAvrKJ9FM/Nt8o1dsqxg== + dependencies: + "@jest/console" "^27.0.2" + "@jest/environment" "^27.0.3" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-docblock "^27.0.1" + jest-environment-jsdom "^27.0.3" + jest-environment-node "^27.0.3" + jest-haste-map "^27.0.2" + jest-leak-detector "^27.0.2" + jest-message-util "^27.0.2" + jest-resolve "^27.0.4" + jest-runtime "^27.0.4" + jest-util "^27.0.2" + jest-worker "^27.0.2" + source-map-support "^0.5.6" + throat "^6.0.1" + +jest-runtime@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.0.4.tgz#2e4a6aa77cac32ac612dfe12768387a8aa15c2f0" + integrity sha512-voJB4xbAjS/qYPboV+e+gmg3jfvHJJY4CagFWBOM9dQKtlaiTjcpD2tWwla84Z7PtXSQPeIpXY0qksA9Dum29A== + dependencies: + "@jest/console" "^27.0.2" + "@jest/environment" "^27.0.3" + "@jest/fake-timers" "^27.0.3" + "@jest/globals" "^27.0.3" + "@jest/source-map" "^27.0.1" + "@jest/test-result" "^27.0.2" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-haste-map "^27.0.2" + jest-message-util "^27.0.2" + jest-mock "^27.0.3" + jest-regex-util "^27.0.1" + jest-resolve "^27.0.4" + jest-snapshot "^27.0.4" + jest-util "^27.0.2" + jest-validate "^27.0.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^16.0.3" + +jest-serializer@^27.0.1: + version "27.0.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.1.tgz#2464d04dcc33fb71dc80b7c82e3c5e8a08cb1020" + integrity sha512-svy//5IH6bfQvAbkAEg1s7xhhgHTtXu0li0I2fdKHDsLP2P2MOiscPQIENQep8oU2g2B3jqLyxKKzotZOz4CwQ== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.0.4.tgz#2b96e22ca90382b3e93bd0aae2ce4c78bf51fb5b" + integrity sha512-hnjrvpKGdSMvKfbHyaG5Kul7pDJGZvjVy0CKpzhu28MmAssDXS6GpynhXzgst1wBQoKD8c9b2VS2a5yhDLQRCA== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/parser" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.0.2" + graceful-fs "^4.2.4" + jest-diff "^27.0.2" + jest-get-type "^27.0.1" + jest-haste-map "^27.0.2" + jest-matcher-utils "^27.0.2" + jest-message-util "^27.0.2" + jest-resolve "^27.0.4" + jest-util "^27.0.2" + natural-compare "^1.4.0" + pretty-format "^27.0.2" + semver "^7.3.2" + +jest-util@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.2.tgz#fc2c7ace3c75ae561cf1e5fdb643bf685a5be7c7" + integrity sha512-1d9uH3a00OFGGWSibpNYr+jojZ6AckOMCXV2Z4K3YXDnzpkAaXQyIpY14FOJPiUmil7CD+A6Qs+lnnh6ctRbIA== + dependencies: + "@jest/types" "^27.0.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^3.0.0" + picomatch "^2.2.3" + +jest-validate@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.0.2.tgz#7fe2c100089449cd5cbb47a5b0b6cb7cda5beee5" + integrity sha512-UgBF6/oVu1ofd1XbaSotXKihi8nZhg0Prm8twQ9uCuAfo59vlxCXMPI/RKmrZEVgi3Nd9dS0I8A0wzWU48pOvg== + dependencies: + "@jest/types" "^27.0.2" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.0.1" + leven "^3.1.0" + pretty-format "^27.0.2" + +jest-watcher@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.0.2.tgz#dab5f9443e2d7f52597186480731a8c6335c5deb" + integrity sha512-8nuf0PGuTxWj/Ytfw5fyvNn/R80iXY8QhIT0ofyImUvdnoaBdT6kob0GmhXR+wO+ALYVnh8bQxN4Tjfez0JgkA== + dependencies: + "@jest/test-result" "^27.0.2" + "@jest/types" "^27.0.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.0.2" + string-length "^4.0.1" + +jest-worker@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.2.tgz#4ebeb56cef48b3e7514552f80d0d80c0129f0b05" + integrity sha512-EoBdilOTTyOgmHXtw/cPc+ZrCA0KJMrkXzkrPGNwLmnvvlN1nj7MPrxpT7m+otSv2e1TLaVffzDnE/LB14zJMg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.0.4: + version "27.0.4" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.0.4.tgz#91d4d564b36bcf93b98dac1ab19f07089e670f53" + integrity sha512-Px1iKFooXgGSkk1H8dJxxBIrM3tsc5SIuI4kfKYK2J+4rvCvPGr/cXktxh0e9zIPQ5g09kOMNfHQEmusBUf/ZA== + dependencies: + "@jest/core" "^27.0.4" + import-local "^3.0.2" + jest-cli "^27.0.4" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsdom@^16.6.0: + version "16.6.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" + integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.5" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + +mime-types@^2.1.12: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-releases@^1.1.71: + version "1.1.73" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nwsapi@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +p-each-series@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picomatch@^2.0.4, picomatch@^2.2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +pretty-format@^27.0.2: + version "27.0.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.2.tgz#9283ff8c4f581b186b2d4da461617143dca478a4" + integrity sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig== + dependencies: + "@jest/types" "^27.0.2" + ansi-regex "^5.0.0" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +prompts@^2.0.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" + integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +psl@^1.1.33: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +screeps-jest@eduter/screeps-jest: + version "1.3.0" + resolved "https://codeload.github.com/eduter/screeps-jest/tar.gz/7d1ee12f1db5dc72ff2f250ed37269ba61c246df" + +semver@^6.0.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-support@^0.5.6: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +stack-utils@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== + dependencies: + escape-string-regexp "^2.0.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +v8-to-istanbul@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" + integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.6.0.tgz#27c0205a4902084b872aecb97cf0f2a7a3011f4c" + integrity sha512-os0KkeeqUOl7ccdDT1qqUcS4KH4tcBTSKK5Nl5WKb2lyxInIZ/CpjkqKa1Ss12mjfdcRX9mHmPPs7/SxG1Hbdw== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.4.5: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^20.2.2: + version "20.2.7" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" + integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== + +yargs@^16.0.3: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2"