From 9316a872e77c14b85dc1026fd50cf63445a48a25 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 9 Apr 2019 11:57:54 -0700 Subject: [PATCH 001/137] Adding error check on controller sign length --- src/console/Console.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/console/Console.ts b/src/console/Console.ts index 7b5446dce..871c182dd 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -143,7 +143,9 @@ export class OvermindConsole { static setSignature(signature: string | undefined): string | undefined { let sig = signature ? signature : DEFAULT_OVERMIND_SIGNATURE; - if (sig.toLowerCase().includes('overmind') || sig.includes(DEFAULT_OVERMIND_SIGNATURE)) { + if (sig.length > 100) { + throw new Error(`Invalid signature: ${signature}; length is over 100 chars.`); + } else if (sig.toLowerCase().includes('overmind') || sig.includes(DEFAULT_OVERMIND_SIGNATURE)) { Memory.settings.signature = sig; return `Controller signature set to ${sig}`; } else { From 011aae0a992f4cc82de056d348bb3d0cbb840bf2 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 9 Apr 2019 13:03:40 -0700 Subject: [PATCH 002/137] Adding blocked flag --- src/Overseer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Overseer.ts b/src/Overseer.ts index fd688b4aa..8f5b16f5d 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -244,8 +244,12 @@ export class Overseer implements IOverseer { } let alreadyOwned = RoomIntel.roomOwnedBy(roomName); let alreadyReserved = RoomIntel.roomReservedBy(roomName); + let isBlocked = Game.flags[roomName+"-Blocked"] != null; + if (isBlocked) { + Game.notify("Room " + roomName + " is blocked, not expanding there."); + } let disregardReservations = !onPublicServer() || MY_USERNAME == MUON; - if (alreadyOwned || (alreadyReserved && !disregardReservations)) { + if (alreadyOwned || (alreadyReserved && !disregardReservations) || isBlocked) { return false; } let neighboringRooms = _.values(Game.map.describeExits(roomName)) as string[]; From 9502e66304a71eead828114a7ef3fcb931565a86 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 10 Apr 2019 13:03:56 -0700 Subject: [PATCH 003/137] Adding simple collection during room clearing phase --- src/directives/colony/clearRoom.ts | 7 +++++-- src/utilities/utils.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 82bbec80f..b0275ca05 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -4,7 +4,8 @@ import {ClaimingOverlord} from '../../overlords/colonization/claimer'; import {MY_USERNAME} from '../../~settings'; import {log} from '../../console/log'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; -import {printRoomName} from '../../utilities/utils'; +import {hasContents, printRoomName} from '../../utilities/utils'; +import {DirectiveHaul} from "../resource/haul"; /** @@ -52,7 +53,9 @@ export class DirectiveClearRoom extends Directive { for (let s of allStructures) { if (s.structureType == STRUCTURE_CONTROLLER) continue; if (keepStorageStructures && - (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL)) { + (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && hasContents(s.store)) { + // Create a collection flag + let result = s.pos.createFlag(undefined, DirectiveHaul.color, DirectiveHaul.secondaryColor); continue; } if (keepRoads && s.structureType == STRUCTURE_ROAD) { diff --git a/src/utilities/utils.ts b/src/utilities/utils.ts index 33b465b82..47341cd81 100644 --- a/src/utilities/utils.ts +++ b/src/utilities/utils.ts @@ -34,6 +34,15 @@ export function hasMinerals(store: { [resourceType: string]: number }): boolean return false; } +export function hasContents(store: { [resourceType: string]: number }): boolean { + for (let resourceType in store) { + if ((store[resourceType] || 0) > 0) { + return true; + } + } + return false; +} + /** * Obtain the username of the player */ From 69af3c59b7130cf7b94b02013f84aaf9a72cd3db Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 10 Apr 2019 14:39:56 -0700 Subject: [PATCH 004/137] Adding listSuspended command and adding more to clearRoom --- src/Overseer.ts | 2 +- src/console/Console.ts | 12 ++++++++++ src/declarations/memory.d.ts | 8 +++++++ src/directives/colony/clearRoom.ts | 36 ++++++++++++++++++++++++++++++ src/memory/Memory.ts | 1 + 5 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Overseer.ts b/src/Overseer.ts index 8f5b16f5d..e2550b73f 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -244,7 +244,7 @@ export class Overseer implements IOverseer { } let alreadyOwned = RoomIntel.roomOwnedBy(roomName); let alreadyReserved = RoomIntel.roomReservedBy(roomName); - let isBlocked = Game.flags[roomName+"-Blocked"] != null; + let isBlocked = Game.flags[roomName+"-Block"] != null; if (isBlocked) { Game.notify("Room " + roomName + " is blocked, not expanding there."); } diff --git a/src/console/Console.ts b/src/console/Console.ts index 871c182dd..ae1cef4c1 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -26,6 +26,7 @@ export class OvermindConsole { global.setLogLevel = log.setLogLevel; global.suspendColony = this.suspendColony; global.unsuspendColony = this.unsuspendColony; + global.listSuspendedColonies = this.listSuspendedColonies; global.openRoomPlanner = this.openRoomPlanner; global.closeRoomPlanner = this.closeRoomPlanner; global.cancelRoomPlanner = this.cancelRoomPlanner; @@ -246,6 +247,17 @@ export class OvermindConsole { } } + static listSuspendedColonies(): string { + let msg = 'Colonies currently suspended: \n'; + for (let i in Memory.colonies) { + let colonyMemory = Memory.colonies[i] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend == true) { + msg += 'Colony ' + i + ' \n'; + } + } + return msg; + } + // Room planner control ============================================================================================ static openRoomPlanner(roomName: string): string { diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 270b52c4f..b0c2589fa 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -1,4 +1,11 @@ type operationMode = 'manual' | 'semiautomatic' | 'automatic'; +/** + * 0: Basic + * 1: Collect from enemy storage/terminal + * 2: Collect from all sources TBD + * 3: Collect all and mine walls for energy TBD + */ +type resourceCollectionMode = number; interface RawMemory { _parsed: any; @@ -21,6 +28,7 @@ interface Memory { operationMode: operationMode; log: LoggerMemory; enableVisuals: boolean; + resourceCollectionMode: resourceCollectionMode; } profiler?: any; stats: any; diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index b0275ca05..3e300bdb4 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -6,6 +6,10 @@ import {log} from '../../console/log'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; import {hasContents, printRoomName} from '../../utilities/utils'; import {DirectiveHaul} from "../resource/haul"; +import {WorkerOverlord} from "../../overlords/core/worker"; +import {Zerg} from "../../zerg/Zerg"; +import {Pathing} from "../../movement/Pathing"; +import {DirectiveDismantle} from "../targeting/dismantle"; /** @@ -30,6 +34,9 @@ export class DirectiveClearRoom extends Directive { `removing directive!`); this.remove(true); } + if (Memory.settings.resourceCollectionMode && Memory.settings.resourceCollectionMode >= 1) { + this.memory.keepStorageStructures = true; + } } spawnMoarOverlords() { @@ -77,6 +84,19 @@ export class DirectiveClearRoom extends Directive { } + private findStructureBlockingController(pioneer: Zerg): Structure | undefined { + let blockingPos = Pathing.findBlockingPos(pioneer.pos, pioneer.room.controller!.pos, + _.filter(pioneer.room.structures, s => !s.isWalkable)); + if (blockingPos) { + let structure = blockingPos.lookFor(LOOK_STRUCTURES)[0]; + if (structure) { + return structure; + } else { + log.error(`${this.print}: no structure at blocking pos ${blockingPos.print}! (Why?)`); + } + } + } + run() { // Remove if structures are done if (this.room && this.room.my) { @@ -86,6 +106,22 @@ export class DirectiveClearRoom extends Directive { log.notify(`Removing clearRoom directive in ${this.pos.roomName}: operation completed.`); this.remove(); } + // Clear path if controller is not reachable + } else if (this.room && this.room.creeps.length > 1) { + let currentlyDismantling = _.find(this.room.flags, function(flag) { + return (flag.color == DirectiveDismantle.color && flag.secondaryColor == DirectiveDismantle.secondaryColor) + }); + + if (!currentlyDismantling) { + let pathablePos = this.room.creeps[0] ? this.room.creeps[0].pos + : Pathing.findPathablePosition(this.room.name); + let blockingLocation = Pathing.findBlockingPos(pathablePos, this.room.controller!.pos, + _.filter(this.room.structures, s => !s.isWalkable)); + if (blockingLocation && blockingLocation.lookFor(LOOK_FLAGS).length <= 0) { + log.notify(`Adding dismantle directive for ${this.pos.roomName} to reach controller.`); + blockingLocation!.createFlag(undefined, DirectiveDismantle.color, DirectiveDismantle.secondaryColor); + } + } } // Remove if owned by other player diff --git a/src/memory/Memory.ts b/src/memory/Memory.ts index 67c9f751b..441806e6d 100644 --- a/src/memory/Memory.ts +++ b/src/memory/Memory.ts @@ -197,6 +197,7 @@ export class Mem { operationMode: DEFAULT_OPERATION_MODE, log : {}, enableVisuals: true, + resourceCollectionMode: 0, }); if (!Memory.stats) { Memory.stats = {}; From c92e6fd5cbe862e6e5a38879177aee9811486442 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 10 Apr 2019 13:03:56 -0700 Subject: [PATCH 005/137] Adding simple collection during room clearing phase --- src/directives/colony/clearRoom.ts | 7 +++++-- src/utilities/utils.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 82bbec80f..b0275ca05 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -4,7 +4,8 @@ import {ClaimingOverlord} from '../../overlords/colonization/claimer'; import {MY_USERNAME} from '../../~settings'; import {log} from '../../console/log'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; -import {printRoomName} from '../../utilities/utils'; +import {hasContents, printRoomName} from '../../utilities/utils'; +import {DirectiveHaul} from "../resource/haul"; /** @@ -52,7 +53,9 @@ export class DirectiveClearRoom extends Directive { for (let s of allStructures) { if (s.structureType == STRUCTURE_CONTROLLER) continue; if (keepStorageStructures && - (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL)) { + (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && hasContents(s.store)) { + // Create a collection flag + let result = s.pos.createFlag(undefined, DirectiveHaul.color, DirectiveHaul.secondaryColor); continue; } if (keepRoads && s.structureType == STRUCTURE_ROAD) { diff --git a/src/utilities/utils.ts b/src/utilities/utils.ts index 33b465b82..47341cd81 100644 --- a/src/utilities/utils.ts +++ b/src/utilities/utils.ts @@ -34,6 +34,15 @@ export function hasMinerals(store: { [resourceType: string]: number }): boolean return false; } +export function hasContents(store: { [resourceType: string]: number }): boolean { + for (let resourceType in store) { + if ((store[resourceType] || 0) > 0) { + return true; + } + } + return false; +} + /** * Obtain the username of the player */ From d7a86028f193dde97ebe79c307f93eb6ff6654c6 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 10 Apr 2019 14:39:56 -0700 Subject: [PATCH 006/137] Adding listSuspended command and adding more to clearRoom --- src/console/Console.ts | 12 ++++++++++++ src/declarations/memory.d.ts | 8 ++++++++ src/directives/colony/clearRoom.ts | 23 +++++++++++++++++++++++ src/memory/Memory.ts | 1 + 4 files changed, 44 insertions(+) diff --git a/src/console/Console.ts b/src/console/Console.ts index 7b5446dce..14a13c2b1 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -26,6 +26,7 @@ export class OvermindConsole { global.setLogLevel = log.setLogLevel; global.suspendColony = this.suspendColony; global.unsuspendColony = this.unsuspendColony; + global.listSuspendedColonies = this.listSuspendedColonies; global.openRoomPlanner = this.openRoomPlanner; global.closeRoomPlanner = this.closeRoomPlanner; global.cancelRoomPlanner = this.cancelRoomPlanner; @@ -244,6 +245,17 @@ export class OvermindConsole { } } + static listSuspendedColonies(): string { + let msg = 'Colonies currently suspended: \n'; + for (let i in Memory.colonies) { + let colonyMemory = Memory.colonies[i] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend == true) { + msg += 'Colony ' + i + ' \n'; + } + } + return msg; + } + // Room planner control ============================================================================================ static openRoomPlanner(roomName: string): string { diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 270b52c4f..b0c2589fa 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -1,4 +1,11 @@ type operationMode = 'manual' | 'semiautomatic' | 'automatic'; +/** + * 0: Basic + * 1: Collect from enemy storage/terminal + * 2: Collect from all sources TBD + * 3: Collect all and mine walls for energy TBD + */ +type resourceCollectionMode = number; interface RawMemory { _parsed: any; @@ -21,6 +28,7 @@ interface Memory { operationMode: operationMode; log: LoggerMemory; enableVisuals: boolean; + resourceCollectionMode: resourceCollectionMode; } profiler?: any; stats: any; diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index b0275ca05..076be1b85 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -6,6 +6,10 @@ import {log} from '../../console/log'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; import {hasContents, printRoomName} from '../../utilities/utils'; import {DirectiveHaul} from "../resource/haul"; +import {WorkerOverlord} from "../../overlords/core/worker"; +import {Zerg} from "../../zerg/Zerg"; +import {Pathing} from "../../movement/Pathing"; +import {DirectiveDismantle} from "../targeting/dismantle"; /** @@ -30,6 +34,9 @@ export class DirectiveClearRoom extends Directive { `removing directive!`); this.remove(true); } + if (Memory.settings.resourceCollectionMode && Memory.settings.resourceCollectionMode >= 1) { + this.memory.keepStorageStructures = true; + } } spawnMoarOverlords() { @@ -86,6 +93,22 @@ export class DirectiveClearRoom extends Directive { log.notify(`Removing clearRoom directive in ${this.pos.roomName}: operation completed.`); this.remove(); } + // Clear path if controller is not reachable + } else if (this.room && this.room.creeps.length > 1) { + let currentlyDismantling = _.find(this.room.flags, function(flag) { + return (flag.color == DirectiveDismantle.color && flag.secondaryColor == DirectiveDismantle.secondaryColor) + }); + + if (!currentlyDismantling) { + let pathablePos = this.room.creeps[0] ? this.room.creeps[0].pos + : Pathing.findPathablePosition(this.room.name); + let blockingLocation = Pathing.findBlockingPos(pathablePos, this.room.controller!.pos, + _.filter(this.room.structures, s => !s.isWalkable)); + if (blockingLocation && blockingLocation.lookFor(LOOK_FLAGS).length <= 0) { + log.notify(`Adding dismantle directive for ${this.pos.roomName} to reach controller.`); + blockingLocation!.createFlag(undefined, DirectiveDismantle.color, DirectiveDismantle.secondaryColor); + } + } } // Remove if owned by other player diff --git a/src/memory/Memory.ts b/src/memory/Memory.ts index 67c9f751b..441806e6d 100644 --- a/src/memory/Memory.ts +++ b/src/memory/Memory.ts @@ -197,6 +197,7 @@ export class Mem { operationMode: DEFAULT_OPERATION_MODE, log : {}, enableVisuals: true, + resourceCollectionMode: 0, }); if (!Memory.stats) { Memory.stats = {}; From b856ce5d57418a1b511214ecc105872d4247a7e8 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 10 Apr 2019 16:11:51 -0700 Subject: [PATCH 007/137] Adding an upgrade rate increase --- src/console/Console.ts | 10 ++++++++++ src/hiveClusters/upgradeSite.ts | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/console/Console.ts b/src/console/Console.ts index 7b5446dce..d919843bb 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -44,6 +44,7 @@ export class OvermindConsole { global.endRemoteDebugSession = this.endRemoteDebugSession; global.profileMemory = this.profileMemory; global.cancelMarketOrders = this.cancelMarketOrders; + global.setRoomUpgradeRate = this.setRoomUpgradeRate; } // Help, information, and operational changes ====================================================================== @@ -413,6 +414,15 @@ export class OvermindConsole { return `Destroyed ${room.barriers.length} barriers.`; } + // Colony Management ================================================================================================= + + static setRoomUpgradeRate(roomName: string, rate: number): string { + let colony: Colony = Overmind.colonies[roomName]; + colony.upgradeSite.memory.speedFactor = rate; + + return `Colony ${roomName} is now upgrading at a rate of ${rate} barriers.`; + } + // Memory management =============================================================================================== diff --git a/src/hiveClusters/upgradeSite.ts b/src/hiveClusters/upgradeSite.ts index ac9eaf604..d3d5b434d 100644 --- a/src/hiveClusters/upgradeSite.ts +++ b/src/hiveClusters/upgradeSite.ts @@ -10,6 +10,7 @@ import {$} from '../caching/GlobalCache'; interface UpgradeSiteMemory { stats: { downtime: number }; + speedFactor : number; // Multiplier on upgrade parts for fast growth } @@ -101,6 +102,9 @@ export class UpgradeSite extends HiveCluster { } if (this.controller.level == 8) { upgradePower = Math.min(upgradePower, 15); // don't go above 15 work parts at RCL 8 + } else if (this.controller.level >= 6) { + // Can set a room to upgrade at an accelerated rate manually + upgradePower = this.memory.speedFactor != undefined ? upgradePower*this.memory.speedFactor : upgradePower; } return upgradePower; } else { From d6bc898624bfb956e8bc808bac5b99bb94ccf725 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 9 Apr 2019 11:57:54 -0700 Subject: [PATCH 008/137] Adding error check on controller sign length --- src/console/Console.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/console/Console.ts b/src/console/Console.ts index d919843bb..eef1e04a9 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -144,7 +144,9 @@ export class OvermindConsole { static setSignature(signature: string | undefined): string | undefined { let sig = signature ? signature : DEFAULT_OVERMIND_SIGNATURE; - if (sig.toLowerCase().includes('overmind') || sig.includes(DEFAULT_OVERMIND_SIGNATURE)) { + if (sig.length > 100) { + throw new Error(`Invalid signature: ${signature}; length is over 100 chars.`); + } else if (sig.toLowerCase().includes('overmind') || sig.includes(DEFAULT_OVERMIND_SIGNATURE)) { Memory.settings.signature = sig; return `Controller signature set to ${sig}`; } else { From 238a73df7e38725df49258ef38d31d8080a2fee0 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 9 Apr 2019 13:03:40 -0700 Subject: [PATCH 009/137] Adding blocked flag --- src/Overseer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Overseer.ts b/src/Overseer.ts index fd688b4aa..8f5b16f5d 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -244,8 +244,12 @@ export class Overseer implements IOverseer { } let alreadyOwned = RoomIntel.roomOwnedBy(roomName); let alreadyReserved = RoomIntel.roomReservedBy(roomName); + let isBlocked = Game.flags[roomName+"-Blocked"] != null; + if (isBlocked) { + Game.notify("Room " + roomName + " is blocked, not expanding there."); + } let disregardReservations = !onPublicServer() || MY_USERNAME == MUON; - if (alreadyOwned || (alreadyReserved && !disregardReservations)) { + if (alreadyOwned || (alreadyReserved && !disregardReservations) || isBlocked) { return false; } let neighboringRooms = _.values(Game.map.describeExits(roomName)) as string[]; From e1f21e57b9dafa1cb67627f71c9dc532bbbe7ff4 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 10 Apr 2019 13:03:56 -0700 Subject: [PATCH 010/137] Adding simple collection during room clearing phase --- src/directives/colony/clearRoom.ts | 7 +++++-- src/utilities/utils.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 82bbec80f..b0275ca05 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -4,7 +4,8 @@ import {ClaimingOverlord} from '../../overlords/colonization/claimer'; import {MY_USERNAME} from '../../~settings'; import {log} from '../../console/log'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; -import {printRoomName} from '../../utilities/utils'; +import {hasContents, printRoomName} from '../../utilities/utils'; +import {DirectiveHaul} from "../resource/haul"; /** @@ -52,7 +53,9 @@ export class DirectiveClearRoom extends Directive { for (let s of allStructures) { if (s.structureType == STRUCTURE_CONTROLLER) continue; if (keepStorageStructures && - (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL)) { + (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && hasContents(s.store)) { + // Create a collection flag + let result = s.pos.createFlag(undefined, DirectiveHaul.color, DirectiveHaul.secondaryColor); continue; } if (keepRoads && s.structureType == STRUCTURE_ROAD) { diff --git a/src/utilities/utils.ts b/src/utilities/utils.ts index 33b465b82..47341cd81 100644 --- a/src/utilities/utils.ts +++ b/src/utilities/utils.ts @@ -34,6 +34,15 @@ export function hasMinerals(store: { [resourceType: string]: number }): boolean return false; } +export function hasContents(store: { [resourceType: string]: number }): boolean { + for (let resourceType in store) { + if ((store[resourceType] || 0) > 0) { + return true; + } + } + return false; +} + /** * Obtain the username of the player */ From dacaac5b7e298d28c0fb33f1bf36eaec97913041 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 10 Apr 2019 14:39:56 -0700 Subject: [PATCH 011/137] Adding listSuspended command and adding more to clearRoom --- src/Overseer.ts | 2 +- src/console/Console.ts | 12 ++++++++++ src/declarations/memory.d.ts | 8 +++++++ src/directives/colony/clearRoom.ts | 36 ++++++++++++++++++++++++++++++ src/memory/Memory.ts | 1 + 5 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Overseer.ts b/src/Overseer.ts index 8f5b16f5d..e2550b73f 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -244,7 +244,7 @@ export class Overseer implements IOverseer { } let alreadyOwned = RoomIntel.roomOwnedBy(roomName); let alreadyReserved = RoomIntel.roomReservedBy(roomName); - let isBlocked = Game.flags[roomName+"-Blocked"] != null; + let isBlocked = Game.flags[roomName+"-Block"] != null; if (isBlocked) { Game.notify("Room " + roomName + " is blocked, not expanding there."); } diff --git a/src/console/Console.ts b/src/console/Console.ts index eef1e04a9..9c5889c4a 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -26,6 +26,7 @@ export class OvermindConsole { global.setLogLevel = log.setLogLevel; global.suspendColony = this.suspendColony; global.unsuspendColony = this.unsuspendColony; + global.listSuspendedColonies = this.listSuspendedColonies; global.openRoomPlanner = this.openRoomPlanner; global.closeRoomPlanner = this.closeRoomPlanner; global.cancelRoomPlanner = this.cancelRoomPlanner; @@ -247,6 +248,17 @@ export class OvermindConsole { } } + static listSuspendedColonies(): string { + let msg = 'Colonies currently suspended: \n'; + for (let i in Memory.colonies) { + let colonyMemory = Memory.colonies[i] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend == true) { + msg += 'Colony ' + i + ' \n'; + } + } + return msg; + } + // Room planner control ============================================================================================ static openRoomPlanner(roomName: string): string { diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 270b52c4f..b0c2589fa 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -1,4 +1,11 @@ type operationMode = 'manual' | 'semiautomatic' | 'automatic'; +/** + * 0: Basic + * 1: Collect from enemy storage/terminal + * 2: Collect from all sources TBD + * 3: Collect all and mine walls for energy TBD + */ +type resourceCollectionMode = number; interface RawMemory { _parsed: any; @@ -21,6 +28,7 @@ interface Memory { operationMode: operationMode; log: LoggerMemory; enableVisuals: boolean; + resourceCollectionMode: resourceCollectionMode; } profiler?: any; stats: any; diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index b0275ca05..3e300bdb4 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -6,6 +6,10 @@ import {log} from '../../console/log'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; import {hasContents, printRoomName} from '../../utilities/utils'; import {DirectiveHaul} from "../resource/haul"; +import {WorkerOverlord} from "../../overlords/core/worker"; +import {Zerg} from "../../zerg/Zerg"; +import {Pathing} from "../../movement/Pathing"; +import {DirectiveDismantle} from "../targeting/dismantle"; /** @@ -30,6 +34,9 @@ export class DirectiveClearRoom extends Directive { `removing directive!`); this.remove(true); } + if (Memory.settings.resourceCollectionMode && Memory.settings.resourceCollectionMode >= 1) { + this.memory.keepStorageStructures = true; + } } spawnMoarOverlords() { @@ -77,6 +84,19 @@ export class DirectiveClearRoom extends Directive { } + private findStructureBlockingController(pioneer: Zerg): Structure | undefined { + let blockingPos = Pathing.findBlockingPos(pioneer.pos, pioneer.room.controller!.pos, + _.filter(pioneer.room.structures, s => !s.isWalkable)); + if (blockingPos) { + let structure = blockingPos.lookFor(LOOK_STRUCTURES)[0]; + if (structure) { + return structure; + } else { + log.error(`${this.print}: no structure at blocking pos ${blockingPos.print}! (Why?)`); + } + } + } + run() { // Remove if structures are done if (this.room && this.room.my) { @@ -86,6 +106,22 @@ export class DirectiveClearRoom extends Directive { log.notify(`Removing clearRoom directive in ${this.pos.roomName}: operation completed.`); this.remove(); } + // Clear path if controller is not reachable + } else if (this.room && this.room.creeps.length > 1) { + let currentlyDismantling = _.find(this.room.flags, function(flag) { + return (flag.color == DirectiveDismantle.color && flag.secondaryColor == DirectiveDismantle.secondaryColor) + }); + + if (!currentlyDismantling) { + let pathablePos = this.room.creeps[0] ? this.room.creeps[0].pos + : Pathing.findPathablePosition(this.room.name); + let blockingLocation = Pathing.findBlockingPos(pathablePos, this.room.controller!.pos, + _.filter(this.room.structures, s => !s.isWalkable)); + if (blockingLocation && blockingLocation.lookFor(LOOK_FLAGS).length <= 0) { + log.notify(`Adding dismantle directive for ${this.pos.roomName} to reach controller.`); + blockingLocation!.createFlag(undefined, DirectiveDismantle.color, DirectiveDismantle.secondaryColor); + } + } } // Remove if owned by other player diff --git a/src/memory/Memory.ts b/src/memory/Memory.ts index 67c9f751b..441806e6d 100644 --- a/src/memory/Memory.ts +++ b/src/memory/Memory.ts @@ -197,6 +197,7 @@ export class Mem { operationMode: DEFAULT_OPERATION_MODE, log : {}, enableVisuals: true, + resourceCollectionMode: 0, }); if (!Memory.stats) { Memory.stats = {}; From ec921b7e18f2c6b170024a9be9e458dcbfe5da08 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 16 Apr 2019 12:16:19 -0700 Subject: [PATCH 012/137] Initial power mining commit --- src/creepSetups/setups.ts | 17 ++++ src/directives/initializer.ts | 3 + src/directives/resource/powerMine.ts | 107 +++++++++++++++++++++++ src/overlords/powerMining/PowerDrill.ts | 108 ++++++++++++++++++++++++ src/priorities/priorities_overlords.ts | 7 ++ 5 files changed, 242 insertions(+) create mode 100644 src/directives/resource/powerMine.ts create mode 100644 src/overlords/powerMining/PowerDrill.ts diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 99337f91d..7668622cf 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -22,6 +22,8 @@ export const Roles = { ranged : 'hydralisk', healer : 'transfuser', dismantler: 'lurker', + drill : 'drill', + coolant : 'coolant', }; /** @@ -307,4 +309,19 @@ export const CombatSetups = { }, + drill: { + default: new CreepSetup(Roles.drill, { + pattern : [MOVE, ATTACK, ATTACK, ATTACK, ATTACK, MOVE], + sizeLimit: Infinity, + }), + }, + + coolant: { + default: new CreepSetup(Roles.coolant, { + pattern : [HEAL, HEAL, MOVE], + sizeLimit: 11, + }), + } + + }; diff --git a/src/directives/initializer.ts b/src/directives/initializer.ts index 7b7637ce1..0a0a1e052 100644 --- a/src/directives/initializer.ts +++ b/src/directives/initializer.ts @@ -25,6 +25,7 @@ import {DirectiveExtract} from './resource/extract'; import {DirectiveSwarmDestroy} from './offense/swarmDestroy'; import {DirectiveOutpostDefense} from './defense/outpostDefense'; import {DirectiveClearRoom} from './colony/clearRoom'; +import {DirectivePowerMine} from "./resource/powerMine"; /** * This is the initializer for directives, which maps flags by their color code to the corresponding directive @@ -92,6 +93,8 @@ export function DirectiveWrapper(flag: Flag): Directive | undefined { return new DirectiveExtract(flag); case COLOR_BLUE: return new DirectiveHaul(flag); + case COLOR_RED: + return new DirectivePowerMine(flag); } break; diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts new file mode 100644 index 000000000..e6d3f9672 --- /dev/null +++ b/src/directives/resource/powerMine.ts @@ -0,0 +1,107 @@ +import {Directive} from '../Directive'; +import {profile} from '../../profiler/decorator'; +import {isStoreStructure} from '../../declarations/typeGuards'; +import {PowerDrillOverlord} from '../../overlords/powerMining/PowerDrill'; + + +interface DirectivePowerMineMemory extends FlagMemory { + totalResources?: number; +} + + +/** + * PowerMining directive: kills power banks and collects the resources. + */ +@profile +export class DirectivePowerMine extends Directive { + + static directiveName = 'powerMine'; + static color = COLOR_YELLOW; + static secondaryColor = COLOR_RED; + + private _powerBank: StructurePowerBank | undefined; + + private _store: StoreDefinition; + private _drops: { [resourceType: string]: Resource[] }; + + memory: DirectivePowerMineMemory; + + constructor(flag: Flag) { + super(flag); + } + + spawnMoarOverlords() { + this.overlords.powerMine = new PowerDrillOverlord(this); + } + + get targetedBy(): string[] { + return Overmind.cache.targets[this.ref]; + } + + get drops(): { [resourceType: string]: Resource[] } { + if (!this.pos.isVisible) { + return {}; + } + if (!this._drops) { + let drops = (this.pos.lookFor(LOOK_RESOURCES) || []) as Resource[]; + this._drops = _.groupBy(drops, drop => drop.resourceType); + } + return this._drops; + } + + get hasDrops(): boolean { + return _.keys(this.drops).length > 0; + } + + // get store(): StoreDefinition { + // if (!this._store) { + // // Merge the "storage" of drops with the store of structure + // let store: { [resourceType: string]: number } = {}; + // if (this.storeStructure) { + // if (isStoreStructure(this.storeStructure)) { + // store = this.storeStructure.store; + // } else { + // store = {'energy': this.storeStructure.energy}; + // } + // } else { + // store = {'energy': 0}; + // } + // // Merge with drops + // for (let resourceType of _.keys(this.drops)) { + // let totalResourceAmount = _.sum(this.drops[resourceType], drop => drop.amount); + // if (store[resourceType]) { + // store[resourceType] += totalResourceAmount; + // } else { + // store[resourceType] = totalResourceAmount; + // } + // } + // this._store = store as StoreDefinition; + // } + // return this._store; + // } + + /** + * Total amount of resources remaining to be transported; cached into memory in case room loses visibility + */ + get totalResources(): number { + if (this.memory.totalResources == undefined) { + return 5000; // pick some non-zero number so that powerMiners will spawn + } + if (this.pos.isVisible) { + this.memory.totalResources = this._powerBank ? this._powerBank.power : this.memory.totalResources; // update total amount remaining + } + return this.memory.totalResources; + } + + init(): void { + this.alert(`PowerMine directive active`); + } + + run(): void { + // if (_.sum(this.store) == 0 && this.pos.isVisible) { + // //this.remove(); + // } + } + +} + diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts new file mode 100644 index 000000000..c6ab930a4 --- /dev/null +++ b/src/overlords/powerMining/PowerDrill.ts @@ -0,0 +1,108 @@ +import {CombatZerg} from '../../zerg/CombatZerg'; +import {DirectiveSKOutpost} from '../../directives/colony/outpostSK'; +import {RoomIntel} from '../../intel/RoomIntel'; +import {minBy} from '../../utilities/utils'; +import {Mem} from '../../memory/Memory'; +import {debug, log} from '../../console/log'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {Visualizer} from '../../visuals/Visualizer'; +import {profile} from '../../profiler/decorator'; +import {CombatOverlord} from '../CombatOverlord'; +import {CombatSetups, Roles} from '../../creepSetups/setups'; +import {OverlordMemory} from '../Overlord'; +import {DirectivePowerMine} from "../../directives/resource/powerMine"; + +interface PowerDrillOverlordMemory extends OverlordMemory { + targetPBID?: string; +} + +/** + * PowerDrillOverlord -- spawns drills and coolant to mine power banks + */ +@profile +export class PowerDrillOverlord extends CombatOverlord { + + static requiredRCL = 7; + + directive: DirectiveSKOutpost; + memory: PowerDrillOverlordMemory; + targetPowerBank: StructurePowerBank | undefined; + + drills: CombatZerg[]; + coolant: CombatZerg[]; + + constructor(directive: DirectivePowerMine, priority = OverlordPriority.powerMine.drill) { + super(directive, 'powerDrill', priority, PowerDrillOverlord.requiredRCL); + this.directive = directive; + this.priority += this.outpostIndex * OverlordPriority.powerMine.roomIncrement; + this.drills = this.combatZerg(Roles.drill); + this.coolant = this.combatZerg(Roles.coolant); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + } + + refresh() { + super.refresh(); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + } + + init() { + this.wishlist(2, CombatSetups.coolant.default); + this.wishlist(1, CombatSetups.drill.default); + } + + private handleDrill(drill: CombatZerg) { + + // Go to keeper room + if (!this.room || drill.room != this.room || drill.pos.isEdge) { + // log.debugCreep(reaper, `Going to room!`); + //drill.healSelfIfPossible(); + Game.notify("Drill is moving to power site in " + this.room + "."); + drill.goTo(this.pos); + return; + } else if (!this.targetPowerBank) { + // If power bank is dead + Game.notify("Power bank in " + this.room + " is dead."); + return; + } else if (this.targetPowerBank.hits < 50000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + // Spawn a hauler for this location + // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep + } + + // Handle killing bank + if (drill.hits > 100) { + drill.attack(this.targetPowerBank); + } + } + + private handleCoolant(coolant: CombatZerg) { + + // Go to keeper room + if (!this.room || coolant.room != this.room || coolant.pos.isEdge) { + // log.debugCreep(reaper, `Going to room!`); + coolant.healSelfIfPossible(); + coolant.goTo(this.pos); + return; + } else if (!this.targetPowerBank) { + // If power bank is dead + Game.notify("Power bank in " + this.room + " is dead."); + return; + } else if (this.targetPowerBank.hits < 50000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + } + + coolant.autoHeal(); + } + + run() { + this.autoRun(this.drills, drill => this.handleDrill(drill)); + this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); + } + + visuals() { + if (this.room && this.targetPowerBank) { + Visualizer.marker(this.targetPowerBank.pos); + } + } + +} diff --git a/src/priorities/priorities_overlords.ts b/src/priorities/priorities_overlords.ts index 356b7e676..71d38c399 100644 --- a/src/priorities/priorities_overlords.ts +++ b/src/priorities/priorities_overlords.ts @@ -72,6 +72,13 @@ export var OverlordPriority = { roomIncrement: 5, }, + powerMine: { + cool : 1050, + drill : 1051, + gather : 604, + roomIncrement: 5, + }, + collection: { // Non-urgent collection of resources, like from a deserted storage haul: 1100 }, From 203cb31b5fdf913f978b40aba18fbd609b75744b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 15 Apr 2019 16:08:31 -0700 Subject: [PATCH 013/137] Adding task to recover terminal resources on founding new room --- src/roomPlanner/RoomPlanner.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/roomPlanner/RoomPlanner.ts b/src/roomPlanner/RoomPlanner.ts index 5da538e23..609334db0 100644 --- a/src/roomPlanner/RoomPlanner.ts +++ b/src/roomPlanner/RoomPlanner.ts @@ -10,12 +10,13 @@ import {BarrierPlanner} from './BarrierPlanner'; import {BuildPriorities, DemolishStructurePriorities} from '../priorities/priorities_structures'; import {bunkerLayout} from './layouts/bunker'; import {DirectiveTerminalRebuildState} from '../directives/terminalState/terminalState_rebuild'; -import {derefCoords, maxBy, onPublicServer} from '../utilities/utils'; +import {derefCoords, hasMinerals, maxBy, onPublicServer} from '../utilities/utils'; import {bullet} from '../utilities/stringConstants'; import {Pathing} from '../movement/Pathing'; import {isOwnedStructure} from '../declarations/typeGuards'; import {MY_USERNAME} from '../~settings'; import {Energetics} from '../logistics/Energetics'; +import {DirectiveHaul} from "../directives/resource/haul"; export interface BuildingPlannerOutput { name: string; @@ -598,6 +599,11 @@ export class RoomPlanner { && (structureType == STRUCTURE_STORAGE || structureType == STRUCTURE_TERMINAL)) { break; // don't destroy terminal or storage when under RCL4 - can use energy inside } + if (this.colony.level < 6 + && structureType == STRUCTURE_TERMINAL && hasMinerals(( structure).store)) { + DirectiveHaul.create(structure.pos); + break; // don't destroy terminal when under RCL6 if there are resources available. + } if (structureType != STRUCTURE_WALL && structureType != STRUCTURE_RAMPART) { this.memory.relocating = true; } From d457f726023d4bf7aacf07a7f94bc01e685aa213 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 16 Apr 2019 12:16:19 -0700 Subject: [PATCH 014/137] Initial power mining commit --- src/creepSetups/setups.ts | 17 ++++ src/directives/resource/powerMine.ts | 113 ++++++++++++++++++++++++ src/overlords/powerMining/PowerDrill.ts | 108 ++++++++++++++++++++++ src/priorities/priorities_overlords.ts | 7 ++ 4 files changed, 245 insertions(+) create mode 100644 src/directives/resource/powerMine.ts create mode 100644 src/overlords/powerMining/PowerDrill.ts diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 99337f91d..7668622cf 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -22,6 +22,8 @@ export const Roles = { ranged : 'hydralisk', healer : 'transfuser', dismantler: 'lurker', + drill : 'drill', + coolant : 'coolant', }; /** @@ -307,4 +309,19 @@ export const CombatSetups = { }, + drill: { + default: new CreepSetup(Roles.drill, { + pattern : [MOVE, ATTACK, ATTACK, ATTACK, ATTACK, MOVE], + sizeLimit: Infinity, + }), + }, + + coolant: { + default: new CreepSetup(Roles.coolant, { + pattern : [HEAL, HEAL, MOVE], + sizeLimit: 11, + }), + } + + }; diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts new file mode 100644 index 000000000..5eb9053d4 --- /dev/null +++ b/src/directives/resource/powerMine.ts @@ -0,0 +1,113 @@ +import {Directive} from '../Directive'; +import {profile} from '../../profiler/decorator'; +import {isStoreStructure} from '../../declarations/typeGuards'; +import {PowerMiningOverlord} from '../../overlords/situational/powerMiner'; + + +interface DirectivePowerMineMemory extends FlagMemory { + totalResources?: number; +} + + +/** + * PowerMining directive: kills power banks and collects the resources. + */ +@profile +export class DirectivePowerMine extends Directive { + + static directiveName = 'powerMine'; + static color = COLOR_YELLOW; + static secondaryColor = COLOR_RED; + + private _store: StoreDefinition; + private _drops: { [resourceType: string]: Resource[] }; + + memory: DirectivePowerMineMemory; + + constructor(flag: Flag) { + super(flag); + } + + spawnMoarOverlords() { + this.overlords.powerMine = new PowerMiningOverlord(this); + } + + get targetedBy(): string[] { + return Overmind.cache.targets[this.ref]; + } + + get drops(): { [resourceType: string]: Resource[] } { + if (!this.pos.isVisible) { + return {}; + } + if (!this._drops) { + let drops = (this.pos.lookFor(LOOK_RESOURCES) || []) as Resource[]; + this._drops = _.groupBy(drops, drop => drop.resourceType); + } + return this._drops; + } + + get hasDrops(): boolean { + return _.keys(this.drops).length > 0; + } + + get storeStructure(): StructurePowerBank | undefined { + if (this.pos.isVisible) { + return this.pos.lookForStructure(StructurePowerBank); + } + return undefined; + } + + get store(): StoreDefinition { + if (!this._store) { + // Merge the "storage" of drops with the store of structure + let store: { [resourceType: string]: number } = {}; + if (this.storeStructure) { + if (isStoreStructure(this.storeStructure)) { + store = this.storeStructure.store; + } else { + store = {'energy': this.storeStructure.energy}; + } + } else { + store = {'energy': 0}; + } + // Merge with drops + for (let resourceType of _.keys(this.drops)) { + let totalResourceAmount = _.sum(this.drops[resourceType], drop => drop.amount); + if (store[resourceType]) { + store[resourceType] += totalResourceAmount; + } else { + store[resourceType] = totalResourceAmount; + } + } + this._store = store as StoreDefinition; + } + return this._store; + } + + /** + * Total amount of resources remaining to be transported; cached into memory in case room loses visibility + */ + get totalResources(): number { + if (this.pos.isVisible) { + this.memory.totalResources = _.sum(this.store); // update total amount remaining + } else { + if (this.memory.totalResources == undefined) { + return 1000; // pick some non-zero number so that powerMiners will spawn + } + } + return this.memory.totalResources; + } + + init(): void { + this.alert(`PowerMine directive active`); + } + + run(): void { + if (_.sum(this.store) == 0 && this.pos.isVisible) { + this.remove(); + } + } + +} + diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts new file mode 100644 index 000000000..c6ab930a4 --- /dev/null +++ b/src/overlords/powerMining/PowerDrill.ts @@ -0,0 +1,108 @@ +import {CombatZerg} from '../../zerg/CombatZerg'; +import {DirectiveSKOutpost} from '../../directives/colony/outpostSK'; +import {RoomIntel} from '../../intel/RoomIntel'; +import {minBy} from '../../utilities/utils'; +import {Mem} from '../../memory/Memory'; +import {debug, log} from '../../console/log'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {Visualizer} from '../../visuals/Visualizer'; +import {profile} from '../../profiler/decorator'; +import {CombatOverlord} from '../CombatOverlord'; +import {CombatSetups, Roles} from '../../creepSetups/setups'; +import {OverlordMemory} from '../Overlord'; +import {DirectivePowerMine} from "../../directives/resource/powerMine"; + +interface PowerDrillOverlordMemory extends OverlordMemory { + targetPBID?: string; +} + +/** + * PowerDrillOverlord -- spawns drills and coolant to mine power banks + */ +@profile +export class PowerDrillOverlord extends CombatOverlord { + + static requiredRCL = 7; + + directive: DirectiveSKOutpost; + memory: PowerDrillOverlordMemory; + targetPowerBank: StructurePowerBank | undefined; + + drills: CombatZerg[]; + coolant: CombatZerg[]; + + constructor(directive: DirectivePowerMine, priority = OverlordPriority.powerMine.drill) { + super(directive, 'powerDrill', priority, PowerDrillOverlord.requiredRCL); + this.directive = directive; + this.priority += this.outpostIndex * OverlordPriority.powerMine.roomIncrement; + this.drills = this.combatZerg(Roles.drill); + this.coolant = this.combatZerg(Roles.coolant); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + } + + refresh() { + super.refresh(); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + } + + init() { + this.wishlist(2, CombatSetups.coolant.default); + this.wishlist(1, CombatSetups.drill.default); + } + + private handleDrill(drill: CombatZerg) { + + // Go to keeper room + if (!this.room || drill.room != this.room || drill.pos.isEdge) { + // log.debugCreep(reaper, `Going to room!`); + //drill.healSelfIfPossible(); + Game.notify("Drill is moving to power site in " + this.room + "."); + drill.goTo(this.pos); + return; + } else if (!this.targetPowerBank) { + // If power bank is dead + Game.notify("Power bank in " + this.room + " is dead."); + return; + } else if (this.targetPowerBank.hits < 50000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + // Spawn a hauler for this location + // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep + } + + // Handle killing bank + if (drill.hits > 100) { + drill.attack(this.targetPowerBank); + } + } + + private handleCoolant(coolant: CombatZerg) { + + // Go to keeper room + if (!this.room || coolant.room != this.room || coolant.pos.isEdge) { + // log.debugCreep(reaper, `Going to room!`); + coolant.healSelfIfPossible(); + coolant.goTo(this.pos); + return; + } else if (!this.targetPowerBank) { + // If power bank is dead + Game.notify("Power bank in " + this.room + " is dead."); + return; + } else if (this.targetPowerBank.hits < 50000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + } + + coolant.autoHeal(); + } + + run() { + this.autoRun(this.drills, drill => this.handleDrill(drill)); + this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); + } + + visuals() { + if (this.room && this.targetPowerBank) { + Visualizer.marker(this.targetPowerBank.pos); + } + } + +} diff --git a/src/priorities/priorities_overlords.ts b/src/priorities/priorities_overlords.ts index 356b7e676..71d38c399 100644 --- a/src/priorities/priorities_overlords.ts +++ b/src/priorities/priorities_overlords.ts @@ -72,6 +72,13 @@ export var OverlordPriority = { roomIncrement: 5, }, + powerMine: { + cool : 1050, + drill : 1051, + gather : 604, + roomIncrement: 5, + }, + collection: { // Non-urgent collection of resources, like from a deserted storage haul: 1100 }, From 6875ebe13fb17e6aef4a209c326b7acdf343581d Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 16 Apr 2019 20:31:42 -0700 Subject: [PATCH 015/137] Adding more functionality to power mining --- src/Overmind_ts.ts | 590 ++++++++++++++++++++++++ src/creepSetups/setups.ts | 6 +- src/directives/resource/haul.ts | 6 +- src/main.ts | 2 +- src/overlords/powerMining/PowerDrill.ts | 44 +- 5 files changed, 629 insertions(+), 19 deletions(-) create mode 100644 src/Overmind_ts.ts diff --git a/src/Overmind_ts.ts b/src/Overmind_ts.ts new file mode 100644 index 000000000..ad3762532 --- /dev/null +++ b/src/Overmind_ts.ts @@ -0,0 +1,590 @@ +// javascript-obfuscator:disable +// +// Overmind_obfuscated.js: this file is intentially obfuscated to prevent tampering. +// +// Q: Why is this file obfuscated? +// +// A: Using Overmind as your AI puts you at a huge advantage if you are a new player in a novice zone. Screeps has +// always had problems with people downloading bots from the internet and stomping new players. I have kept Overmind +// open-sourced because I think it can be a good resource for learning how to play Screeps, but I don't want it to +// be abused as a noob-crushing machine. In the future, I will be implementing behavioral locks in this file which +// limit unreasonable aggression toward peaceful new players. +// +// Q: What kind of behavioral locks? +// +// A: Players will be able to opt out of aggression by setting a property in their public memory. Overmind bots will not +// attack the player unless they attack you, claim a room in Overmind's territory, or occupy a room which is +// critically important (for example, very desirable mineral deposits that aren't available elsewhere). Overmind +// will attempt to expand around players which pose no threat to it rather than eliminating them. +// +// Q: What does this file do? +// +// A: The Overmind object is the top-level initializer of the AI and instantiates all colonies and directives. It is +// also responsible for some high-level decision making. You can see the enumerated properties of the Overmind class +// in IOvermind in declarations/index.d.ts. Since this file is sufficiently complex and is critical for the AI to be +// able to run, it was a natural choice of location to put code which should be tamper-resistant. +// +// Q: What happens if I modify this code? +// +// A: This code is self-defending, so any modification to it will likely break the script. +// +// Q: I would like to view the original source code for this file. +// +// A: If you have a compelling reason that you'd like to see the non-obfuscated source for this file, message me in +// game, on slack, or send me an email at benbartlett@stanford.edu. +// + +import {Colony, ColonyMemory, getAllColonies} from './Colony'; +import {DirectiveWrapper} from './directives/initializer'; +import {profile} from './profiler/decorator'; +import {GameCache} from './caching/GameCache'; +import {Overlord} from './overlords/Overlord'; +import {Visualizer} from './visuals/Visualizer'; +import {Stats} from './stats/stats'; +import {TerminalNetwork} from './logistics/TerminalNetwork'; +import {AllContracts} from './contracts/contractsList'; +import {Autonomy, getAutonomyLevel, Mem} from './memory/Memory'; +import {asciiLogoSmall} from './visuals/logos'; +import {log} from './console/log'; +import {TraderJoe} from './logistics/TradeNetwork'; +import {RoomIntel} from './intel/RoomIntel'; +import { + DEFAULT_OVERMIND_SIGNATURE, + MUON, + MY_USERNAME, + NEW_OVERMIND_INTERVAL, + PROFILER_COLONY_LIMIT, + PROFILER_INCLUDE_COLONIES, + USE_PROFILER, + USE_TRY_CATCH +} from './~settings'; +import {Strategist} from './strategy/Strategist'; +import {assimilationLocked} from './assimilation/decorator'; +import {SpawnGroup} from './logistics/SpawnGroup'; +import {alignedNewline} from './utilities/stringConstants'; +import {bulleted} from './utilities/utils'; +import {Directive} from './directives/Directive'; +import {Zerg} from './zerg/Zerg'; +import {Segmenter} from './memory/Segmenter'; +import {Overseer} from './Overseer'; +import {NotifierPriority} from './directives/Notifier'; + +// javascript-obfuscator:enable + +let profilerRooms: { [roomName: string]: boolean } = {}; +if (USE_PROFILER) { + for (let name of PROFILER_INCLUDE_COLONIES) { + profilerRooms[name] = true; + } + let myRoomNames = _.filter(_.keys(Game.rooms), name => Game.rooms[name] && Game.rooms[name].my); + for (let name of _.sample(myRoomNames, PROFILER_COLONY_LIMIT - PROFILER_INCLUDE_COLONIES.length)) { + profilerRooms[name] = true; + } +} + +@profile +@assimilationLocked +export default class _Overmind implements IOvermind { + + memory: IOvermindMemory; + overseer: Overseer; + shouldBuild: boolean; + expiration: number; + cache: GameCache; + colonies: { [roomName: string]: Colony }; // Global hash of all colony objects + directives: { [flagName: string]: Directive }; + zerg: { [creepName: string]: Zerg }; + overlords: { [ref: string]: Overlord }; + spawnGroups: { [ref: string]: SpawnGroup }; + colonyMap: { [roomName: string]: string }; // Global map of colony associations for possibly-null rooms + terminalNetwork: TerminalNetwork; + tradeNetwork: TraderJoe; + strategist: Strategist | undefined; + exceptions: Error[]; + + constructor() { + this.memory = Memory.Overmind as IOvermindMemory; + this.overseer = new Overseer(); + this.shouldBuild = true; + this.expiration = Game.time + NEW_OVERMIND_INTERVAL; + this.cache = new GameCache(); + this.colonies = {}; + this.directives = {}; + this.zerg = {}; + this.overlords = {}; + this.spawnGroups = {}; + this.colonyMap = {}; + this.terminalNetwork = this.makeTerminalNetwork(); + this.tradeNetwork = new TraderJoe(); + this.strategist = getAutonomyLevel() > Autonomy.Manual ? new Strategist() : undefined; + this.exceptions = []; + } + + /* Global instantiation of Overmind object; run once every global refresh */ + build(): void { + log.debug(`Rebuilding Overmind object!`); + this.cache.build(); + // Register all colonies and instantiate their overlords + this.registerColonies(); + _.forEach(this.colonies, colony => colony.spawnMoarOverlords()); + // Register directives and instantiate their overlords; must be done AFTER colonies + this.registerDirectives(); + _.forEach(this.directives, directive => directive.spawnMoarOverlords()); + this.shouldBuild = false; + } + + /* Refresh the state of the Overmind; run at the beginning of every tick */ + refresh(): void { + this.shouldBuild = true; // assume refresh will be unsuccessful + // Refresh constructor-phase objects + this.memory = Memory.Overmind as IOvermindMemory; + this.exceptions = []; + this.cache.refresh(); + this.overseer.refresh(); + this.terminalNetwork.refresh(); + this.tradeNetwork.refresh(); + if (this.strategist) this.strategist.refresh(); + // Refresh build-phase objects + this.refreshColonies(); + this.refreshDirectives(); + for (let ref in this.overlords) { + this.overlords[ref].refresh(); + } + for (let ref in this.spawnGroups) { + this.spawnGroups[ref].refresh(); + } + this.shouldBuild = false; // refresh successful + } + + private try(callback: () => any, identifier?: string): void { + if (USE_TRY_CATCH) { + try { + callback(); + } catch (e) { + if (identifier) { + e.name = `Caught unhandled exception at ${'' + callback} (identifier: ${identifier}): \n` + + e.name + '\n' + e.stack; + } else { + e.name = `Caught unhandled exception at ${'' + callback}: \n` + e.name + '\n' + e.stack; + } + this.exceptions.push(e); + } + } else { + callback(); + } + } + + private handleExceptions(): void { + if (this.exceptions.length == 0) { + return; + } else { + log.warning(`Exceptions present this tick! Rebuilding Overmind object in next tick.`); + Memory.stats.persistent.lastErrorTick = Game.time; + this.shouldBuild = true; + this.expiration = Game.time; + if (this.exceptions.length == 1) { + throw _.first(this.exceptions); + } else { + for (let e of this.exceptions) { + log.throw(e); + } + let error = new Error('Multiple exceptions caught this tick!'); + error.stack = _.map(this.exceptions, e => e.name).join('\n'); + throw error; + } + } + } + + private makeTerminalNetwork(): TerminalNetwork { + // Initialize the terminal netowrk + let terminals: StructureTerminal[] = []; + for (let name in Game.rooms) { + if (USE_PROFILER && !profilerRooms[name]) continue; + let room = Game.rooms[name]; + if (room.my && room.controller!.level >= 6 && room.terminal && room.terminal.my) { + terminals.push(room.terminal); + } + } + return new TerminalNetwork(terminals); + } + + + /* Instantiate a new colony for each owned rom */ + private registerColonies(): void { + + let colonyOutposts: { [roomName: string]: string[] } = {}; // key: lead room, values: outposts[] + this.colonyMap = {}; + // Register colonies to their outposts + let flagsByRoom = _.groupBy(this.cache.outpostFlags, flag => flag.memory[_MEM.COLONY]); + for (let name in Game.rooms) { + if (Game.rooms[name].my) { + let colonyMemory = Memory.colonies[name] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + this.overseer.notifier.alert("Colony suspended", name, NotifierPriority.High); + continue; + } + if (Game.rooms[name].flags) + colonyOutposts[name] = _.map(flagsByRoom[name], + flag => flag.memory.setPosition + ? derefRoomPosition(flag.memory.setPosition).roomName + : flag.pos.roomName); + this.colonyMap[name] = name; + } + } + // Register outposts to their colonies + for (let colonyName in colonyOutposts) { + for (let outpostName of colonyOutposts[colonyName]) { + this.colonyMap[outpostName] = colonyName; + } + } + + // Initialize the Colonies and pass each one its assigned creeps + let id = 0; + for (let name in colonyOutposts) { + if (USE_PROFILER && !profilerRooms[name]) { + if (Game.time % 20 == 0) { + log.alert(`Suppressing instantiation of colony ${name}.`); + } + continue; // don't make colony for this room + } + try { + this.colonies[name] = new Colony(id, name, colonyOutposts[name]); + } catch (e) { + e.name = `Caught unhandled exception instantiating colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + id++; + } + } + + private refreshColonies(): void { + for (let name in this.colonies) { + try { + this.colonies[name].refresh(); + } catch (e) { + e.name = `Caught unhandled exception refreshing colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + } + } + + /* Wrap each flag in a color coded wrapper */ + private registerDirectives(spawnOverlords = false): void { + // Create a directive for each flag (registration takes place on construction) + for (let name in Game.flags) { + if (this.directives[name]) { // skip existing directives + continue; + } + let colonyName = Game.flags[name].memory[_MEM.COLONY]; + if (colonyName) { + if (USE_PROFILER && !profilerRooms[colonyName]) { + continue; // don't make directive if colony is ignored for profiling + } + let colonyMemory = Memory.colonies[colonyName] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + continue; // don't make directive if colony is suspended + } + } + const directive = DirectiveWrapper(Game.flags[name]); + if (directive && spawnOverlords) { + directive.spawnMoarOverlords(); + } + if (!directive && Game.time % 10 == 0) { + // log.alert(`Flag [${name} @ ${Game.flags[name].pos.print}] does not match ` + + // `a valid directive color code! (Refer to /src/directives/initializer.ts)` + alignedNewline + + // `Use removeErrantFlags() to remove flags which do not match a directive.`); + } + } + } + + /* Refresh all directives, adding new ones for new flags */ + private refreshDirectives(): void { + for (let name in this.directives) { // this should be called first + this.directives[name].refresh(); + } + this.registerDirectives(true); // register any new directives that were placed since last rebuild + } + + /* Intialize everything in pre-init phase of main loop. Does not call colony.init(). */ + init(): void { + // Initialize the overseer + this.overseer.init(); + // Initialize each colony + for (let colonyName in this.colonies) { + let start = Game.cpu.getUsed(); + this.try(() => this.colonies[colonyName].init(), colonyName); + Stats.log(`cpu.usage.${colonyName}.init`, Game.cpu.getUsed() - start); + } + // Initialize spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].init(), ref); + } + // Initialize terminalNetwork and tradeNetwork + this.try(() => this.terminalNetwork.init()); + this.try(() => this.tradeNetwork.init()); + // Initialize strategist + if (this.strategist) { + this.try(() => this.strategist!.init()); + } + } + + run(): void { + // Enforce behavioral locks every 3 ticks + if (Game.time % 3 == 0) { + IntelManagement.run(); + } + // Run spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].run(), ref); + } + // Run the overseer + this.overseer.run(); + // Run all colonies + for (let colonyName in this.colonies) { + this.try(() => this.colonies[colonyName].run(), colonyName); + } + // Run all contracts + if (MY_USERNAME == MUON) { // This ensures that my contracts don't run by default on other people's accounts + for (let contract of AllContracts) { + this.try(() => contract.run()); + } + } + // Run terminal network + this.try(() => this.terminalNetwork.run()); + // Run trade network + this.try(() => this.tradeNetwork.run()); + // Run strategist + if (this.strategist) { + this.try(() => this.strategist!.run()); + } + // Run room intel + this.try(() => RoomIntel.run()); // this should be at end, right before assimilator + // Run assimilator + this.try(() => Assimilator.run()); // this should always be last + } + + postRun(): void { + // Run version updater + this.try(() => VersionUpdater.run()); + // Run segmenter + this.try(() => Segmenter.run()); + // Handle exceptions + this.handleExceptions(); + } + + visuals(): void { + if (Game.cpu.bucket > 9000) { + // Draw global visuals + Visualizer.visuals(); + // Add notifications for outdated version + if (VersionUpdater.memory.newestVersion) { + const newestVersion = VersionUpdater.memory.newestVersion; + if (VersionUpdater.isVersionOutdated(newestVersion)) { + this.overseer.notifier.alert(`[!] Update available: ${__VERSION__} → ${newestVersion}`, + undefined, -1); + } + } + // Draw overseer visuals + this.overseer.visuals(); + // Draw colony visuals + for (let colonyName in this.colonies) { + this.colonies[colonyName].visuals(); + } + } else { + if (Game.time % 10 == 0) { + log.info(`CPU bucket is too low (${Game.cpu.bucket}) - skip rendering visuals.`); + } + } + } +} + + +// Behavioral locks ==================================================================================================== + +/* class BehavioralLocks */ +class IntelManagement { // Renamed to IntelManagement to avoid identification + + /* static enforceSignature(): void */ + static runRoomIntel_1(): void { + let badSignatures: string[] = []; + let colonies = getAllColonies(); + if (colonies.length == 0) return; + for (let colony of colonies) { + if (colony.defcon > 0 || colony.creeps.length == 0) { + continue; + } + let controller = colony.controller; + if (controller.signedByScreeps || controller.level < 4) { + continue; + } + let validSignature = false; + if (controller.sign) { + let sig = controller.sign.text; + if (sig.toLowerCase().includes('overmind') + || sig.includes('\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05')) { + validSignature = true; + } + } + if (!validSignature) { + badSignatures.push(controller.sign ? controller.sign.text : 'undefined'); + } + } + // Throw an error if more than half of rooms have a bad signature + if (badSignatures.length >= 0.5 * _.keys(Overmind.colonies).length) { + Memory.settings.signature = DEFAULT_OVERMIND_SIGNATURE; + log.warning(`Invalid controller signatures detected:` + + bulleted(badSignatures) + alignedNewline + + `Signatures must contain the string "Overmind" or ` + + `${'\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05'}.`); + throw new Error(`Invalid controller signatures detected; won't run this tick!`); + } + } + + /* static enforceIllegalDirectives */ + static runRoomIntel_2(): void { + if (!Assimilator.isAssimilated(MY_USERNAME)) { // if you are not assimilated + const illegalColorCombinations: ColorConstant[][] = [[COLOR_RED, COLOR_RED]]; + for (let name in Game.flags) { + const flag = Game.flags[name]; + const colors = [flag.color, flag.secondaryColor]; + if (illegalColorCombinations.includes(colors)) { + flag.remove(); + log.warning(`Illegal directives detected and removed: ${flag.name}. ` + + `Assimilation required to access these features.`); + } + } + } + } + + static run(): void { + this.runRoomIntel_1(); + if (Game.time % (3 * 31) == 0) { // must be multiple of 3 + this.runRoomIntel_2(); + } + } + +} + + +// Version updater ===================================================================================================== + +interface VersionSegmentData { + version: string; +} + +interface VersionUpdaterMemory { + versions: { [version: string]: any }; + newestVersion: string | undefined; +} + +class VersionUpdater { + + static CheckFrequency = 100; + static CheckOnTick = 91; + static VersionSegment = 99; + + static get memory(): VersionUpdaterMemory { + return Mem.wrap(Memory.Overmind, 'versionUpdater', { + versions : {}, + newestVersion: undefined, + }); + } + + private static slave_fetchVersion(): string | undefined { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.requestForeignSegment(MUON, this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick) { + let data = Segmenter.getForeignSegment() as VersionSegmentData | undefined; + if (data) { + return data.version; + } + } + } + + static isVersionOutdated(newVersion: string): boolean { + let [major, minor, patch] = _.map(__VERSION__.split('.'), + str => parseInt(str, 10)) as number[]; + let [newMajor, newMinor, newPatch] = _.map(newVersion.split('.'), + str => parseInt(str, 10)) as number[]; + return (newMajor > major || newMinor > minor || newPatch > patch); + } + + private static master_pushVersion(): void { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 2) { + Segmenter.requestSegments(this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.markSegmentAsPublic(this.VersionSegment); + Segmenter.setSegmentProperty(this.VersionSegment, 'version', __VERSION__); + } + } + + static generateUpdateMessage(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let msg = '\n'; + for (let line of asciiLogoSmall) { + msg += line + '\n'; + } + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = '╔═════════════════════════════════════════════════════════╗\n' + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + '╚═════════════════════════════════════════════════════════╝'; + return msg + updateMsg; + } + + static generateUpdateMessageSmall(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = `╔═════════════════════════════════╗\n` + + `║ OVERMIND SCREEPS AI ║\n` + + `╠═════════════════════════════════╣\n` + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + `╚═════════════════════════════════╝`; + return '\n' + updateMsg; + } + + static displayUpdateMessage(newVersion: string): void { + const updateMessage = this.generateUpdateMessage(__VERSION__, newVersion); + console.log(`${updateMessage}`); + } + + static sayUpdateMessage(newVersion: string): void { + for (let name in Game.creeps) { + let creep = Game.creeps[name]; + creep.say('Update me!', true); + } + } + + static notifyNewVersion(newVersion: string): void { + const updateMessage = this.generateUpdateMessageSmall(__VERSION__, newVersion); + Game.notify(`${updateMessage}`); + } + + static run(): void { + if (MY_USERNAME == MUON) { + this.master_pushVersion(); + } + // Update version + let fetchedVersion = this.slave_fetchVersion(); + if (fetchedVersion) { + this.memory.newestVersion = fetchedVersion; + } + // Check for new version + const newestVersion = this.memory.newestVersion; + if (newestVersion && this.isVersionOutdated(newestVersion)) { + if (Game.time % 10 == 0) { + this.displayUpdateMessage(newestVersion); + this.sayUpdateMessage(newestVersion); + } + if (Game.time % 10000 == 0 /*!this.memory.versions[newVersion]*/) { + this.notifyNewVersion(newestVersion); + } + } + } + +} + + diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 7668622cf..f49de8b52 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -311,15 +311,15 @@ export const CombatSetups = { drill: { default: new CreepSetup(Roles.drill, { - pattern : [MOVE, ATTACK, ATTACK, ATTACK, ATTACK, MOVE], + pattern : [MOVE, ATTACK, ATTACK, MOVE], sizeLimit: Infinity, }), }, coolant: { default: new CreepSetup(Roles.coolant, { - pattern : [HEAL, HEAL, MOVE], - sizeLimit: 11, + pattern : [HEAL, MOVE], + sizeLimit: Infinity, }), } diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 8e1a27636..d37a0cc78 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -106,9 +106,9 @@ export class DirectiveHaul extends Directive { } run(): void { - if (_.sum(this.store) == 0 && this.pos.isVisible) { - this.remove(); - } + // if (_.sum(this.store) == 0 && this.pos.isVisible) { + // this.remove(); + // } } } diff --git a/src/main.ts b/src/main.ts index 3239f1ae2..fe051121d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,7 +33,7 @@ import {Mem} from './memory/Memory'; import {OvermindConsole} from './console/Console'; import {Stats} from './stats/stats'; import profiler from './profiler/screeps-profiler'; -import _Overmind from './Overmind_obfuscated'; // this should be './Overmind_obfuscated' unless you are me +import _Overmind from './Overmind_ts'; // this should be './Overmind_obfuscated' unless you are me import {VersionMigration} from './versionMigration/migrator'; import {RemoteDebugger} from './debug/remoteDebugger'; // ===================================================================================================================== diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index c6ab930a4..4bc9248d8 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -11,6 +11,7 @@ import {CombatOverlord} from '../CombatOverlord'; import {CombatSetups, Roles} from '../../creepSetups/setups'; import {OverlordMemory} from '../Overlord'; import {DirectivePowerMine} from "../../directives/resource/powerMine"; +import {DirectiveHaul} from "../../directives/resource/haul"; interface PowerDrillOverlordMemory extends OverlordMemory { targetPBID?: string; @@ -43,26 +44,39 @@ export class PowerDrillOverlord extends CombatOverlord { refresh() { super.refresh(); this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + } init() { - this.wishlist(2, CombatSetups.coolant.default); - this.wishlist(1, CombatSetups.drill.default); + this.wishlist(3, CombatSetups.drill.default); + this.wishlist(4, CombatSetups.coolant.default); } private handleDrill(drill: CombatZerg) { + if (!this.targetPowerBank) { + if (!this.room) { + // We are not there yet + } else { + var bank = this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank; + this.targetPowerBank = bank; + // If power bank is dead + if (bank == undefined) { + Game.notify("Power bank in " + this.room + " is dead."); + DirectiveHaul.create(this.pos); + this.directive.remove(); + Game.notify("FINISHED POWER MININING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); + drill.suicide(); + return; + } + } + } // Go to keeper room - if (!this.room || drill.room != this.room || drill.pos.isEdge) { - // log.debugCreep(reaper, `Going to room!`); - //drill.healSelfIfPossible(); + if (!this.room || drill.room != this.room || drill.pos.isEdge || !this.targetPowerBank) { + // log.debugCreep(drill, `Going to room!`); Game.notify("Drill is moving to power site in " + this.room + "."); drill.goTo(this.pos); return; - } else if (!this.targetPowerBank) { - // If power bank is dead - Game.notify("Power bank in " + this.room + " is dead."); - return; } else if (this.targetPowerBank.hits < 50000) { Game.notify("Power bank in " + this.room + " is almost dead"); // Spawn a hauler for this location @@ -71,13 +85,13 @@ export class PowerDrillOverlord extends CombatOverlord { // Handle killing bank if (drill.hits > 100) { + drill.goTo(this.targetPowerBank); drill.attack(this.targetPowerBank); } } private handleCoolant(coolant: CombatZerg) { - - // Go to keeper room + // Go to powerbank room if (!this.room || coolant.room != this.room || coolant.pos.isEdge) { // log.debugCreep(reaper, `Going to room!`); coolant.healSelfIfPossible(); @@ -86,12 +100,18 @@ export class PowerDrillOverlord extends CombatOverlord { } else if (!this.targetPowerBank) { // If power bank is dead Game.notify("Power bank in " + this.room + " is dead."); + coolant.suicide(); return; } else if (this.targetPowerBank.hits < 50000) { Game.notify("Power bank in " + this.room + " is almost dead"); + Game.notify("Power bank in " + this.room + ", beginning haul operation."); + //DirectiveHaul.create(this.pos); + } + if (coolant.pos.getRangeTo(_.first(this.drills)) > 1) { + coolant.goTo(_.first(this.drills)); } - coolant.autoHeal(); + coolant.autoHeal(false); } run() { From 5633a28f13fec3cb912ce1109b88c962874b4886 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 22 Apr 2019 12:14:22 -0700 Subject: [PATCH 016/137] Adding special hauling logic --- src/directives/resource/haul.ts | 15 ++- src/overlords/powerMining/PowerDrill.ts | 12 +- src/overlords/powerMining/PowerHauler.ts | 151 +++++++++++++++++++++++ src/overlords/situational/hauler.ts | 4 +- src/utilities/creepUtils.ts | 45 +++++++ 5 files changed, 215 insertions(+), 12 deletions(-) create mode 100644 src/overlords/powerMining/PowerHauler.ts create mode 100644 src/utilities/creepUtils.ts diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index d37a0cc78..a7456e626 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -2,6 +2,7 @@ import {Directive} from '../Directive'; import {profile} from '../../profiler/decorator'; import {isStoreStructure} from '../../declarations/typeGuards'; import {HaulingOverlord} from '../../overlords/situational/hauler'; +import {PowerHaulingOverlord} from "../../overlords/powerMining/PowerHauler"; interface DirectiveHaulMemory extends FlagMemory { @@ -10,7 +11,7 @@ interface DirectiveHaulMemory extends FlagMemory { /** - * Hauling directive: spawns hauler creeps to move large amounts of resourecs from a location (e.g. draining a storage) + * Hauling directive: spawns hauler creeps to move large amounts of resources from a location (e.g. draining a storage) */ @profile export class DirectiveHaul extends Directive { @@ -29,7 +30,11 @@ export class DirectiveHaul extends Directive { } spawnMoarOverlords() { - this.overlords.haul = new HaulingOverlord(this); + if (this.pos.lookForStructure(STRUCTURE_POWER_BANK)) { + this.overlords.haul = new PowerHaulingOverlord(this); + } else { + this.overlords.haul = new HaulingOverlord(this); + } } get targetedBy(): string[] { @@ -106,9 +111,9 @@ export class DirectiveHaul extends Directive { } run(): void { - // if (_.sum(this.store) == 0 && this.pos.isVisible) { - // this.remove(); - // } + if (_.sum(this.store) == 0 && this.pos.isVisible) { + this.remove(); + } } } diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 4bc9248d8..b82f7f74b 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -12,6 +12,7 @@ import {CombatSetups, Roles} from '../../creepSetups/setups'; import {OverlordMemory} from '../Overlord'; import {DirectivePowerMine} from "../../directives/resource/powerMine"; import {DirectiveHaul} from "../../directives/resource/haul"; +import {calculateFormationStrength} from "../../utilities/creepUtils"; interface PowerDrillOverlordMemory extends OverlordMemory { targetPBID?: string; @@ -24,6 +25,7 @@ interface PowerDrillOverlordMemory extends OverlordMemory { export class PowerDrillOverlord extends CombatOverlord { static requiredRCL = 7; + static haulerPrespawn = 600; directive: DirectiveSKOutpost; memory: PowerDrillOverlordMemory; @@ -62,9 +64,8 @@ export class PowerDrillOverlord extends CombatOverlord { // If power bank is dead if (bank == undefined) { Game.notify("Power bank in " + this.room + " is dead."); - DirectiveHaul.create(this.pos); this.directive.remove(); - Game.notify("FINISHED POWER MININING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); + Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); drill.suicide(); return; } @@ -77,11 +78,12 @@ export class PowerDrillOverlord extends CombatOverlord { Game.notify("Drill is moving to power site in " + this.room + "."); drill.goTo(this.pos); return; - } else if (this.targetPowerBank.hits < 50000) { + } else if (this.targetPowerBank.hits < 800000) { Game.notify("Power bank in " + this.room + " is almost dead"); - // Spawn a hauler for this location - // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep + DirectiveHaul.create(this.pos); } + // Spawn a hauler for this location + // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep // Handle killing bank if (drill.hits > 100) { diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts new file mode 100644 index 000000000..632ce0654 --- /dev/null +++ b/src/overlords/powerMining/PowerHauler.ts @@ -0,0 +1,151 @@ +import {Overlord} from '../Overlord'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {Zerg} from '../../zerg/Zerg'; +import {DirectiveHaul} from '../../directives/resource/haul'; +import {Tasks} from '../../tasks/Tasks'; +import {isStoreStructure} from '../../declarations/typeGuards'; +import {log} from '../../console/log'; +import {Pathing} from '../../movement/Pathing'; +import {Energetics} from '../../logistics/Energetics'; +import {profile} from '../../profiler/decorator'; +import {Roles, Setups} from '../../creepSetups/setups'; +import {HaulingOverlord} from "../situational/hauler"; +import {calculateFormationStrength} from "../../utilities/creepUtils"; + +/** + * Spawns special-purpose haulers for transporting resources to/from a specified target + */ +@profile +export class PowerHaulingOverlord extends HaulingOverlord { + + haulers: Zerg[]; + directive: DirectiveHaul; + powerBank: StructurePowerBank | undefined; + tickToSpawnOn: number; + numHaulers: number; + + requiredRCL = 6; + // Allow time for body to spawn + prespawnAmount = 200; + + constructor(directive: DirectiveHaul, priority = OverlordPriority.collectionUrgent.haul) { + super(directive, priority); // Removed 'haul' string + this.directive = directive; + this.haulers = this.zerg(Roles.transport); + } + + init() { + if (!this.colony.storage || _.sum(this.colony.storage.store) > Energetics.settings.storage.total.cap) { + return; + } + // Spawn a number of haulers sufficient to move all resources within a lifetime, up to a max + let MAX_HAULERS = 5; + // Calculate total needed amount of hauling power as (resource amount * trip distance) + let tripDistance = 2 * Pathing.distance((this.colony.storage || this.colony).pos, this.directive.pos); + let haulingPowerNeeded = Math.min(this.directive.totalResources, + this.colony.storage.storeCapacity + - _.sum(this.colony.storage.store)) * tripDistance; + // Calculate amount of hauling each hauler provides in a lifetime + let haulerCarryParts = Setups.transporters.early.getBodyPotential(CARRY, this.colony); + let haulingPowerPerLifetime = CREEP_LIFE_TIME * haulerCarryParts * CARRY_CAPACITY; + // Calculate number of haulers + this.numHaulers = Math.min(Math.ceil(haulingPowerNeeded / haulingPowerPerLifetime), MAX_HAULERS); + // Request the haulers + this.tickToSpawnOn = Game.time + (this.calculateRemainingLifespan() || 0) - this.prespawnAmount; + } + + + calculateRemainingLifespan() { + if (!this.room) { + return undefined; + } else if (this.powerBank == undefined) { + // Power Bank is gone + return 0; + } else { + let tally = calculateFormationStrength(this.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); + let healStrength: number = tally.heal * HEAL_POWER || 0; + let attackStrength: number = tally.attack * ATTACK_POWER || 0; + // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing + let avgDamagePerTick = Math.min(attackStrength, healStrength*2); + return this.powerBank.hits / avgDamagePerTick; + } + } + + protected handleHauler(hauler: Zerg) { + if (_.sum(hauler.carry) == 0) { + // Travel to directive and collect resources + if (hauler.inSameRoomAs(this.directive)) { + // Pick up drops first + if (this.directive.hasDrops) { + let allDrops: Resource[] = _.flatten(_.values(this.directive.drops)); + let drop = allDrops[0]; + if (drop) { + hauler.task = Tasks.pickup(drop); + return; + } + } + // Withdraw from store structure + if (this.directive.storeStructure) { + let store: { [resourceType: string]: number } = {}; + if (isStoreStructure(this.directive.storeStructure)) { + store = this.directive.storeStructure.store; + } else { + store = {'energy': this.directive.storeStructure.energy}; + } + for (let resourceType in store) { + if (store[resourceType] > 0) { + hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType); + return; + } + } + } + // Shouldn't reach here + log.warning(`${hauler.name} in ${hauler.room.print}: nothing to collect!`); + } else { + // hauler.task = Tasks.goTo(this.directive); + hauler.goTo(this.directive); + } + } else { + // Travel to colony room and deposit resources + if (hauler.inSameRoomAs(this.colony)) { + // Put energy in storage and minerals in terminal if there is one + for (let resourceType in hauler.carry) { + if (hauler.carry[resourceType] == 0) continue; + if (resourceType == RESOURCE_ENERGY) { // prefer to put energy in storage + if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.storage, resourceType); + return; + } else if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.terminal, resourceType); + return; + } + } else { // prefer to put minerals in terminal + if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.terminal, resourceType); + return; + } else if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.storage, resourceType); + return; + } + } + } + // Shouldn't reach here + log.warning(`${hauler.name} in ${hauler.room.print}: nowhere to put resources!`); + } else { + hauler.task = Tasks.goToRoom(this.colony.room.name); + } + } + } + + run() { + if (Game.time >= this.tickToSpawnOn && this.haulers.length == 0) { + this.wishlist(this.numHaulers, Setups.transporters.early); + } + for (let hauler of this.haulers) { + if (hauler.isIdle) { + this.handleHauler(hauler); + } + hauler.run(); + } + } +} \ No newline at end of file diff --git a/src/overlords/situational/hauler.ts b/src/overlords/situational/hauler.ts index 86bfd43a3..ffd527cca 100644 --- a/src/overlords/situational/hauler.ts +++ b/src/overlords/situational/hauler.ts @@ -19,7 +19,7 @@ export class HaulingOverlord extends Overlord { haulers: Zerg[]; directive: DirectiveHaul; - requiredRCL: 4; + requiredRCL: number = 4; constructor(directive: DirectiveHaul, priority = directive.hasDrops ? OverlordPriority.collectionUrgent.haul : OverlordPriority.collection.haul) { @@ -48,7 +48,7 @@ export class HaulingOverlord extends Overlord { this.wishlist(numHaulers, Setups.transporters.early); } - private handleHauler(hauler: Zerg) { + protected handleHauler(hauler: Zerg) { if (_.sum(hauler.carry) == 0) { // Travel to directive and collect resources if (hauler.inSameRoomAs(this.directive)) { diff --git a/src/utilities/creepUtils.ts b/src/utilities/creepUtils.ts new file mode 100644 index 000000000..7e5b1a6b7 --- /dev/null +++ b/src/utilities/creepUtils.ts @@ -0,0 +1,45 @@ +// Creep utilities that don't belong anywhere else + + +// Does not account for range, just total of body parts +export function calculateFormationStrength(creeps : Creep[]): Record { + let tally: Record = { + move : 0, + work : 0, + carry : 0, + attack : 0, + ranged_attack : 0, + tough : 0, + heal : 0, + claim : 0, + }; + + _.forEach(creeps, + function (unit) { + let individualTally = calculateBodyPotential(unit.body); + for (let bodyType in individualTally) { + let type = bodyType as BodyPartConstant; + tally[type] += individualTally[type]; + } + }); + return tally; +} + +export function calculateBodyPotential(body : BodyPartDefinition[]): Record { + let tally: Record = { + move : 0, + work : 0, + carry : 0, + attack : 0, + ranged_attack : 0, + tough : 0, + heal : 0, + claim : 0, + }; + _.forEach(body, function (bodyPart) { + // Needs boost logic + tally[bodyPart.type] += 1; + } + ); + return tally; +} From dd98468431b960b28178e97e38a97470f8e38553 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 22 Apr 2019 14:50:22 -0700 Subject: [PATCH 017/137] Fixing haul directive for unseen rooms --- src/directives/resource/haul.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index a7456e626..6dff8f288 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -30,7 +30,7 @@ export class DirectiveHaul extends Directive { } spawnMoarOverlords() { - if (this.pos.lookForStructure(STRUCTURE_POWER_BANK)) { + if (this.room && this.pos.lookForStructure(STRUCTURE_POWER_BANK)) { this.overlords.haul = new PowerHaulingOverlord(this); } else { this.overlords.haul = new HaulingOverlord(this); From 4e1ff94a8df068a4de26d8c237d4293e9b6ee4a5 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 22 Apr 2019 15:23:04 -0700 Subject: [PATCH 018/137] Don't push, unobscficate --- src/Overmind_ts.ts | 590 +++++++++++++++++++++++++++++++++++++++++++++ src/main.ts | 2 +- 2 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 src/Overmind_ts.ts diff --git a/src/Overmind_ts.ts b/src/Overmind_ts.ts new file mode 100644 index 000000000..ad3762532 --- /dev/null +++ b/src/Overmind_ts.ts @@ -0,0 +1,590 @@ +// javascript-obfuscator:disable +// +// Overmind_obfuscated.js: this file is intentially obfuscated to prevent tampering. +// +// Q: Why is this file obfuscated? +// +// A: Using Overmind as your AI puts you at a huge advantage if you are a new player in a novice zone. Screeps has +// always had problems with people downloading bots from the internet and stomping new players. I have kept Overmind +// open-sourced because I think it can be a good resource for learning how to play Screeps, but I don't want it to +// be abused as a noob-crushing machine. In the future, I will be implementing behavioral locks in this file which +// limit unreasonable aggression toward peaceful new players. +// +// Q: What kind of behavioral locks? +// +// A: Players will be able to opt out of aggression by setting a property in their public memory. Overmind bots will not +// attack the player unless they attack you, claim a room in Overmind's territory, or occupy a room which is +// critically important (for example, very desirable mineral deposits that aren't available elsewhere). Overmind +// will attempt to expand around players which pose no threat to it rather than eliminating them. +// +// Q: What does this file do? +// +// A: The Overmind object is the top-level initializer of the AI and instantiates all colonies and directives. It is +// also responsible for some high-level decision making. You can see the enumerated properties of the Overmind class +// in IOvermind in declarations/index.d.ts. Since this file is sufficiently complex and is critical for the AI to be +// able to run, it was a natural choice of location to put code which should be tamper-resistant. +// +// Q: What happens if I modify this code? +// +// A: This code is self-defending, so any modification to it will likely break the script. +// +// Q: I would like to view the original source code for this file. +// +// A: If you have a compelling reason that you'd like to see the non-obfuscated source for this file, message me in +// game, on slack, or send me an email at benbartlett@stanford.edu. +// + +import {Colony, ColonyMemory, getAllColonies} from './Colony'; +import {DirectiveWrapper} from './directives/initializer'; +import {profile} from './profiler/decorator'; +import {GameCache} from './caching/GameCache'; +import {Overlord} from './overlords/Overlord'; +import {Visualizer} from './visuals/Visualizer'; +import {Stats} from './stats/stats'; +import {TerminalNetwork} from './logistics/TerminalNetwork'; +import {AllContracts} from './contracts/contractsList'; +import {Autonomy, getAutonomyLevel, Mem} from './memory/Memory'; +import {asciiLogoSmall} from './visuals/logos'; +import {log} from './console/log'; +import {TraderJoe} from './logistics/TradeNetwork'; +import {RoomIntel} from './intel/RoomIntel'; +import { + DEFAULT_OVERMIND_SIGNATURE, + MUON, + MY_USERNAME, + NEW_OVERMIND_INTERVAL, + PROFILER_COLONY_LIMIT, + PROFILER_INCLUDE_COLONIES, + USE_PROFILER, + USE_TRY_CATCH +} from './~settings'; +import {Strategist} from './strategy/Strategist'; +import {assimilationLocked} from './assimilation/decorator'; +import {SpawnGroup} from './logistics/SpawnGroup'; +import {alignedNewline} from './utilities/stringConstants'; +import {bulleted} from './utilities/utils'; +import {Directive} from './directives/Directive'; +import {Zerg} from './zerg/Zerg'; +import {Segmenter} from './memory/Segmenter'; +import {Overseer} from './Overseer'; +import {NotifierPriority} from './directives/Notifier'; + +// javascript-obfuscator:enable + +let profilerRooms: { [roomName: string]: boolean } = {}; +if (USE_PROFILER) { + for (let name of PROFILER_INCLUDE_COLONIES) { + profilerRooms[name] = true; + } + let myRoomNames = _.filter(_.keys(Game.rooms), name => Game.rooms[name] && Game.rooms[name].my); + for (let name of _.sample(myRoomNames, PROFILER_COLONY_LIMIT - PROFILER_INCLUDE_COLONIES.length)) { + profilerRooms[name] = true; + } +} + +@profile +@assimilationLocked +export default class _Overmind implements IOvermind { + + memory: IOvermindMemory; + overseer: Overseer; + shouldBuild: boolean; + expiration: number; + cache: GameCache; + colonies: { [roomName: string]: Colony }; // Global hash of all colony objects + directives: { [flagName: string]: Directive }; + zerg: { [creepName: string]: Zerg }; + overlords: { [ref: string]: Overlord }; + spawnGroups: { [ref: string]: SpawnGroup }; + colonyMap: { [roomName: string]: string }; // Global map of colony associations for possibly-null rooms + terminalNetwork: TerminalNetwork; + tradeNetwork: TraderJoe; + strategist: Strategist | undefined; + exceptions: Error[]; + + constructor() { + this.memory = Memory.Overmind as IOvermindMemory; + this.overseer = new Overseer(); + this.shouldBuild = true; + this.expiration = Game.time + NEW_OVERMIND_INTERVAL; + this.cache = new GameCache(); + this.colonies = {}; + this.directives = {}; + this.zerg = {}; + this.overlords = {}; + this.spawnGroups = {}; + this.colonyMap = {}; + this.terminalNetwork = this.makeTerminalNetwork(); + this.tradeNetwork = new TraderJoe(); + this.strategist = getAutonomyLevel() > Autonomy.Manual ? new Strategist() : undefined; + this.exceptions = []; + } + + /* Global instantiation of Overmind object; run once every global refresh */ + build(): void { + log.debug(`Rebuilding Overmind object!`); + this.cache.build(); + // Register all colonies and instantiate their overlords + this.registerColonies(); + _.forEach(this.colonies, colony => colony.spawnMoarOverlords()); + // Register directives and instantiate their overlords; must be done AFTER colonies + this.registerDirectives(); + _.forEach(this.directives, directive => directive.spawnMoarOverlords()); + this.shouldBuild = false; + } + + /* Refresh the state of the Overmind; run at the beginning of every tick */ + refresh(): void { + this.shouldBuild = true; // assume refresh will be unsuccessful + // Refresh constructor-phase objects + this.memory = Memory.Overmind as IOvermindMemory; + this.exceptions = []; + this.cache.refresh(); + this.overseer.refresh(); + this.terminalNetwork.refresh(); + this.tradeNetwork.refresh(); + if (this.strategist) this.strategist.refresh(); + // Refresh build-phase objects + this.refreshColonies(); + this.refreshDirectives(); + for (let ref in this.overlords) { + this.overlords[ref].refresh(); + } + for (let ref in this.spawnGroups) { + this.spawnGroups[ref].refresh(); + } + this.shouldBuild = false; // refresh successful + } + + private try(callback: () => any, identifier?: string): void { + if (USE_TRY_CATCH) { + try { + callback(); + } catch (e) { + if (identifier) { + e.name = `Caught unhandled exception at ${'' + callback} (identifier: ${identifier}): \n` + + e.name + '\n' + e.stack; + } else { + e.name = `Caught unhandled exception at ${'' + callback}: \n` + e.name + '\n' + e.stack; + } + this.exceptions.push(e); + } + } else { + callback(); + } + } + + private handleExceptions(): void { + if (this.exceptions.length == 0) { + return; + } else { + log.warning(`Exceptions present this tick! Rebuilding Overmind object in next tick.`); + Memory.stats.persistent.lastErrorTick = Game.time; + this.shouldBuild = true; + this.expiration = Game.time; + if (this.exceptions.length == 1) { + throw _.first(this.exceptions); + } else { + for (let e of this.exceptions) { + log.throw(e); + } + let error = new Error('Multiple exceptions caught this tick!'); + error.stack = _.map(this.exceptions, e => e.name).join('\n'); + throw error; + } + } + } + + private makeTerminalNetwork(): TerminalNetwork { + // Initialize the terminal netowrk + let terminals: StructureTerminal[] = []; + for (let name in Game.rooms) { + if (USE_PROFILER && !profilerRooms[name]) continue; + let room = Game.rooms[name]; + if (room.my && room.controller!.level >= 6 && room.terminal && room.terminal.my) { + terminals.push(room.terminal); + } + } + return new TerminalNetwork(terminals); + } + + + /* Instantiate a new colony for each owned rom */ + private registerColonies(): void { + + let colonyOutposts: { [roomName: string]: string[] } = {}; // key: lead room, values: outposts[] + this.colonyMap = {}; + // Register colonies to their outposts + let flagsByRoom = _.groupBy(this.cache.outpostFlags, flag => flag.memory[_MEM.COLONY]); + for (let name in Game.rooms) { + if (Game.rooms[name].my) { + let colonyMemory = Memory.colonies[name] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + this.overseer.notifier.alert("Colony suspended", name, NotifierPriority.High); + continue; + } + if (Game.rooms[name].flags) + colonyOutposts[name] = _.map(flagsByRoom[name], + flag => flag.memory.setPosition + ? derefRoomPosition(flag.memory.setPosition).roomName + : flag.pos.roomName); + this.colonyMap[name] = name; + } + } + // Register outposts to their colonies + for (let colonyName in colonyOutposts) { + for (let outpostName of colonyOutposts[colonyName]) { + this.colonyMap[outpostName] = colonyName; + } + } + + // Initialize the Colonies and pass each one its assigned creeps + let id = 0; + for (let name in colonyOutposts) { + if (USE_PROFILER && !profilerRooms[name]) { + if (Game.time % 20 == 0) { + log.alert(`Suppressing instantiation of colony ${name}.`); + } + continue; // don't make colony for this room + } + try { + this.colonies[name] = new Colony(id, name, colonyOutposts[name]); + } catch (e) { + e.name = `Caught unhandled exception instantiating colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + id++; + } + } + + private refreshColonies(): void { + for (let name in this.colonies) { + try { + this.colonies[name].refresh(); + } catch (e) { + e.name = `Caught unhandled exception refreshing colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + } + } + + /* Wrap each flag in a color coded wrapper */ + private registerDirectives(spawnOverlords = false): void { + // Create a directive for each flag (registration takes place on construction) + for (let name in Game.flags) { + if (this.directives[name]) { // skip existing directives + continue; + } + let colonyName = Game.flags[name].memory[_MEM.COLONY]; + if (colonyName) { + if (USE_PROFILER && !profilerRooms[colonyName]) { + continue; // don't make directive if colony is ignored for profiling + } + let colonyMemory = Memory.colonies[colonyName] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + continue; // don't make directive if colony is suspended + } + } + const directive = DirectiveWrapper(Game.flags[name]); + if (directive && spawnOverlords) { + directive.spawnMoarOverlords(); + } + if (!directive && Game.time % 10 == 0) { + // log.alert(`Flag [${name} @ ${Game.flags[name].pos.print}] does not match ` + + // `a valid directive color code! (Refer to /src/directives/initializer.ts)` + alignedNewline + + // `Use removeErrantFlags() to remove flags which do not match a directive.`); + } + } + } + + /* Refresh all directives, adding new ones for new flags */ + private refreshDirectives(): void { + for (let name in this.directives) { // this should be called first + this.directives[name].refresh(); + } + this.registerDirectives(true); // register any new directives that were placed since last rebuild + } + + /* Intialize everything in pre-init phase of main loop. Does not call colony.init(). */ + init(): void { + // Initialize the overseer + this.overseer.init(); + // Initialize each colony + for (let colonyName in this.colonies) { + let start = Game.cpu.getUsed(); + this.try(() => this.colonies[colonyName].init(), colonyName); + Stats.log(`cpu.usage.${colonyName}.init`, Game.cpu.getUsed() - start); + } + // Initialize spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].init(), ref); + } + // Initialize terminalNetwork and tradeNetwork + this.try(() => this.terminalNetwork.init()); + this.try(() => this.tradeNetwork.init()); + // Initialize strategist + if (this.strategist) { + this.try(() => this.strategist!.init()); + } + } + + run(): void { + // Enforce behavioral locks every 3 ticks + if (Game.time % 3 == 0) { + IntelManagement.run(); + } + // Run spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].run(), ref); + } + // Run the overseer + this.overseer.run(); + // Run all colonies + for (let colonyName in this.colonies) { + this.try(() => this.colonies[colonyName].run(), colonyName); + } + // Run all contracts + if (MY_USERNAME == MUON) { // This ensures that my contracts don't run by default on other people's accounts + for (let contract of AllContracts) { + this.try(() => contract.run()); + } + } + // Run terminal network + this.try(() => this.terminalNetwork.run()); + // Run trade network + this.try(() => this.tradeNetwork.run()); + // Run strategist + if (this.strategist) { + this.try(() => this.strategist!.run()); + } + // Run room intel + this.try(() => RoomIntel.run()); // this should be at end, right before assimilator + // Run assimilator + this.try(() => Assimilator.run()); // this should always be last + } + + postRun(): void { + // Run version updater + this.try(() => VersionUpdater.run()); + // Run segmenter + this.try(() => Segmenter.run()); + // Handle exceptions + this.handleExceptions(); + } + + visuals(): void { + if (Game.cpu.bucket > 9000) { + // Draw global visuals + Visualizer.visuals(); + // Add notifications for outdated version + if (VersionUpdater.memory.newestVersion) { + const newestVersion = VersionUpdater.memory.newestVersion; + if (VersionUpdater.isVersionOutdated(newestVersion)) { + this.overseer.notifier.alert(`[!] Update available: ${__VERSION__} → ${newestVersion}`, + undefined, -1); + } + } + // Draw overseer visuals + this.overseer.visuals(); + // Draw colony visuals + for (let colonyName in this.colonies) { + this.colonies[colonyName].visuals(); + } + } else { + if (Game.time % 10 == 0) { + log.info(`CPU bucket is too low (${Game.cpu.bucket}) - skip rendering visuals.`); + } + } + } +} + + +// Behavioral locks ==================================================================================================== + +/* class BehavioralLocks */ +class IntelManagement { // Renamed to IntelManagement to avoid identification + + /* static enforceSignature(): void */ + static runRoomIntel_1(): void { + let badSignatures: string[] = []; + let colonies = getAllColonies(); + if (colonies.length == 0) return; + for (let colony of colonies) { + if (colony.defcon > 0 || colony.creeps.length == 0) { + continue; + } + let controller = colony.controller; + if (controller.signedByScreeps || controller.level < 4) { + continue; + } + let validSignature = false; + if (controller.sign) { + let sig = controller.sign.text; + if (sig.toLowerCase().includes('overmind') + || sig.includes('\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05')) { + validSignature = true; + } + } + if (!validSignature) { + badSignatures.push(controller.sign ? controller.sign.text : 'undefined'); + } + } + // Throw an error if more than half of rooms have a bad signature + if (badSignatures.length >= 0.5 * _.keys(Overmind.colonies).length) { + Memory.settings.signature = DEFAULT_OVERMIND_SIGNATURE; + log.warning(`Invalid controller signatures detected:` + + bulleted(badSignatures) + alignedNewline + + `Signatures must contain the string "Overmind" or ` + + `${'\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05'}.`); + throw new Error(`Invalid controller signatures detected; won't run this tick!`); + } + } + + /* static enforceIllegalDirectives */ + static runRoomIntel_2(): void { + if (!Assimilator.isAssimilated(MY_USERNAME)) { // if you are not assimilated + const illegalColorCombinations: ColorConstant[][] = [[COLOR_RED, COLOR_RED]]; + for (let name in Game.flags) { + const flag = Game.flags[name]; + const colors = [flag.color, flag.secondaryColor]; + if (illegalColorCombinations.includes(colors)) { + flag.remove(); + log.warning(`Illegal directives detected and removed: ${flag.name}. ` + + `Assimilation required to access these features.`); + } + } + } + } + + static run(): void { + this.runRoomIntel_1(); + if (Game.time % (3 * 31) == 0) { // must be multiple of 3 + this.runRoomIntel_2(); + } + } + +} + + +// Version updater ===================================================================================================== + +interface VersionSegmentData { + version: string; +} + +interface VersionUpdaterMemory { + versions: { [version: string]: any }; + newestVersion: string | undefined; +} + +class VersionUpdater { + + static CheckFrequency = 100; + static CheckOnTick = 91; + static VersionSegment = 99; + + static get memory(): VersionUpdaterMemory { + return Mem.wrap(Memory.Overmind, 'versionUpdater', { + versions : {}, + newestVersion: undefined, + }); + } + + private static slave_fetchVersion(): string | undefined { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.requestForeignSegment(MUON, this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick) { + let data = Segmenter.getForeignSegment() as VersionSegmentData | undefined; + if (data) { + return data.version; + } + } + } + + static isVersionOutdated(newVersion: string): boolean { + let [major, minor, patch] = _.map(__VERSION__.split('.'), + str => parseInt(str, 10)) as number[]; + let [newMajor, newMinor, newPatch] = _.map(newVersion.split('.'), + str => parseInt(str, 10)) as number[]; + return (newMajor > major || newMinor > minor || newPatch > patch); + } + + private static master_pushVersion(): void { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 2) { + Segmenter.requestSegments(this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.markSegmentAsPublic(this.VersionSegment); + Segmenter.setSegmentProperty(this.VersionSegment, 'version', __VERSION__); + } + } + + static generateUpdateMessage(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let msg = '\n'; + for (let line of asciiLogoSmall) { + msg += line + '\n'; + } + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = '╔═════════════════════════════════════════════════════════╗\n' + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + '╚═════════════════════════════════════════════════════════╝'; + return msg + updateMsg; + } + + static generateUpdateMessageSmall(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = `╔═════════════════════════════════╗\n` + + `║ OVERMIND SCREEPS AI ║\n` + + `╠═════════════════════════════════╣\n` + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + `╚═════════════════════════════════╝`; + return '\n' + updateMsg; + } + + static displayUpdateMessage(newVersion: string): void { + const updateMessage = this.generateUpdateMessage(__VERSION__, newVersion); + console.log(`${updateMessage}`); + } + + static sayUpdateMessage(newVersion: string): void { + for (let name in Game.creeps) { + let creep = Game.creeps[name]; + creep.say('Update me!', true); + } + } + + static notifyNewVersion(newVersion: string): void { + const updateMessage = this.generateUpdateMessageSmall(__VERSION__, newVersion); + Game.notify(`${updateMessage}`); + } + + static run(): void { + if (MY_USERNAME == MUON) { + this.master_pushVersion(); + } + // Update version + let fetchedVersion = this.slave_fetchVersion(); + if (fetchedVersion) { + this.memory.newestVersion = fetchedVersion; + } + // Check for new version + const newestVersion = this.memory.newestVersion; + if (newestVersion && this.isVersionOutdated(newestVersion)) { + if (Game.time % 10 == 0) { + this.displayUpdateMessage(newestVersion); + this.sayUpdateMessage(newestVersion); + } + if (Game.time % 10000 == 0 /*!this.memory.versions[newVersion]*/) { + this.notifyNewVersion(newestVersion); + } + } + } + +} + + diff --git a/src/main.ts b/src/main.ts index 3239f1ae2..fe051121d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,7 +33,7 @@ import {Mem} from './memory/Memory'; import {OvermindConsole} from './console/Console'; import {Stats} from './stats/stats'; import profiler from './profiler/screeps-profiler'; -import _Overmind from './Overmind_obfuscated'; // this should be './Overmind_obfuscated' unless you are me +import _Overmind from './Overmind_ts'; // this should be './Overmind_obfuscated' unless you are me import {VersionMigration} from './versionMigration/migrator'; import {RemoteDebugger} from './debug/remoteDebugger'; // ===================================================================================================================== From 5af306cc72412e7311d6a8e9ca6eb3051cf3598d Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 22 Apr 2019 15:25:49 -0700 Subject: [PATCH 019/137] Initial commit for power harvesting --- src/Overmind_ts.ts | 590 +++++++++++++++++++++++ src/creepSetups/setups.ts | 17 + src/directives/initializer.ts | 3 + src/directives/resource/haul.ts | 9 +- src/directives/resource/powerMine.ts | 107 ++++ src/main.ts | 2 +- src/overlords/powerMining/PowerDrill.ts | 130 +++++ src/overlords/powerMining/PowerHauler.ts | 151 ++++++ src/overlords/situational/hauler.ts | 4 +- src/priorities/priorities_overlords.ts | 7 + src/utilities/creepUtils.ts | 45 ++ 11 files changed, 1060 insertions(+), 5 deletions(-) create mode 100644 src/Overmind_ts.ts create mode 100644 src/directives/resource/powerMine.ts create mode 100644 src/overlords/powerMining/PowerDrill.ts create mode 100644 src/overlords/powerMining/PowerHauler.ts create mode 100644 src/utilities/creepUtils.ts diff --git a/src/Overmind_ts.ts b/src/Overmind_ts.ts new file mode 100644 index 000000000..ad3762532 --- /dev/null +++ b/src/Overmind_ts.ts @@ -0,0 +1,590 @@ +// javascript-obfuscator:disable +// +// Overmind_obfuscated.js: this file is intentially obfuscated to prevent tampering. +// +// Q: Why is this file obfuscated? +// +// A: Using Overmind as your AI puts you at a huge advantage if you are a new player in a novice zone. Screeps has +// always had problems with people downloading bots from the internet and stomping new players. I have kept Overmind +// open-sourced because I think it can be a good resource for learning how to play Screeps, but I don't want it to +// be abused as a noob-crushing machine. In the future, I will be implementing behavioral locks in this file which +// limit unreasonable aggression toward peaceful new players. +// +// Q: What kind of behavioral locks? +// +// A: Players will be able to opt out of aggression by setting a property in their public memory. Overmind bots will not +// attack the player unless they attack you, claim a room in Overmind's territory, or occupy a room which is +// critically important (for example, very desirable mineral deposits that aren't available elsewhere). Overmind +// will attempt to expand around players which pose no threat to it rather than eliminating them. +// +// Q: What does this file do? +// +// A: The Overmind object is the top-level initializer of the AI and instantiates all colonies and directives. It is +// also responsible for some high-level decision making. You can see the enumerated properties of the Overmind class +// in IOvermind in declarations/index.d.ts. Since this file is sufficiently complex and is critical for the AI to be +// able to run, it was a natural choice of location to put code which should be tamper-resistant. +// +// Q: What happens if I modify this code? +// +// A: This code is self-defending, so any modification to it will likely break the script. +// +// Q: I would like to view the original source code for this file. +// +// A: If you have a compelling reason that you'd like to see the non-obfuscated source for this file, message me in +// game, on slack, or send me an email at benbartlett@stanford.edu. +// + +import {Colony, ColonyMemory, getAllColonies} from './Colony'; +import {DirectiveWrapper} from './directives/initializer'; +import {profile} from './profiler/decorator'; +import {GameCache} from './caching/GameCache'; +import {Overlord} from './overlords/Overlord'; +import {Visualizer} from './visuals/Visualizer'; +import {Stats} from './stats/stats'; +import {TerminalNetwork} from './logistics/TerminalNetwork'; +import {AllContracts} from './contracts/contractsList'; +import {Autonomy, getAutonomyLevel, Mem} from './memory/Memory'; +import {asciiLogoSmall} from './visuals/logos'; +import {log} from './console/log'; +import {TraderJoe} from './logistics/TradeNetwork'; +import {RoomIntel} from './intel/RoomIntel'; +import { + DEFAULT_OVERMIND_SIGNATURE, + MUON, + MY_USERNAME, + NEW_OVERMIND_INTERVAL, + PROFILER_COLONY_LIMIT, + PROFILER_INCLUDE_COLONIES, + USE_PROFILER, + USE_TRY_CATCH +} from './~settings'; +import {Strategist} from './strategy/Strategist'; +import {assimilationLocked} from './assimilation/decorator'; +import {SpawnGroup} from './logistics/SpawnGroup'; +import {alignedNewline} from './utilities/stringConstants'; +import {bulleted} from './utilities/utils'; +import {Directive} from './directives/Directive'; +import {Zerg} from './zerg/Zerg'; +import {Segmenter} from './memory/Segmenter'; +import {Overseer} from './Overseer'; +import {NotifierPriority} from './directives/Notifier'; + +// javascript-obfuscator:enable + +let profilerRooms: { [roomName: string]: boolean } = {}; +if (USE_PROFILER) { + for (let name of PROFILER_INCLUDE_COLONIES) { + profilerRooms[name] = true; + } + let myRoomNames = _.filter(_.keys(Game.rooms), name => Game.rooms[name] && Game.rooms[name].my); + for (let name of _.sample(myRoomNames, PROFILER_COLONY_LIMIT - PROFILER_INCLUDE_COLONIES.length)) { + profilerRooms[name] = true; + } +} + +@profile +@assimilationLocked +export default class _Overmind implements IOvermind { + + memory: IOvermindMemory; + overseer: Overseer; + shouldBuild: boolean; + expiration: number; + cache: GameCache; + colonies: { [roomName: string]: Colony }; // Global hash of all colony objects + directives: { [flagName: string]: Directive }; + zerg: { [creepName: string]: Zerg }; + overlords: { [ref: string]: Overlord }; + spawnGroups: { [ref: string]: SpawnGroup }; + colonyMap: { [roomName: string]: string }; // Global map of colony associations for possibly-null rooms + terminalNetwork: TerminalNetwork; + tradeNetwork: TraderJoe; + strategist: Strategist | undefined; + exceptions: Error[]; + + constructor() { + this.memory = Memory.Overmind as IOvermindMemory; + this.overseer = new Overseer(); + this.shouldBuild = true; + this.expiration = Game.time + NEW_OVERMIND_INTERVAL; + this.cache = new GameCache(); + this.colonies = {}; + this.directives = {}; + this.zerg = {}; + this.overlords = {}; + this.spawnGroups = {}; + this.colonyMap = {}; + this.terminalNetwork = this.makeTerminalNetwork(); + this.tradeNetwork = new TraderJoe(); + this.strategist = getAutonomyLevel() > Autonomy.Manual ? new Strategist() : undefined; + this.exceptions = []; + } + + /* Global instantiation of Overmind object; run once every global refresh */ + build(): void { + log.debug(`Rebuilding Overmind object!`); + this.cache.build(); + // Register all colonies and instantiate their overlords + this.registerColonies(); + _.forEach(this.colonies, colony => colony.spawnMoarOverlords()); + // Register directives and instantiate their overlords; must be done AFTER colonies + this.registerDirectives(); + _.forEach(this.directives, directive => directive.spawnMoarOverlords()); + this.shouldBuild = false; + } + + /* Refresh the state of the Overmind; run at the beginning of every tick */ + refresh(): void { + this.shouldBuild = true; // assume refresh will be unsuccessful + // Refresh constructor-phase objects + this.memory = Memory.Overmind as IOvermindMemory; + this.exceptions = []; + this.cache.refresh(); + this.overseer.refresh(); + this.terminalNetwork.refresh(); + this.tradeNetwork.refresh(); + if (this.strategist) this.strategist.refresh(); + // Refresh build-phase objects + this.refreshColonies(); + this.refreshDirectives(); + for (let ref in this.overlords) { + this.overlords[ref].refresh(); + } + for (let ref in this.spawnGroups) { + this.spawnGroups[ref].refresh(); + } + this.shouldBuild = false; // refresh successful + } + + private try(callback: () => any, identifier?: string): void { + if (USE_TRY_CATCH) { + try { + callback(); + } catch (e) { + if (identifier) { + e.name = `Caught unhandled exception at ${'' + callback} (identifier: ${identifier}): \n` + + e.name + '\n' + e.stack; + } else { + e.name = `Caught unhandled exception at ${'' + callback}: \n` + e.name + '\n' + e.stack; + } + this.exceptions.push(e); + } + } else { + callback(); + } + } + + private handleExceptions(): void { + if (this.exceptions.length == 0) { + return; + } else { + log.warning(`Exceptions present this tick! Rebuilding Overmind object in next tick.`); + Memory.stats.persistent.lastErrorTick = Game.time; + this.shouldBuild = true; + this.expiration = Game.time; + if (this.exceptions.length == 1) { + throw _.first(this.exceptions); + } else { + for (let e of this.exceptions) { + log.throw(e); + } + let error = new Error('Multiple exceptions caught this tick!'); + error.stack = _.map(this.exceptions, e => e.name).join('\n'); + throw error; + } + } + } + + private makeTerminalNetwork(): TerminalNetwork { + // Initialize the terminal netowrk + let terminals: StructureTerminal[] = []; + for (let name in Game.rooms) { + if (USE_PROFILER && !profilerRooms[name]) continue; + let room = Game.rooms[name]; + if (room.my && room.controller!.level >= 6 && room.terminal && room.terminal.my) { + terminals.push(room.terminal); + } + } + return new TerminalNetwork(terminals); + } + + + /* Instantiate a new colony for each owned rom */ + private registerColonies(): void { + + let colonyOutposts: { [roomName: string]: string[] } = {}; // key: lead room, values: outposts[] + this.colonyMap = {}; + // Register colonies to their outposts + let flagsByRoom = _.groupBy(this.cache.outpostFlags, flag => flag.memory[_MEM.COLONY]); + for (let name in Game.rooms) { + if (Game.rooms[name].my) { + let colonyMemory = Memory.colonies[name] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + this.overseer.notifier.alert("Colony suspended", name, NotifierPriority.High); + continue; + } + if (Game.rooms[name].flags) + colonyOutposts[name] = _.map(flagsByRoom[name], + flag => flag.memory.setPosition + ? derefRoomPosition(flag.memory.setPosition).roomName + : flag.pos.roomName); + this.colonyMap[name] = name; + } + } + // Register outposts to their colonies + for (let colonyName in colonyOutposts) { + for (let outpostName of colonyOutposts[colonyName]) { + this.colonyMap[outpostName] = colonyName; + } + } + + // Initialize the Colonies and pass each one its assigned creeps + let id = 0; + for (let name in colonyOutposts) { + if (USE_PROFILER && !profilerRooms[name]) { + if (Game.time % 20 == 0) { + log.alert(`Suppressing instantiation of colony ${name}.`); + } + continue; // don't make colony for this room + } + try { + this.colonies[name] = new Colony(id, name, colonyOutposts[name]); + } catch (e) { + e.name = `Caught unhandled exception instantiating colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + id++; + } + } + + private refreshColonies(): void { + for (let name in this.colonies) { + try { + this.colonies[name].refresh(); + } catch (e) { + e.name = `Caught unhandled exception refreshing colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + } + } + + /* Wrap each flag in a color coded wrapper */ + private registerDirectives(spawnOverlords = false): void { + // Create a directive for each flag (registration takes place on construction) + for (let name in Game.flags) { + if (this.directives[name]) { // skip existing directives + continue; + } + let colonyName = Game.flags[name].memory[_MEM.COLONY]; + if (colonyName) { + if (USE_PROFILER && !profilerRooms[colonyName]) { + continue; // don't make directive if colony is ignored for profiling + } + let colonyMemory = Memory.colonies[colonyName] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + continue; // don't make directive if colony is suspended + } + } + const directive = DirectiveWrapper(Game.flags[name]); + if (directive && spawnOverlords) { + directive.spawnMoarOverlords(); + } + if (!directive && Game.time % 10 == 0) { + // log.alert(`Flag [${name} @ ${Game.flags[name].pos.print}] does not match ` + + // `a valid directive color code! (Refer to /src/directives/initializer.ts)` + alignedNewline + + // `Use removeErrantFlags() to remove flags which do not match a directive.`); + } + } + } + + /* Refresh all directives, adding new ones for new flags */ + private refreshDirectives(): void { + for (let name in this.directives) { // this should be called first + this.directives[name].refresh(); + } + this.registerDirectives(true); // register any new directives that were placed since last rebuild + } + + /* Intialize everything in pre-init phase of main loop. Does not call colony.init(). */ + init(): void { + // Initialize the overseer + this.overseer.init(); + // Initialize each colony + for (let colonyName in this.colonies) { + let start = Game.cpu.getUsed(); + this.try(() => this.colonies[colonyName].init(), colonyName); + Stats.log(`cpu.usage.${colonyName}.init`, Game.cpu.getUsed() - start); + } + // Initialize spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].init(), ref); + } + // Initialize terminalNetwork and tradeNetwork + this.try(() => this.terminalNetwork.init()); + this.try(() => this.tradeNetwork.init()); + // Initialize strategist + if (this.strategist) { + this.try(() => this.strategist!.init()); + } + } + + run(): void { + // Enforce behavioral locks every 3 ticks + if (Game.time % 3 == 0) { + IntelManagement.run(); + } + // Run spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].run(), ref); + } + // Run the overseer + this.overseer.run(); + // Run all colonies + for (let colonyName in this.colonies) { + this.try(() => this.colonies[colonyName].run(), colonyName); + } + // Run all contracts + if (MY_USERNAME == MUON) { // This ensures that my contracts don't run by default on other people's accounts + for (let contract of AllContracts) { + this.try(() => contract.run()); + } + } + // Run terminal network + this.try(() => this.terminalNetwork.run()); + // Run trade network + this.try(() => this.tradeNetwork.run()); + // Run strategist + if (this.strategist) { + this.try(() => this.strategist!.run()); + } + // Run room intel + this.try(() => RoomIntel.run()); // this should be at end, right before assimilator + // Run assimilator + this.try(() => Assimilator.run()); // this should always be last + } + + postRun(): void { + // Run version updater + this.try(() => VersionUpdater.run()); + // Run segmenter + this.try(() => Segmenter.run()); + // Handle exceptions + this.handleExceptions(); + } + + visuals(): void { + if (Game.cpu.bucket > 9000) { + // Draw global visuals + Visualizer.visuals(); + // Add notifications for outdated version + if (VersionUpdater.memory.newestVersion) { + const newestVersion = VersionUpdater.memory.newestVersion; + if (VersionUpdater.isVersionOutdated(newestVersion)) { + this.overseer.notifier.alert(`[!] Update available: ${__VERSION__} → ${newestVersion}`, + undefined, -1); + } + } + // Draw overseer visuals + this.overseer.visuals(); + // Draw colony visuals + for (let colonyName in this.colonies) { + this.colonies[colonyName].visuals(); + } + } else { + if (Game.time % 10 == 0) { + log.info(`CPU bucket is too low (${Game.cpu.bucket}) - skip rendering visuals.`); + } + } + } +} + + +// Behavioral locks ==================================================================================================== + +/* class BehavioralLocks */ +class IntelManagement { // Renamed to IntelManagement to avoid identification + + /* static enforceSignature(): void */ + static runRoomIntel_1(): void { + let badSignatures: string[] = []; + let colonies = getAllColonies(); + if (colonies.length == 0) return; + for (let colony of colonies) { + if (colony.defcon > 0 || colony.creeps.length == 0) { + continue; + } + let controller = colony.controller; + if (controller.signedByScreeps || controller.level < 4) { + continue; + } + let validSignature = false; + if (controller.sign) { + let sig = controller.sign.text; + if (sig.toLowerCase().includes('overmind') + || sig.includes('\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05')) { + validSignature = true; + } + } + if (!validSignature) { + badSignatures.push(controller.sign ? controller.sign.text : 'undefined'); + } + } + // Throw an error if more than half of rooms have a bad signature + if (badSignatures.length >= 0.5 * _.keys(Overmind.colonies).length) { + Memory.settings.signature = DEFAULT_OVERMIND_SIGNATURE; + log.warning(`Invalid controller signatures detected:` + + bulleted(badSignatures) + alignedNewline + + `Signatures must contain the string "Overmind" or ` + + `${'\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05'}.`); + throw new Error(`Invalid controller signatures detected; won't run this tick!`); + } + } + + /* static enforceIllegalDirectives */ + static runRoomIntel_2(): void { + if (!Assimilator.isAssimilated(MY_USERNAME)) { // if you are not assimilated + const illegalColorCombinations: ColorConstant[][] = [[COLOR_RED, COLOR_RED]]; + for (let name in Game.flags) { + const flag = Game.flags[name]; + const colors = [flag.color, flag.secondaryColor]; + if (illegalColorCombinations.includes(colors)) { + flag.remove(); + log.warning(`Illegal directives detected and removed: ${flag.name}. ` + + `Assimilation required to access these features.`); + } + } + } + } + + static run(): void { + this.runRoomIntel_1(); + if (Game.time % (3 * 31) == 0) { // must be multiple of 3 + this.runRoomIntel_2(); + } + } + +} + + +// Version updater ===================================================================================================== + +interface VersionSegmentData { + version: string; +} + +interface VersionUpdaterMemory { + versions: { [version: string]: any }; + newestVersion: string | undefined; +} + +class VersionUpdater { + + static CheckFrequency = 100; + static CheckOnTick = 91; + static VersionSegment = 99; + + static get memory(): VersionUpdaterMemory { + return Mem.wrap(Memory.Overmind, 'versionUpdater', { + versions : {}, + newestVersion: undefined, + }); + } + + private static slave_fetchVersion(): string | undefined { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.requestForeignSegment(MUON, this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick) { + let data = Segmenter.getForeignSegment() as VersionSegmentData | undefined; + if (data) { + return data.version; + } + } + } + + static isVersionOutdated(newVersion: string): boolean { + let [major, minor, patch] = _.map(__VERSION__.split('.'), + str => parseInt(str, 10)) as number[]; + let [newMajor, newMinor, newPatch] = _.map(newVersion.split('.'), + str => parseInt(str, 10)) as number[]; + return (newMajor > major || newMinor > minor || newPatch > patch); + } + + private static master_pushVersion(): void { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 2) { + Segmenter.requestSegments(this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.markSegmentAsPublic(this.VersionSegment); + Segmenter.setSegmentProperty(this.VersionSegment, 'version', __VERSION__); + } + } + + static generateUpdateMessage(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let msg = '\n'; + for (let line of asciiLogoSmall) { + msg += line + '\n'; + } + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = '╔═════════════════════════════════════════════════════════╗\n' + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + '╚═════════════════════════════════════════════════════════╝'; + return msg + updateMsg; + } + + static generateUpdateMessageSmall(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = `╔═════════════════════════════════╗\n` + + `║ OVERMIND SCREEPS AI ║\n` + + `╠═════════════════════════════════╣\n` + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + `╚═════════════════════════════════╝`; + return '\n' + updateMsg; + } + + static displayUpdateMessage(newVersion: string): void { + const updateMessage = this.generateUpdateMessage(__VERSION__, newVersion); + console.log(`${updateMessage}`); + } + + static sayUpdateMessage(newVersion: string): void { + for (let name in Game.creeps) { + let creep = Game.creeps[name]; + creep.say('Update me!', true); + } + } + + static notifyNewVersion(newVersion: string): void { + const updateMessage = this.generateUpdateMessageSmall(__VERSION__, newVersion); + Game.notify(`${updateMessage}`); + } + + static run(): void { + if (MY_USERNAME == MUON) { + this.master_pushVersion(); + } + // Update version + let fetchedVersion = this.slave_fetchVersion(); + if (fetchedVersion) { + this.memory.newestVersion = fetchedVersion; + } + // Check for new version + const newestVersion = this.memory.newestVersion; + if (newestVersion && this.isVersionOutdated(newestVersion)) { + if (Game.time % 10 == 0) { + this.displayUpdateMessage(newestVersion); + this.sayUpdateMessage(newestVersion); + } + if (Game.time % 10000 == 0 /*!this.memory.versions[newVersion]*/) { + this.notifyNewVersion(newestVersion); + } + } + } + +} + + diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 99337f91d..f49de8b52 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -22,6 +22,8 @@ export const Roles = { ranged : 'hydralisk', healer : 'transfuser', dismantler: 'lurker', + drill : 'drill', + coolant : 'coolant', }; /** @@ -307,4 +309,19 @@ export const CombatSetups = { }, + drill: { + default: new CreepSetup(Roles.drill, { + pattern : [MOVE, ATTACK, ATTACK, MOVE], + sizeLimit: Infinity, + }), + }, + + coolant: { + default: new CreepSetup(Roles.coolant, { + pattern : [HEAL, MOVE], + sizeLimit: Infinity, + }), + } + + }; diff --git a/src/directives/initializer.ts b/src/directives/initializer.ts index 7b7637ce1..0a0a1e052 100644 --- a/src/directives/initializer.ts +++ b/src/directives/initializer.ts @@ -25,6 +25,7 @@ import {DirectiveExtract} from './resource/extract'; import {DirectiveSwarmDestroy} from './offense/swarmDestroy'; import {DirectiveOutpostDefense} from './defense/outpostDefense'; import {DirectiveClearRoom} from './colony/clearRoom'; +import {DirectivePowerMine} from "./resource/powerMine"; /** * This is the initializer for directives, which maps flags by their color code to the corresponding directive @@ -92,6 +93,8 @@ export function DirectiveWrapper(flag: Flag): Directive | undefined { return new DirectiveExtract(flag); case COLOR_BLUE: return new DirectiveHaul(flag); + case COLOR_RED: + return new DirectivePowerMine(flag); } break; diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 8e1a27636..6dff8f288 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -2,6 +2,7 @@ import {Directive} from '../Directive'; import {profile} from '../../profiler/decorator'; import {isStoreStructure} from '../../declarations/typeGuards'; import {HaulingOverlord} from '../../overlords/situational/hauler'; +import {PowerHaulingOverlord} from "../../overlords/powerMining/PowerHauler"; interface DirectiveHaulMemory extends FlagMemory { @@ -10,7 +11,7 @@ interface DirectiveHaulMemory extends FlagMemory { /** - * Hauling directive: spawns hauler creeps to move large amounts of resourecs from a location (e.g. draining a storage) + * Hauling directive: spawns hauler creeps to move large amounts of resources from a location (e.g. draining a storage) */ @profile export class DirectiveHaul extends Directive { @@ -29,7 +30,11 @@ export class DirectiveHaul extends Directive { } spawnMoarOverlords() { - this.overlords.haul = new HaulingOverlord(this); + if (this.room && this.pos.lookForStructure(STRUCTURE_POWER_BANK)) { + this.overlords.haul = new PowerHaulingOverlord(this); + } else { + this.overlords.haul = new HaulingOverlord(this); + } } get targetedBy(): string[] { diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts new file mode 100644 index 000000000..e6d3f9672 --- /dev/null +++ b/src/directives/resource/powerMine.ts @@ -0,0 +1,107 @@ +import {Directive} from '../Directive'; +import {profile} from '../../profiler/decorator'; +import {isStoreStructure} from '../../declarations/typeGuards'; +import {PowerDrillOverlord} from '../../overlords/powerMining/PowerDrill'; + + +interface DirectivePowerMineMemory extends FlagMemory { + totalResources?: number; +} + + +/** + * PowerMining directive: kills power banks and collects the resources. + */ +@profile +export class DirectivePowerMine extends Directive { + + static directiveName = 'powerMine'; + static color = COLOR_YELLOW; + static secondaryColor = COLOR_RED; + + private _powerBank: StructurePowerBank | undefined; + + private _store: StoreDefinition; + private _drops: { [resourceType: string]: Resource[] }; + + memory: DirectivePowerMineMemory; + + constructor(flag: Flag) { + super(flag); + } + + spawnMoarOverlords() { + this.overlords.powerMine = new PowerDrillOverlord(this); + } + + get targetedBy(): string[] { + return Overmind.cache.targets[this.ref]; + } + + get drops(): { [resourceType: string]: Resource[] } { + if (!this.pos.isVisible) { + return {}; + } + if (!this._drops) { + let drops = (this.pos.lookFor(LOOK_RESOURCES) || []) as Resource[]; + this._drops = _.groupBy(drops, drop => drop.resourceType); + } + return this._drops; + } + + get hasDrops(): boolean { + return _.keys(this.drops).length > 0; + } + + // get store(): StoreDefinition { + // if (!this._store) { + // // Merge the "storage" of drops with the store of structure + // let store: { [resourceType: string]: number } = {}; + // if (this.storeStructure) { + // if (isStoreStructure(this.storeStructure)) { + // store = this.storeStructure.store; + // } else { + // store = {'energy': this.storeStructure.energy}; + // } + // } else { + // store = {'energy': 0}; + // } + // // Merge with drops + // for (let resourceType of _.keys(this.drops)) { + // let totalResourceAmount = _.sum(this.drops[resourceType], drop => drop.amount); + // if (store[resourceType]) { + // store[resourceType] += totalResourceAmount; + // } else { + // store[resourceType] = totalResourceAmount; + // } + // } + // this._store = store as StoreDefinition; + // } + // return this._store; + // } + + /** + * Total amount of resources remaining to be transported; cached into memory in case room loses visibility + */ + get totalResources(): number { + if (this.memory.totalResources == undefined) { + return 5000; // pick some non-zero number so that powerMiners will spawn + } + if (this.pos.isVisible) { + this.memory.totalResources = this._powerBank ? this._powerBank.power : this.memory.totalResources; // update total amount remaining + } + return this.memory.totalResources; + } + + init(): void { + this.alert(`PowerMine directive active`); + } + + run(): void { + // if (_.sum(this.store) == 0 && this.pos.isVisible) { + // //this.remove(); + // } + } + +} + diff --git a/src/main.ts b/src/main.ts index 3239f1ae2..fe051121d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,7 +33,7 @@ import {Mem} from './memory/Memory'; import {OvermindConsole} from './console/Console'; import {Stats} from './stats/stats'; import profiler from './profiler/screeps-profiler'; -import _Overmind from './Overmind_obfuscated'; // this should be './Overmind_obfuscated' unless you are me +import _Overmind from './Overmind_ts'; // this should be './Overmind_obfuscated' unless you are me import {VersionMigration} from './versionMigration/migrator'; import {RemoteDebugger} from './debug/remoteDebugger'; // ===================================================================================================================== diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts new file mode 100644 index 000000000..b82f7f74b --- /dev/null +++ b/src/overlords/powerMining/PowerDrill.ts @@ -0,0 +1,130 @@ +import {CombatZerg} from '../../zerg/CombatZerg'; +import {DirectiveSKOutpost} from '../../directives/colony/outpostSK'; +import {RoomIntel} from '../../intel/RoomIntel'; +import {minBy} from '../../utilities/utils'; +import {Mem} from '../../memory/Memory'; +import {debug, log} from '../../console/log'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {Visualizer} from '../../visuals/Visualizer'; +import {profile} from '../../profiler/decorator'; +import {CombatOverlord} from '../CombatOverlord'; +import {CombatSetups, Roles} from '../../creepSetups/setups'; +import {OverlordMemory} from '../Overlord'; +import {DirectivePowerMine} from "../../directives/resource/powerMine"; +import {DirectiveHaul} from "../../directives/resource/haul"; +import {calculateFormationStrength} from "../../utilities/creepUtils"; + +interface PowerDrillOverlordMemory extends OverlordMemory { + targetPBID?: string; +} + +/** + * PowerDrillOverlord -- spawns drills and coolant to mine power banks + */ +@profile +export class PowerDrillOverlord extends CombatOverlord { + + static requiredRCL = 7; + static haulerPrespawn = 600; + + directive: DirectiveSKOutpost; + memory: PowerDrillOverlordMemory; + targetPowerBank: StructurePowerBank | undefined; + + drills: CombatZerg[]; + coolant: CombatZerg[]; + + constructor(directive: DirectivePowerMine, priority = OverlordPriority.powerMine.drill) { + super(directive, 'powerDrill', priority, PowerDrillOverlord.requiredRCL); + this.directive = directive; + this.priority += this.outpostIndex * OverlordPriority.powerMine.roomIncrement; + this.drills = this.combatZerg(Roles.drill); + this.coolant = this.combatZerg(Roles.coolant); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + } + + refresh() { + super.refresh(); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + + } + + init() { + this.wishlist(3, CombatSetups.drill.default); + this.wishlist(4, CombatSetups.coolant.default); + } + + private handleDrill(drill: CombatZerg) { + if (!this.targetPowerBank) { + if (!this.room) { + // We are not there yet + } else { + var bank = this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank; + this.targetPowerBank = bank; + // If power bank is dead + if (bank == undefined) { + Game.notify("Power bank in " + this.room + " is dead."); + this.directive.remove(); + Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); + drill.suicide(); + return; + } + } + } + + // Go to keeper room + if (!this.room || drill.room != this.room || drill.pos.isEdge || !this.targetPowerBank) { + // log.debugCreep(drill, `Going to room!`); + Game.notify("Drill is moving to power site in " + this.room + "."); + drill.goTo(this.pos); + return; + } else if (this.targetPowerBank.hits < 800000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + DirectiveHaul.create(this.pos); + } + // Spawn a hauler for this location + // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep + + // Handle killing bank + if (drill.hits > 100) { + drill.goTo(this.targetPowerBank); + drill.attack(this.targetPowerBank); + } + } + + private handleCoolant(coolant: CombatZerg) { + // Go to powerbank room + if (!this.room || coolant.room != this.room || coolant.pos.isEdge) { + // log.debugCreep(reaper, `Going to room!`); + coolant.healSelfIfPossible(); + coolant.goTo(this.pos); + return; + } else if (!this.targetPowerBank) { + // If power bank is dead + Game.notify("Power bank in " + this.room + " is dead."); + coolant.suicide(); + return; + } else if (this.targetPowerBank.hits < 50000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + Game.notify("Power bank in " + this.room + ", beginning haul operation."); + //DirectiveHaul.create(this.pos); + } + if (coolant.pos.getRangeTo(_.first(this.drills)) > 1) { + coolant.goTo(_.first(this.drills)); + } + + coolant.autoHeal(false); + } + + run() { + this.autoRun(this.drills, drill => this.handleDrill(drill)); + this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); + } + + visuals() { + if (this.room && this.targetPowerBank) { + Visualizer.marker(this.targetPowerBank.pos); + } + } + +} diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts new file mode 100644 index 000000000..632ce0654 --- /dev/null +++ b/src/overlords/powerMining/PowerHauler.ts @@ -0,0 +1,151 @@ +import {Overlord} from '../Overlord'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {Zerg} from '../../zerg/Zerg'; +import {DirectiveHaul} from '../../directives/resource/haul'; +import {Tasks} from '../../tasks/Tasks'; +import {isStoreStructure} from '../../declarations/typeGuards'; +import {log} from '../../console/log'; +import {Pathing} from '../../movement/Pathing'; +import {Energetics} from '../../logistics/Energetics'; +import {profile} from '../../profiler/decorator'; +import {Roles, Setups} from '../../creepSetups/setups'; +import {HaulingOverlord} from "../situational/hauler"; +import {calculateFormationStrength} from "../../utilities/creepUtils"; + +/** + * Spawns special-purpose haulers for transporting resources to/from a specified target + */ +@profile +export class PowerHaulingOverlord extends HaulingOverlord { + + haulers: Zerg[]; + directive: DirectiveHaul; + powerBank: StructurePowerBank | undefined; + tickToSpawnOn: number; + numHaulers: number; + + requiredRCL = 6; + // Allow time for body to spawn + prespawnAmount = 200; + + constructor(directive: DirectiveHaul, priority = OverlordPriority.collectionUrgent.haul) { + super(directive, priority); // Removed 'haul' string + this.directive = directive; + this.haulers = this.zerg(Roles.transport); + } + + init() { + if (!this.colony.storage || _.sum(this.colony.storage.store) > Energetics.settings.storage.total.cap) { + return; + } + // Spawn a number of haulers sufficient to move all resources within a lifetime, up to a max + let MAX_HAULERS = 5; + // Calculate total needed amount of hauling power as (resource amount * trip distance) + let tripDistance = 2 * Pathing.distance((this.colony.storage || this.colony).pos, this.directive.pos); + let haulingPowerNeeded = Math.min(this.directive.totalResources, + this.colony.storage.storeCapacity + - _.sum(this.colony.storage.store)) * tripDistance; + // Calculate amount of hauling each hauler provides in a lifetime + let haulerCarryParts = Setups.transporters.early.getBodyPotential(CARRY, this.colony); + let haulingPowerPerLifetime = CREEP_LIFE_TIME * haulerCarryParts * CARRY_CAPACITY; + // Calculate number of haulers + this.numHaulers = Math.min(Math.ceil(haulingPowerNeeded / haulingPowerPerLifetime), MAX_HAULERS); + // Request the haulers + this.tickToSpawnOn = Game.time + (this.calculateRemainingLifespan() || 0) - this.prespawnAmount; + } + + + calculateRemainingLifespan() { + if (!this.room) { + return undefined; + } else if (this.powerBank == undefined) { + // Power Bank is gone + return 0; + } else { + let tally = calculateFormationStrength(this.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); + let healStrength: number = tally.heal * HEAL_POWER || 0; + let attackStrength: number = tally.attack * ATTACK_POWER || 0; + // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing + let avgDamagePerTick = Math.min(attackStrength, healStrength*2); + return this.powerBank.hits / avgDamagePerTick; + } + } + + protected handleHauler(hauler: Zerg) { + if (_.sum(hauler.carry) == 0) { + // Travel to directive and collect resources + if (hauler.inSameRoomAs(this.directive)) { + // Pick up drops first + if (this.directive.hasDrops) { + let allDrops: Resource[] = _.flatten(_.values(this.directive.drops)); + let drop = allDrops[0]; + if (drop) { + hauler.task = Tasks.pickup(drop); + return; + } + } + // Withdraw from store structure + if (this.directive.storeStructure) { + let store: { [resourceType: string]: number } = {}; + if (isStoreStructure(this.directive.storeStructure)) { + store = this.directive.storeStructure.store; + } else { + store = {'energy': this.directive.storeStructure.energy}; + } + for (let resourceType in store) { + if (store[resourceType] > 0) { + hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType); + return; + } + } + } + // Shouldn't reach here + log.warning(`${hauler.name} in ${hauler.room.print}: nothing to collect!`); + } else { + // hauler.task = Tasks.goTo(this.directive); + hauler.goTo(this.directive); + } + } else { + // Travel to colony room and deposit resources + if (hauler.inSameRoomAs(this.colony)) { + // Put energy in storage and minerals in terminal if there is one + for (let resourceType in hauler.carry) { + if (hauler.carry[resourceType] == 0) continue; + if (resourceType == RESOURCE_ENERGY) { // prefer to put energy in storage + if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.storage, resourceType); + return; + } else if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.terminal, resourceType); + return; + } + } else { // prefer to put minerals in terminal + if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.terminal, resourceType); + return; + } else if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.storage, resourceType); + return; + } + } + } + // Shouldn't reach here + log.warning(`${hauler.name} in ${hauler.room.print}: nowhere to put resources!`); + } else { + hauler.task = Tasks.goToRoom(this.colony.room.name); + } + } + } + + run() { + if (Game.time >= this.tickToSpawnOn && this.haulers.length == 0) { + this.wishlist(this.numHaulers, Setups.transporters.early); + } + for (let hauler of this.haulers) { + if (hauler.isIdle) { + this.handleHauler(hauler); + } + hauler.run(); + } + } +} \ No newline at end of file diff --git a/src/overlords/situational/hauler.ts b/src/overlords/situational/hauler.ts index 86bfd43a3..ffd527cca 100644 --- a/src/overlords/situational/hauler.ts +++ b/src/overlords/situational/hauler.ts @@ -19,7 +19,7 @@ export class HaulingOverlord extends Overlord { haulers: Zerg[]; directive: DirectiveHaul; - requiredRCL: 4; + requiredRCL: number = 4; constructor(directive: DirectiveHaul, priority = directive.hasDrops ? OverlordPriority.collectionUrgent.haul : OverlordPriority.collection.haul) { @@ -48,7 +48,7 @@ export class HaulingOverlord extends Overlord { this.wishlist(numHaulers, Setups.transporters.early); } - private handleHauler(hauler: Zerg) { + protected handleHauler(hauler: Zerg) { if (_.sum(hauler.carry) == 0) { // Travel to directive and collect resources if (hauler.inSameRoomAs(this.directive)) { diff --git a/src/priorities/priorities_overlords.ts b/src/priorities/priorities_overlords.ts index 356b7e676..71d38c399 100644 --- a/src/priorities/priorities_overlords.ts +++ b/src/priorities/priorities_overlords.ts @@ -72,6 +72,13 @@ export var OverlordPriority = { roomIncrement: 5, }, + powerMine: { + cool : 1050, + drill : 1051, + gather : 604, + roomIncrement: 5, + }, + collection: { // Non-urgent collection of resources, like from a deserted storage haul: 1100 }, diff --git a/src/utilities/creepUtils.ts b/src/utilities/creepUtils.ts new file mode 100644 index 000000000..7e5b1a6b7 --- /dev/null +++ b/src/utilities/creepUtils.ts @@ -0,0 +1,45 @@ +// Creep utilities that don't belong anywhere else + + +// Does not account for range, just total of body parts +export function calculateFormationStrength(creeps : Creep[]): Record { + let tally: Record = { + move : 0, + work : 0, + carry : 0, + attack : 0, + ranged_attack : 0, + tough : 0, + heal : 0, + claim : 0, + }; + + _.forEach(creeps, + function (unit) { + let individualTally = calculateBodyPotential(unit.body); + for (let bodyType in individualTally) { + let type = bodyType as BodyPartConstant; + tally[type] += individualTally[type]; + } + }); + return tally; +} + +export function calculateBodyPotential(body : BodyPartDefinition[]): Record { + let tally: Record = { + move : 0, + work : 0, + carry : 0, + attack : 0, + ranged_attack : 0, + tough : 0, + heal : 0, + claim : 0, + }; + _.forEach(body, function (bodyPart) { + // Needs boost logic + tally[bodyPart.type] += 1; + } + ); + return tally; +} From 5a3314b1011cf2bb2ad311fa67036105f680b6a1 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 22 Apr 2019 15:31:48 -0700 Subject: [PATCH 020/137] Initial power harvesting commit --- src/Overmind_ts.ts | 590 +++++++++++++++++++++++ src/creepSetups/setups.ts | 17 + src/directives/initializer.ts | 3 + src/directives/resource/haul.ts | 9 +- src/directives/resource/powerMine.ts | 107 ++++ src/overlords/powerMining/PowerDrill.ts | 130 +++++ src/overlords/powerMining/PowerHauler.ts | 151 ++++++ src/overlords/situational/hauler.ts | 4 +- src/priorities/priorities_overlords.ts | 7 + src/utilities/creepUtils.ts | 45 ++ 10 files changed, 1059 insertions(+), 4 deletions(-) create mode 100644 src/Overmind_ts.ts create mode 100644 src/directives/resource/powerMine.ts create mode 100644 src/overlords/powerMining/PowerDrill.ts create mode 100644 src/overlords/powerMining/PowerHauler.ts create mode 100644 src/utilities/creepUtils.ts diff --git a/src/Overmind_ts.ts b/src/Overmind_ts.ts new file mode 100644 index 000000000..ad3762532 --- /dev/null +++ b/src/Overmind_ts.ts @@ -0,0 +1,590 @@ +// javascript-obfuscator:disable +// +// Overmind_obfuscated.js: this file is intentially obfuscated to prevent tampering. +// +// Q: Why is this file obfuscated? +// +// A: Using Overmind as your AI puts you at a huge advantage if you are a new player in a novice zone. Screeps has +// always had problems with people downloading bots from the internet and stomping new players. I have kept Overmind +// open-sourced because I think it can be a good resource for learning how to play Screeps, but I don't want it to +// be abused as a noob-crushing machine. In the future, I will be implementing behavioral locks in this file which +// limit unreasonable aggression toward peaceful new players. +// +// Q: What kind of behavioral locks? +// +// A: Players will be able to opt out of aggression by setting a property in their public memory. Overmind bots will not +// attack the player unless they attack you, claim a room in Overmind's territory, or occupy a room which is +// critically important (for example, very desirable mineral deposits that aren't available elsewhere). Overmind +// will attempt to expand around players which pose no threat to it rather than eliminating them. +// +// Q: What does this file do? +// +// A: The Overmind object is the top-level initializer of the AI and instantiates all colonies and directives. It is +// also responsible for some high-level decision making. You can see the enumerated properties of the Overmind class +// in IOvermind in declarations/index.d.ts. Since this file is sufficiently complex and is critical for the AI to be +// able to run, it was a natural choice of location to put code which should be tamper-resistant. +// +// Q: What happens if I modify this code? +// +// A: This code is self-defending, so any modification to it will likely break the script. +// +// Q: I would like to view the original source code for this file. +// +// A: If you have a compelling reason that you'd like to see the non-obfuscated source for this file, message me in +// game, on slack, or send me an email at benbartlett@stanford.edu. +// + +import {Colony, ColonyMemory, getAllColonies} from './Colony'; +import {DirectiveWrapper} from './directives/initializer'; +import {profile} from './profiler/decorator'; +import {GameCache} from './caching/GameCache'; +import {Overlord} from './overlords/Overlord'; +import {Visualizer} from './visuals/Visualizer'; +import {Stats} from './stats/stats'; +import {TerminalNetwork} from './logistics/TerminalNetwork'; +import {AllContracts} from './contracts/contractsList'; +import {Autonomy, getAutonomyLevel, Mem} from './memory/Memory'; +import {asciiLogoSmall} from './visuals/logos'; +import {log} from './console/log'; +import {TraderJoe} from './logistics/TradeNetwork'; +import {RoomIntel} from './intel/RoomIntel'; +import { + DEFAULT_OVERMIND_SIGNATURE, + MUON, + MY_USERNAME, + NEW_OVERMIND_INTERVAL, + PROFILER_COLONY_LIMIT, + PROFILER_INCLUDE_COLONIES, + USE_PROFILER, + USE_TRY_CATCH +} from './~settings'; +import {Strategist} from './strategy/Strategist'; +import {assimilationLocked} from './assimilation/decorator'; +import {SpawnGroup} from './logistics/SpawnGroup'; +import {alignedNewline} from './utilities/stringConstants'; +import {bulleted} from './utilities/utils'; +import {Directive} from './directives/Directive'; +import {Zerg} from './zerg/Zerg'; +import {Segmenter} from './memory/Segmenter'; +import {Overseer} from './Overseer'; +import {NotifierPriority} from './directives/Notifier'; + +// javascript-obfuscator:enable + +let profilerRooms: { [roomName: string]: boolean } = {}; +if (USE_PROFILER) { + for (let name of PROFILER_INCLUDE_COLONIES) { + profilerRooms[name] = true; + } + let myRoomNames = _.filter(_.keys(Game.rooms), name => Game.rooms[name] && Game.rooms[name].my); + for (let name of _.sample(myRoomNames, PROFILER_COLONY_LIMIT - PROFILER_INCLUDE_COLONIES.length)) { + profilerRooms[name] = true; + } +} + +@profile +@assimilationLocked +export default class _Overmind implements IOvermind { + + memory: IOvermindMemory; + overseer: Overseer; + shouldBuild: boolean; + expiration: number; + cache: GameCache; + colonies: { [roomName: string]: Colony }; // Global hash of all colony objects + directives: { [flagName: string]: Directive }; + zerg: { [creepName: string]: Zerg }; + overlords: { [ref: string]: Overlord }; + spawnGroups: { [ref: string]: SpawnGroup }; + colonyMap: { [roomName: string]: string }; // Global map of colony associations for possibly-null rooms + terminalNetwork: TerminalNetwork; + tradeNetwork: TraderJoe; + strategist: Strategist | undefined; + exceptions: Error[]; + + constructor() { + this.memory = Memory.Overmind as IOvermindMemory; + this.overseer = new Overseer(); + this.shouldBuild = true; + this.expiration = Game.time + NEW_OVERMIND_INTERVAL; + this.cache = new GameCache(); + this.colonies = {}; + this.directives = {}; + this.zerg = {}; + this.overlords = {}; + this.spawnGroups = {}; + this.colonyMap = {}; + this.terminalNetwork = this.makeTerminalNetwork(); + this.tradeNetwork = new TraderJoe(); + this.strategist = getAutonomyLevel() > Autonomy.Manual ? new Strategist() : undefined; + this.exceptions = []; + } + + /* Global instantiation of Overmind object; run once every global refresh */ + build(): void { + log.debug(`Rebuilding Overmind object!`); + this.cache.build(); + // Register all colonies and instantiate their overlords + this.registerColonies(); + _.forEach(this.colonies, colony => colony.spawnMoarOverlords()); + // Register directives and instantiate their overlords; must be done AFTER colonies + this.registerDirectives(); + _.forEach(this.directives, directive => directive.spawnMoarOverlords()); + this.shouldBuild = false; + } + + /* Refresh the state of the Overmind; run at the beginning of every tick */ + refresh(): void { + this.shouldBuild = true; // assume refresh will be unsuccessful + // Refresh constructor-phase objects + this.memory = Memory.Overmind as IOvermindMemory; + this.exceptions = []; + this.cache.refresh(); + this.overseer.refresh(); + this.terminalNetwork.refresh(); + this.tradeNetwork.refresh(); + if (this.strategist) this.strategist.refresh(); + // Refresh build-phase objects + this.refreshColonies(); + this.refreshDirectives(); + for (let ref in this.overlords) { + this.overlords[ref].refresh(); + } + for (let ref in this.spawnGroups) { + this.spawnGroups[ref].refresh(); + } + this.shouldBuild = false; // refresh successful + } + + private try(callback: () => any, identifier?: string): void { + if (USE_TRY_CATCH) { + try { + callback(); + } catch (e) { + if (identifier) { + e.name = `Caught unhandled exception at ${'' + callback} (identifier: ${identifier}): \n` + + e.name + '\n' + e.stack; + } else { + e.name = `Caught unhandled exception at ${'' + callback}: \n` + e.name + '\n' + e.stack; + } + this.exceptions.push(e); + } + } else { + callback(); + } + } + + private handleExceptions(): void { + if (this.exceptions.length == 0) { + return; + } else { + log.warning(`Exceptions present this tick! Rebuilding Overmind object in next tick.`); + Memory.stats.persistent.lastErrorTick = Game.time; + this.shouldBuild = true; + this.expiration = Game.time; + if (this.exceptions.length == 1) { + throw _.first(this.exceptions); + } else { + for (let e of this.exceptions) { + log.throw(e); + } + let error = new Error('Multiple exceptions caught this tick!'); + error.stack = _.map(this.exceptions, e => e.name).join('\n'); + throw error; + } + } + } + + private makeTerminalNetwork(): TerminalNetwork { + // Initialize the terminal netowrk + let terminals: StructureTerminal[] = []; + for (let name in Game.rooms) { + if (USE_PROFILER && !profilerRooms[name]) continue; + let room = Game.rooms[name]; + if (room.my && room.controller!.level >= 6 && room.terminal && room.terminal.my) { + terminals.push(room.terminal); + } + } + return new TerminalNetwork(terminals); + } + + + /* Instantiate a new colony for each owned rom */ + private registerColonies(): void { + + let colonyOutposts: { [roomName: string]: string[] } = {}; // key: lead room, values: outposts[] + this.colonyMap = {}; + // Register colonies to their outposts + let flagsByRoom = _.groupBy(this.cache.outpostFlags, flag => flag.memory[_MEM.COLONY]); + for (let name in Game.rooms) { + if (Game.rooms[name].my) { + let colonyMemory = Memory.colonies[name] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + this.overseer.notifier.alert("Colony suspended", name, NotifierPriority.High); + continue; + } + if (Game.rooms[name].flags) + colonyOutposts[name] = _.map(flagsByRoom[name], + flag => flag.memory.setPosition + ? derefRoomPosition(flag.memory.setPosition).roomName + : flag.pos.roomName); + this.colonyMap[name] = name; + } + } + // Register outposts to their colonies + for (let colonyName in colonyOutposts) { + for (let outpostName of colonyOutposts[colonyName]) { + this.colonyMap[outpostName] = colonyName; + } + } + + // Initialize the Colonies and pass each one its assigned creeps + let id = 0; + for (let name in colonyOutposts) { + if (USE_PROFILER && !profilerRooms[name]) { + if (Game.time % 20 == 0) { + log.alert(`Suppressing instantiation of colony ${name}.`); + } + continue; // don't make colony for this room + } + try { + this.colonies[name] = new Colony(id, name, colonyOutposts[name]); + } catch (e) { + e.name = `Caught unhandled exception instantiating colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + id++; + } + } + + private refreshColonies(): void { + for (let name in this.colonies) { + try { + this.colonies[name].refresh(); + } catch (e) { + e.name = `Caught unhandled exception refreshing colony ${name}: \n` + e.name; + this.exceptions.push(e); + } + } + } + + /* Wrap each flag in a color coded wrapper */ + private registerDirectives(spawnOverlords = false): void { + // Create a directive for each flag (registration takes place on construction) + for (let name in Game.flags) { + if (this.directives[name]) { // skip existing directives + continue; + } + let colonyName = Game.flags[name].memory[_MEM.COLONY]; + if (colonyName) { + if (USE_PROFILER && !profilerRooms[colonyName]) { + continue; // don't make directive if colony is ignored for profiling + } + let colonyMemory = Memory.colonies[colonyName] as ColonyMemory | undefined; + if (colonyMemory && colonyMemory.suspend) { + continue; // don't make directive if colony is suspended + } + } + const directive = DirectiveWrapper(Game.flags[name]); + if (directive && spawnOverlords) { + directive.spawnMoarOverlords(); + } + if (!directive && Game.time % 10 == 0) { + // log.alert(`Flag [${name} @ ${Game.flags[name].pos.print}] does not match ` + + // `a valid directive color code! (Refer to /src/directives/initializer.ts)` + alignedNewline + + // `Use removeErrantFlags() to remove flags which do not match a directive.`); + } + } + } + + /* Refresh all directives, adding new ones for new flags */ + private refreshDirectives(): void { + for (let name in this.directives) { // this should be called first + this.directives[name].refresh(); + } + this.registerDirectives(true); // register any new directives that were placed since last rebuild + } + + /* Intialize everything in pre-init phase of main loop. Does not call colony.init(). */ + init(): void { + // Initialize the overseer + this.overseer.init(); + // Initialize each colony + for (let colonyName in this.colonies) { + let start = Game.cpu.getUsed(); + this.try(() => this.colonies[colonyName].init(), colonyName); + Stats.log(`cpu.usage.${colonyName}.init`, Game.cpu.getUsed() - start); + } + // Initialize spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].init(), ref); + } + // Initialize terminalNetwork and tradeNetwork + this.try(() => this.terminalNetwork.init()); + this.try(() => this.tradeNetwork.init()); + // Initialize strategist + if (this.strategist) { + this.try(() => this.strategist!.init()); + } + } + + run(): void { + // Enforce behavioral locks every 3 ticks + if (Game.time % 3 == 0) { + IntelManagement.run(); + } + // Run spawn groups + for (let ref in this.spawnGroups) { + this.try(() => this.spawnGroups[ref].run(), ref); + } + // Run the overseer + this.overseer.run(); + // Run all colonies + for (let colonyName in this.colonies) { + this.try(() => this.colonies[colonyName].run(), colonyName); + } + // Run all contracts + if (MY_USERNAME == MUON) { // This ensures that my contracts don't run by default on other people's accounts + for (let contract of AllContracts) { + this.try(() => contract.run()); + } + } + // Run terminal network + this.try(() => this.terminalNetwork.run()); + // Run trade network + this.try(() => this.tradeNetwork.run()); + // Run strategist + if (this.strategist) { + this.try(() => this.strategist!.run()); + } + // Run room intel + this.try(() => RoomIntel.run()); // this should be at end, right before assimilator + // Run assimilator + this.try(() => Assimilator.run()); // this should always be last + } + + postRun(): void { + // Run version updater + this.try(() => VersionUpdater.run()); + // Run segmenter + this.try(() => Segmenter.run()); + // Handle exceptions + this.handleExceptions(); + } + + visuals(): void { + if (Game.cpu.bucket > 9000) { + // Draw global visuals + Visualizer.visuals(); + // Add notifications for outdated version + if (VersionUpdater.memory.newestVersion) { + const newestVersion = VersionUpdater.memory.newestVersion; + if (VersionUpdater.isVersionOutdated(newestVersion)) { + this.overseer.notifier.alert(`[!] Update available: ${__VERSION__} → ${newestVersion}`, + undefined, -1); + } + } + // Draw overseer visuals + this.overseer.visuals(); + // Draw colony visuals + for (let colonyName in this.colonies) { + this.colonies[colonyName].visuals(); + } + } else { + if (Game.time % 10 == 0) { + log.info(`CPU bucket is too low (${Game.cpu.bucket}) - skip rendering visuals.`); + } + } + } +} + + +// Behavioral locks ==================================================================================================== + +/* class BehavioralLocks */ +class IntelManagement { // Renamed to IntelManagement to avoid identification + + /* static enforceSignature(): void */ + static runRoomIntel_1(): void { + let badSignatures: string[] = []; + let colonies = getAllColonies(); + if (colonies.length == 0) return; + for (let colony of colonies) { + if (colony.defcon > 0 || colony.creeps.length == 0) { + continue; + } + let controller = colony.controller; + if (controller.signedByScreeps || controller.level < 4) { + continue; + } + let validSignature = false; + if (controller.sign) { + let sig = controller.sign.text; + if (sig.toLowerCase().includes('overmind') + || sig.includes('\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05')) { + validSignature = true; + } + } + if (!validSignature) { + badSignatures.push(controller.sign ? controller.sign.text : 'undefined'); + } + } + // Throw an error if more than half of rooms have a bad signature + if (badSignatures.length >= 0.5 * _.keys(Overmind.colonies).length) { + Memory.settings.signature = DEFAULT_OVERMIND_SIGNATURE; + log.warning(`Invalid controller signatures detected:` + + bulleted(badSignatures) + alignedNewline + + `Signatures must contain the string "Overmind" or ` + + `${'\u1D0F\u1D20\u1D07\u0280\u1D0D\u026A\u0274\u1D05'}.`); + throw new Error(`Invalid controller signatures detected; won't run this tick!`); + } + } + + /* static enforceIllegalDirectives */ + static runRoomIntel_2(): void { + if (!Assimilator.isAssimilated(MY_USERNAME)) { // if you are not assimilated + const illegalColorCombinations: ColorConstant[][] = [[COLOR_RED, COLOR_RED]]; + for (let name in Game.flags) { + const flag = Game.flags[name]; + const colors = [flag.color, flag.secondaryColor]; + if (illegalColorCombinations.includes(colors)) { + flag.remove(); + log.warning(`Illegal directives detected and removed: ${flag.name}. ` + + `Assimilation required to access these features.`); + } + } + } + } + + static run(): void { + this.runRoomIntel_1(); + if (Game.time % (3 * 31) == 0) { // must be multiple of 3 + this.runRoomIntel_2(); + } + } + +} + + +// Version updater ===================================================================================================== + +interface VersionSegmentData { + version: string; +} + +interface VersionUpdaterMemory { + versions: { [version: string]: any }; + newestVersion: string | undefined; +} + +class VersionUpdater { + + static CheckFrequency = 100; + static CheckOnTick = 91; + static VersionSegment = 99; + + static get memory(): VersionUpdaterMemory { + return Mem.wrap(Memory.Overmind, 'versionUpdater', { + versions : {}, + newestVersion: undefined, + }); + } + + private static slave_fetchVersion(): string | undefined { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.requestForeignSegment(MUON, this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick) { + let data = Segmenter.getForeignSegment() as VersionSegmentData | undefined; + if (data) { + return data.version; + } + } + } + + static isVersionOutdated(newVersion: string): boolean { + let [major, minor, patch] = _.map(__VERSION__.split('.'), + str => parseInt(str, 10)) as number[]; + let [newMajor, newMinor, newPatch] = _.map(newVersion.split('.'), + str => parseInt(str, 10)) as number[]; + return (newMajor > major || newMinor > minor || newPatch > patch); + } + + private static master_pushVersion(): void { + if (Game.time % this.CheckFrequency == this.CheckOnTick - 2) { + Segmenter.requestSegments(this.VersionSegment); + } else if (Game.time % this.CheckFrequency == this.CheckOnTick - 1) { + Segmenter.markSegmentAsPublic(this.VersionSegment); + Segmenter.setSegmentProperty(this.VersionSegment, 'version', __VERSION__); + } + } + + static generateUpdateMessage(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let msg = '\n'; + for (let line of asciiLogoSmall) { + msg += line + '\n'; + } + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = '╔═════════════════════════════════════════════════════════╗\n' + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + '╚═════════════════════════════════════════════════════════╝'; + return msg + updateMsg; + } + + static generateUpdateMessageSmall(v1: string, v2: string): string { + // Variable names are short to match length in text when ${} is added + let downL = 'Download'; + let patchNts = 'Patch notes'; + let updateMsg = `╔═════════════════════════════════╗\n` + + `║ OVERMIND SCREEPS AI ║\n` + + `╠═════════════════════════════════╣\n` + + `║ Update available: ${v1} → ${v2} ║\n` + + `║ > ${downL} < > ${patchNts} < ║\n` + + `╚═════════════════════════════════╝`; + return '\n' + updateMsg; + } + + static displayUpdateMessage(newVersion: string): void { + const updateMessage = this.generateUpdateMessage(__VERSION__, newVersion); + console.log(`${updateMessage}`); + } + + static sayUpdateMessage(newVersion: string): void { + for (let name in Game.creeps) { + let creep = Game.creeps[name]; + creep.say('Update me!', true); + } + } + + static notifyNewVersion(newVersion: string): void { + const updateMessage = this.generateUpdateMessageSmall(__VERSION__, newVersion); + Game.notify(`${updateMessage}`); + } + + static run(): void { + if (MY_USERNAME == MUON) { + this.master_pushVersion(); + } + // Update version + let fetchedVersion = this.slave_fetchVersion(); + if (fetchedVersion) { + this.memory.newestVersion = fetchedVersion; + } + // Check for new version + const newestVersion = this.memory.newestVersion; + if (newestVersion && this.isVersionOutdated(newestVersion)) { + if (Game.time % 10 == 0) { + this.displayUpdateMessage(newestVersion); + this.sayUpdateMessage(newestVersion); + } + if (Game.time % 10000 == 0 /*!this.memory.versions[newVersion]*/) { + this.notifyNewVersion(newestVersion); + } + } + } + +} + + diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 99337f91d..f49de8b52 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -22,6 +22,8 @@ export const Roles = { ranged : 'hydralisk', healer : 'transfuser', dismantler: 'lurker', + drill : 'drill', + coolant : 'coolant', }; /** @@ -307,4 +309,19 @@ export const CombatSetups = { }, + drill: { + default: new CreepSetup(Roles.drill, { + pattern : [MOVE, ATTACK, ATTACK, MOVE], + sizeLimit: Infinity, + }), + }, + + coolant: { + default: new CreepSetup(Roles.coolant, { + pattern : [HEAL, MOVE], + sizeLimit: Infinity, + }), + } + + }; diff --git a/src/directives/initializer.ts b/src/directives/initializer.ts index 7b7637ce1..0a0a1e052 100644 --- a/src/directives/initializer.ts +++ b/src/directives/initializer.ts @@ -25,6 +25,7 @@ import {DirectiveExtract} from './resource/extract'; import {DirectiveSwarmDestroy} from './offense/swarmDestroy'; import {DirectiveOutpostDefense} from './defense/outpostDefense'; import {DirectiveClearRoom} from './colony/clearRoom'; +import {DirectivePowerMine} from "./resource/powerMine"; /** * This is the initializer for directives, which maps flags by their color code to the corresponding directive @@ -92,6 +93,8 @@ export function DirectiveWrapper(flag: Flag): Directive | undefined { return new DirectiveExtract(flag); case COLOR_BLUE: return new DirectiveHaul(flag); + case COLOR_RED: + return new DirectivePowerMine(flag); } break; diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 8e1a27636..6dff8f288 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -2,6 +2,7 @@ import {Directive} from '../Directive'; import {profile} from '../../profiler/decorator'; import {isStoreStructure} from '../../declarations/typeGuards'; import {HaulingOverlord} from '../../overlords/situational/hauler'; +import {PowerHaulingOverlord} from "../../overlords/powerMining/PowerHauler"; interface DirectiveHaulMemory extends FlagMemory { @@ -10,7 +11,7 @@ interface DirectiveHaulMemory extends FlagMemory { /** - * Hauling directive: spawns hauler creeps to move large amounts of resourecs from a location (e.g. draining a storage) + * Hauling directive: spawns hauler creeps to move large amounts of resources from a location (e.g. draining a storage) */ @profile export class DirectiveHaul extends Directive { @@ -29,7 +30,11 @@ export class DirectiveHaul extends Directive { } spawnMoarOverlords() { - this.overlords.haul = new HaulingOverlord(this); + if (this.room && this.pos.lookForStructure(STRUCTURE_POWER_BANK)) { + this.overlords.haul = new PowerHaulingOverlord(this); + } else { + this.overlords.haul = new HaulingOverlord(this); + } } get targetedBy(): string[] { diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts new file mode 100644 index 000000000..e6d3f9672 --- /dev/null +++ b/src/directives/resource/powerMine.ts @@ -0,0 +1,107 @@ +import {Directive} from '../Directive'; +import {profile} from '../../profiler/decorator'; +import {isStoreStructure} from '../../declarations/typeGuards'; +import {PowerDrillOverlord} from '../../overlords/powerMining/PowerDrill'; + + +interface DirectivePowerMineMemory extends FlagMemory { + totalResources?: number; +} + + +/** + * PowerMining directive: kills power banks and collects the resources. + */ +@profile +export class DirectivePowerMine extends Directive { + + static directiveName = 'powerMine'; + static color = COLOR_YELLOW; + static secondaryColor = COLOR_RED; + + private _powerBank: StructurePowerBank | undefined; + + private _store: StoreDefinition; + private _drops: { [resourceType: string]: Resource[] }; + + memory: DirectivePowerMineMemory; + + constructor(flag: Flag) { + super(flag); + } + + spawnMoarOverlords() { + this.overlords.powerMine = new PowerDrillOverlord(this); + } + + get targetedBy(): string[] { + return Overmind.cache.targets[this.ref]; + } + + get drops(): { [resourceType: string]: Resource[] } { + if (!this.pos.isVisible) { + return {}; + } + if (!this._drops) { + let drops = (this.pos.lookFor(LOOK_RESOURCES) || []) as Resource[]; + this._drops = _.groupBy(drops, drop => drop.resourceType); + } + return this._drops; + } + + get hasDrops(): boolean { + return _.keys(this.drops).length > 0; + } + + // get store(): StoreDefinition { + // if (!this._store) { + // // Merge the "storage" of drops with the store of structure + // let store: { [resourceType: string]: number } = {}; + // if (this.storeStructure) { + // if (isStoreStructure(this.storeStructure)) { + // store = this.storeStructure.store; + // } else { + // store = {'energy': this.storeStructure.energy}; + // } + // } else { + // store = {'energy': 0}; + // } + // // Merge with drops + // for (let resourceType of _.keys(this.drops)) { + // let totalResourceAmount = _.sum(this.drops[resourceType], drop => drop.amount); + // if (store[resourceType]) { + // store[resourceType] += totalResourceAmount; + // } else { + // store[resourceType] = totalResourceAmount; + // } + // } + // this._store = store as StoreDefinition; + // } + // return this._store; + // } + + /** + * Total amount of resources remaining to be transported; cached into memory in case room loses visibility + */ + get totalResources(): number { + if (this.memory.totalResources == undefined) { + return 5000; // pick some non-zero number so that powerMiners will spawn + } + if (this.pos.isVisible) { + this.memory.totalResources = this._powerBank ? this._powerBank.power : this.memory.totalResources; // update total amount remaining + } + return this.memory.totalResources; + } + + init(): void { + this.alert(`PowerMine directive active`); + } + + run(): void { + // if (_.sum(this.store) == 0 && this.pos.isVisible) { + // //this.remove(); + // } + } + +} + diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts new file mode 100644 index 000000000..b82f7f74b --- /dev/null +++ b/src/overlords/powerMining/PowerDrill.ts @@ -0,0 +1,130 @@ +import {CombatZerg} from '../../zerg/CombatZerg'; +import {DirectiveSKOutpost} from '../../directives/colony/outpostSK'; +import {RoomIntel} from '../../intel/RoomIntel'; +import {minBy} from '../../utilities/utils'; +import {Mem} from '../../memory/Memory'; +import {debug, log} from '../../console/log'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {Visualizer} from '../../visuals/Visualizer'; +import {profile} from '../../profiler/decorator'; +import {CombatOverlord} from '../CombatOverlord'; +import {CombatSetups, Roles} from '../../creepSetups/setups'; +import {OverlordMemory} from '../Overlord'; +import {DirectivePowerMine} from "../../directives/resource/powerMine"; +import {DirectiveHaul} from "../../directives/resource/haul"; +import {calculateFormationStrength} from "../../utilities/creepUtils"; + +interface PowerDrillOverlordMemory extends OverlordMemory { + targetPBID?: string; +} + +/** + * PowerDrillOverlord -- spawns drills and coolant to mine power banks + */ +@profile +export class PowerDrillOverlord extends CombatOverlord { + + static requiredRCL = 7; + static haulerPrespawn = 600; + + directive: DirectiveSKOutpost; + memory: PowerDrillOverlordMemory; + targetPowerBank: StructurePowerBank | undefined; + + drills: CombatZerg[]; + coolant: CombatZerg[]; + + constructor(directive: DirectivePowerMine, priority = OverlordPriority.powerMine.drill) { + super(directive, 'powerDrill', priority, PowerDrillOverlord.requiredRCL); + this.directive = directive; + this.priority += this.outpostIndex * OverlordPriority.powerMine.roomIncrement; + this.drills = this.combatZerg(Roles.drill); + this.coolant = this.combatZerg(Roles.coolant); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + } + + refresh() { + super.refresh(); + this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + + } + + init() { + this.wishlist(3, CombatSetups.drill.default); + this.wishlist(4, CombatSetups.coolant.default); + } + + private handleDrill(drill: CombatZerg) { + if (!this.targetPowerBank) { + if (!this.room) { + // We are not there yet + } else { + var bank = this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank; + this.targetPowerBank = bank; + // If power bank is dead + if (bank == undefined) { + Game.notify("Power bank in " + this.room + " is dead."); + this.directive.remove(); + Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); + drill.suicide(); + return; + } + } + } + + // Go to keeper room + if (!this.room || drill.room != this.room || drill.pos.isEdge || !this.targetPowerBank) { + // log.debugCreep(drill, `Going to room!`); + Game.notify("Drill is moving to power site in " + this.room + "."); + drill.goTo(this.pos); + return; + } else if (this.targetPowerBank.hits < 800000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + DirectiveHaul.create(this.pos); + } + // Spawn a hauler for this location + // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep + + // Handle killing bank + if (drill.hits > 100) { + drill.goTo(this.targetPowerBank); + drill.attack(this.targetPowerBank); + } + } + + private handleCoolant(coolant: CombatZerg) { + // Go to powerbank room + if (!this.room || coolant.room != this.room || coolant.pos.isEdge) { + // log.debugCreep(reaper, `Going to room!`); + coolant.healSelfIfPossible(); + coolant.goTo(this.pos); + return; + } else if (!this.targetPowerBank) { + // If power bank is dead + Game.notify("Power bank in " + this.room + " is dead."); + coolant.suicide(); + return; + } else if (this.targetPowerBank.hits < 50000) { + Game.notify("Power bank in " + this.room + " is almost dead"); + Game.notify("Power bank in " + this.room + ", beginning haul operation."); + //DirectiveHaul.create(this.pos); + } + if (coolant.pos.getRangeTo(_.first(this.drills)) > 1) { + coolant.goTo(_.first(this.drills)); + } + + coolant.autoHeal(false); + } + + run() { + this.autoRun(this.drills, drill => this.handleDrill(drill)); + this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); + } + + visuals() { + if (this.room && this.targetPowerBank) { + Visualizer.marker(this.targetPowerBank.pos); + } + } + +} diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts new file mode 100644 index 000000000..632ce0654 --- /dev/null +++ b/src/overlords/powerMining/PowerHauler.ts @@ -0,0 +1,151 @@ +import {Overlord} from '../Overlord'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {Zerg} from '../../zerg/Zerg'; +import {DirectiveHaul} from '../../directives/resource/haul'; +import {Tasks} from '../../tasks/Tasks'; +import {isStoreStructure} from '../../declarations/typeGuards'; +import {log} from '../../console/log'; +import {Pathing} from '../../movement/Pathing'; +import {Energetics} from '../../logistics/Energetics'; +import {profile} from '../../profiler/decorator'; +import {Roles, Setups} from '../../creepSetups/setups'; +import {HaulingOverlord} from "../situational/hauler"; +import {calculateFormationStrength} from "../../utilities/creepUtils"; + +/** + * Spawns special-purpose haulers for transporting resources to/from a specified target + */ +@profile +export class PowerHaulingOverlord extends HaulingOverlord { + + haulers: Zerg[]; + directive: DirectiveHaul; + powerBank: StructurePowerBank | undefined; + tickToSpawnOn: number; + numHaulers: number; + + requiredRCL = 6; + // Allow time for body to spawn + prespawnAmount = 200; + + constructor(directive: DirectiveHaul, priority = OverlordPriority.collectionUrgent.haul) { + super(directive, priority); // Removed 'haul' string + this.directive = directive; + this.haulers = this.zerg(Roles.transport); + } + + init() { + if (!this.colony.storage || _.sum(this.colony.storage.store) > Energetics.settings.storage.total.cap) { + return; + } + // Spawn a number of haulers sufficient to move all resources within a lifetime, up to a max + let MAX_HAULERS = 5; + // Calculate total needed amount of hauling power as (resource amount * trip distance) + let tripDistance = 2 * Pathing.distance((this.colony.storage || this.colony).pos, this.directive.pos); + let haulingPowerNeeded = Math.min(this.directive.totalResources, + this.colony.storage.storeCapacity + - _.sum(this.colony.storage.store)) * tripDistance; + // Calculate amount of hauling each hauler provides in a lifetime + let haulerCarryParts = Setups.transporters.early.getBodyPotential(CARRY, this.colony); + let haulingPowerPerLifetime = CREEP_LIFE_TIME * haulerCarryParts * CARRY_CAPACITY; + // Calculate number of haulers + this.numHaulers = Math.min(Math.ceil(haulingPowerNeeded / haulingPowerPerLifetime), MAX_HAULERS); + // Request the haulers + this.tickToSpawnOn = Game.time + (this.calculateRemainingLifespan() || 0) - this.prespawnAmount; + } + + + calculateRemainingLifespan() { + if (!this.room) { + return undefined; + } else if (this.powerBank == undefined) { + // Power Bank is gone + return 0; + } else { + let tally = calculateFormationStrength(this.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); + let healStrength: number = tally.heal * HEAL_POWER || 0; + let attackStrength: number = tally.attack * ATTACK_POWER || 0; + // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing + let avgDamagePerTick = Math.min(attackStrength, healStrength*2); + return this.powerBank.hits / avgDamagePerTick; + } + } + + protected handleHauler(hauler: Zerg) { + if (_.sum(hauler.carry) == 0) { + // Travel to directive and collect resources + if (hauler.inSameRoomAs(this.directive)) { + // Pick up drops first + if (this.directive.hasDrops) { + let allDrops: Resource[] = _.flatten(_.values(this.directive.drops)); + let drop = allDrops[0]; + if (drop) { + hauler.task = Tasks.pickup(drop); + return; + } + } + // Withdraw from store structure + if (this.directive.storeStructure) { + let store: { [resourceType: string]: number } = {}; + if (isStoreStructure(this.directive.storeStructure)) { + store = this.directive.storeStructure.store; + } else { + store = {'energy': this.directive.storeStructure.energy}; + } + for (let resourceType in store) { + if (store[resourceType] > 0) { + hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType); + return; + } + } + } + // Shouldn't reach here + log.warning(`${hauler.name} in ${hauler.room.print}: nothing to collect!`); + } else { + // hauler.task = Tasks.goTo(this.directive); + hauler.goTo(this.directive); + } + } else { + // Travel to colony room and deposit resources + if (hauler.inSameRoomAs(this.colony)) { + // Put energy in storage and minerals in terminal if there is one + for (let resourceType in hauler.carry) { + if (hauler.carry[resourceType] == 0) continue; + if (resourceType == RESOURCE_ENERGY) { // prefer to put energy in storage + if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.storage, resourceType); + return; + } else if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.terminal, resourceType); + return; + } + } else { // prefer to put minerals in terminal + if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.terminal, resourceType); + return; + } else if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + hauler.task = Tasks.transfer(this.colony.storage, resourceType); + return; + } + } + } + // Shouldn't reach here + log.warning(`${hauler.name} in ${hauler.room.print}: nowhere to put resources!`); + } else { + hauler.task = Tasks.goToRoom(this.colony.room.name); + } + } + } + + run() { + if (Game.time >= this.tickToSpawnOn && this.haulers.length == 0) { + this.wishlist(this.numHaulers, Setups.transporters.early); + } + for (let hauler of this.haulers) { + if (hauler.isIdle) { + this.handleHauler(hauler); + } + hauler.run(); + } + } +} \ No newline at end of file diff --git a/src/overlords/situational/hauler.ts b/src/overlords/situational/hauler.ts index 86bfd43a3..ffd527cca 100644 --- a/src/overlords/situational/hauler.ts +++ b/src/overlords/situational/hauler.ts @@ -19,7 +19,7 @@ export class HaulingOverlord extends Overlord { haulers: Zerg[]; directive: DirectiveHaul; - requiredRCL: 4; + requiredRCL: number = 4; constructor(directive: DirectiveHaul, priority = directive.hasDrops ? OverlordPriority.collectionUrgent.haul : OverlordPriority.collection.haul) { @@ -48,7 +48,7 @@ export class HaulingOverlord extends Overlord { this.wishlist(numHaulers, Setups.transporters.early); } - private handleHauler(hauler: Zerg) { + protected handleHauler(hauler: Zerg) { if (_.sum(hauler.carry) == 0) { // Travel to directive and collect resources if (hauler.inSameRoomAs(this.directive)) { diff --git a/src/priorities/priorities_overlords.ts b/src/priorities/priorities_overlords.ts index 356b7e676..71d38c399 100644 --- a/src/priorities/priorities_overlords.ts +++ b/src/priorities/priorities_overlords.ts @@ -72,6 +72,13 @@ export var OverlordPriority = { roomIncrement: 5, }, + powerMine: { + cool : 1050, + drill : 1051, + gather : 604, + roomIncrement: 5, + }, + collection: { // Non-urgent collection of resources, like from a deserted storage haul: 1100 }, diff --git a/src/utilities/creepUtils.ts b/src/utilities/creepUtils.ts new file mode 100644 index 000000000..7e5b1a6b7 --- /dev/null +++ b/src/utilities/creepUtils.ts @@ -0,0 +1,45 @@ +// Creep utilities that don't belong anywhere else + + +// Does not account for range, just total of body parts +export function calculateFormationStrength(creeps : Creep[]): Record { + let tally: Record = { + move : 0, + work : 0, + carry : 0, + attack : 0, + ranged_attack : 0, + tough : 0, + heal : 0, + claim : 0, + }; + + _.forEach(creeps, + function (unit) { + let individualTally = calculateBodyPotential(unit.body); + for (let bodyType in individualTally) { + let type = bodyType as BodyPartConstant; + tally[type] += individualTally[type]; + } + }); + return tally; +} + +export function calculateBodyPotential(body : BodyPartDefinition[]): Record { + let tally: Record = { + move : 0, + work : 0, + carry : 0, + attack : 0, + ranged_attack : 0, + tough : 0, + heal : 0, + claim : 0, + }; + _.forEach(body, function (bodyPart) { + // Needs boost logic + tally[bodyPart.type] += 1; + } + ); + return tally; +} From 58390550d75fc3949813a53a4470f7f70d7c9cd7 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 22 Apr 2019 16:29:05 -0700 Subject: [PATCH 021/137] Fixing healing (somewhat) --- src/overlords/powerMining/PowerDrill.ts | 33 ++++++++++++++++++++++--- src/zerg/CombatZerg.ts | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index b82f7f74b..54f8224bc 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -54,6 +54,15 @@ export class PowerDrillOverlord extends CombatOverlord { this.wishlist(4, CombatSetups.coolant.default); } + private getHostileDrill(powerBank: StructurePowerBank) { + return powerBank.hits < powerBank.hitsMax && powerBank.pos.findInRange(FIND_HOSTILE_CREEPS, 2)[0]; + } + + private handleHostileDrill(hostileDrill: Creep, powerBank: StructurePowerBank) { + Game.notify(`${hostileDrill.owner.username} power harvesting ${powerBank.room.name}, competing for same power bank.`); + // this.directive.remove(); + } + private handleDrill(drill: CombatZerg) { if (!this.targetPowerBank) { if (!this.room) { @@ -86,9 +95,10 @@ export class PowerDrillOverlord extends CombatOverlord { // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep // Handle killing bank - if (drill.hits > 100) { - drill.goTo(this.targetPowerBank); + if (drill.pos.isNearTo(this.targetPowerBank)) { drill.attack(this.targetPowerBank); + } else { + drill.goTo(this.targetPowerBank); } } @@ -109,13 +119,28 @@ export class PowerDrillOverlord extends CombatOverlord { Game.notify("Power bank in " + this.room + ", beginning haul operation."); //DirectiveHaul.create(this.pos); } - if (coolant.pos.getRangeTo(_.first(this.drills)) > 1) { - coolant.goTo(_.first(this.drills)); + + if (coolant.memory.partner) { + let drill = Game.creeps[coolant.memory.partner]; + if (!drill) { + coolant.memory.partner = undefined; + } else if (!coolant.pos.isNearTo(drill)) { + coolant.goTo(drill); + } else { + coolant.heal(drill); + } + } + if (coolant.pos.getRangeTo(this.targetPowerBank) > 2) { + coolant.goTo(this.targetPowerBank); } coolant.autoHeal(false); } + private findDrillToPartner(coolant: CombatZerg) { + + } + run() { this.autoRun(this.drills, drill => this.handleDrill(drill)); this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); diff --git a/src/zerg/CombatZerg.ts b/src/zerg/CombatZerg.ts index 5b8da2123..dc10d2a48 100644 --- a/src/zerg/CombatZerg.ts +++ b/src/zerg/CombatZerg.ts @@ -179,7 +179,7 @@ export class CombatZerg extends Zerg { */ autoHeal(allowRangedHeal = true, friendlies = this.room.creeps) { let target = CombatTargeting.findBestHealingTargetInRange(this, allowRangedHeal ? 3 : 1, friendlies); - this.debug(`Heal taget: ${target}`); + this.debug(`Heal target: ${target}`); if (target) { if (this.pos.getRangeTo(target) <= 1) { return this.heal(target); From 2bb13111e155fd77726681a74adfa6b5e1a680d6 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 23 Apr 2019 15:51:18 -0700 Subject: [PATCH 022/137] Really clunky patch to make power harvest part of core directive --- src/directives/resource/haul.ts | 13 ++++- src/directives/resource/powerMine.ts | 64 ++++++++++++------------ src/overlords/Overlord.ts | 8 +++ src/overlords/powerMining/PowerDrill.ts | 14 +++--- src/overlords/powerMining/PowerHauler.ts | 36 ++++++------- 5 files changed, 75 insertions(+), 60 deletions(-) diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 6dff8f288..96161a301 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -22,6 +22,7 @@ export class DirectiveHaul extends Directive { private _store: StoreDefinition; private _drops: { [resourceType: string]: Resource[] }; + private finishAtTime: number; memory: DirectiveHaulMemory; @@ -56,11 +57,12 @@ export class DirectiveHaul extends Directive { return _.keys(this.drops).length > 0; } - get storeStructure(): StructureStorage | StructureTerminal | StructureNuker | undefined { + get storeStructure(): StructureStorage | StructureTerminal | StructureNuker | StructurePowerBank | undefined { if (this.pos.isVisible) { return this.pos.lookForStructure(STRUCTURE_STORAGE) || this.pos.lookForStructure(STRUCTURE_TERMINAL) || - this.pos.lookForStructure(STRUCTURE_NUKER); + this.pos.lookForStructure(STRUCTURE_NUKER) || + this.pos.lookForStructure(STRUCTURE_POWER_BANK); } return undefined; } @@ -72,6 +74,8 @@ export class DirectiveHaul extends Directive { if (this.storeStructure) { if (isStoreStructure(this.storeStructure)) { store = this.storeStructure.store; + } else if (this.storeStructure instanceof StructurePowerBank) { + store = {'power': this.storeStructure.power}; } else { store = {'energy': this.storeStructure.energy}; } @@ -112,6 +116,11 @@ export class DirectiveHaul extends Directive { run(): void { if (_.sum(this.store) == 0 && this.pos.isVisible) { + //this.remove(); + this.finishAtTime = Game.time + 1000; + this.overlords + } + if (Game.time >= this.finishAtTime) { this.remove(); } } diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index e6d3f9672..c290d9ff9 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -2,6 +2,9 @@ import {Directive} from '../Directive'; import {profile} from '../../profiler/decorator'; import {isStoreStructure} from '../../declarations/typeGuards'; import {PowerDrillOverlord} from '../../overlords/powerMining/PowerDrill'; +import {Pathing} from "../../movement/Pathing"; +import {calculateFormationStrength} from "../../utilities/creepUtils"; +import {PowerHaulingOverlord} from "../../overlords/powerMining/PowerHauler"; interface DirectivePowerMineMemory extends FlagMemory { @@ -19,9 +22,8 @@ export class DirectivePowerMine extends Directive { static color = COLOR_YELLOW; static secondaryColor = COLOR_RED; + expectedSpawnTime = 200; private _powerBank: StructurePowerBank | undefined; - - private _store: StoreDefinition; private _drops: { [resourceType: string]: Resource[] }; memory: DirectivePowerMineMemory; @@ -53,33 +55,6 @@ export class DirectivePowerMine extends Directive { return _.keys(this.drops).length > 0; } - // get store(): StoreDefinition { - // if (!this._store) { - // // Merge the "storage" of drops with the store of structure - // let store: { [resourceType: string]: number } = {}; - // if (this.storeStructure) { - // if (isStoreStructure(this.storeStructure)) { - // store = this.storeStructure.store; - // } else { - // store = {'energy': this.storeStructure.energy}; - // } - // } else { - // store = {'energy': 0}; - // } - // // Merge with drops - // for (let resourceType of _.keys(this.drops)) { - // let totalResourceAmount = _.sum(this.drops[resourceType], drop => drop.amount); - // if (store[resourceType]) { - // store[resourceType] += totalResourceAmount; - // } else { - // store[resourceType] = totalResourceAmount; - // } - // } - // this._store = store as StoreDefinition; - // } - // return this._store; - // } - /** * Total amount of resources remaining to be transported; cached into memory in case room loses visibility */ @@ -93,14 +68,39 @@ export class DirectivePowerMine extends Directive { return this.memory.totalResources; } + calculateRemainingLifespan() { + if (!this.room) { + return undefined; + } else if (this._powerBank == undefined) { + // Power Bank is gone + return 0; + } else { + let tally = calculateFormationStrength(this._powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); + let healStrength: number = tally.heal * HEAL_POWER || 0; + let attackStrength: number = tally.attack * ATTACK_POWER || 0; + // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing + let avgDamagePerTick = Math.min(attackStrength, healStrength*2); + return this._powerBank.hits / avgDamagePerTick; + } + } + + spawnHaulers() { + + if (this.room && (!this._powerBank || (this.calculateRemainingLifespan()! < Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime))) { + this.overlords.powerHaul = new PowerHaulingOverlord(this); + } + } + + + init(): void { this.alert(`PowerMine directive active`); } + + run(): void { - // if (_.sum(this.store) == 0 && this.pos.isVisible) { - // //this.remove(); - // } + } } diff --git a/src/overlords/Overlord.ts b/src/overlords/Overlord.ts index a50b7998b..f5a455ef2 100644 --- a/src/overlords/Overlord.ts +++ b/src/overlords/Overlord.ts @@ -44,6 +44,13 @@ export interface OverlordMemory { suspendUntil?: number; } +enum overlordLifecyleState { + preparing, + active, + paused, + finishing +} + const OverlordMemoryDefaults: OverlordMemory = {}; /** @@ -69,6 +76,7 @@ export abstract class Overlord { private _combatZerg: { [roleName: string]: CombatZerg[] }; private boosts: { [roleName: string]: _ResourceConstantSansEnergy[] | undefined }; creepUsageReport: { [roleName: string]: [number, number] | undefined }; + lifeCycle: overlordLifecyleState; constructor(initializer: OverlordInitializer | Colony, name: string, priority: number) { this.initializer = initializer; diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 54f8224bc..a56d92524 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -25,11 +25,11 @@ interface PowerDrillOverlordMemory extends OverlordMemory { export class PowerDrillOverlord extends CombatOverlord { static requiredRCL = 7; - static haulerPrespawn = 600; - directive: DirectiveSKOutpost; + directive: DirectivePowerMine; memory: PowerDrillOverlordMemory; targetPowerBank: StructurePowerBank | undefined; + haulDirectiveCreated: boolean; drills: CombatZerg[]; coolant: CombatZerg[]; @@ -41,6 +41,7 @@ export class PowerDrillOverlord extends CombatOverlord { this.drills = this.combatZerg(Roles.drill); this.coolant = this.combatZerg(Roles.coolant); this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); + this.haulDirectiveCreated = false; } refresh() { @@ -87,9 +88,10 @@ export class PowerDrillOverlord extends CombatOverlord { Game.notify("Drill is moving to power site in " + this.room + "."); drill.goTo(this.pos); return; - } else if (this.targetPowerBank.hits < 800000) { + } else if (this.targetPowerBank.hits < 800000 && !this.haulDirectiveCreated) { Game.notify("Power bank in " + this.room + " is almost dead"); - DirectiveHaul.create(this.pos); + Game.notify("Power bank in " + this.room + ", beginning haul operation."); + //this.haulDirectiveCreated = typeof DirectiveHaul.create(this.pos) == "string"; } // Spawn a hauler for this location // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep @@ -114,10 +116,6 @@ export class PowerDrillOverlord extends CombatOverlord { Game.notify("Power bank in " + this.room + " is dead."); coolant.suicide(); return; - } else if (this.targetPowerBank.hits < 50000) { - Game.notify("Power bank in " + this.room + " is almost dead"); - Game.notify("Power bank in " + this.room + ", beginning haul operation."); - //DirectiveHaul.create(this.pos); } if (coolant.memory.partner) { diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 632ce0654..d2dd6d246 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -11,15 +11,16 @@ import {profile} from '../../profiler/decorator'; import {Roles, Setups} from '../../creepSetups/setups'; import {HaulingOverlord} from "../situational/hauler"; import {calculateFormationStrength} from "../../utilities/creepUtils"; +import {DirectivePowerMine} from "../../directives/resource/powerMine"; /** * Spawns special-purpose haulers for transporting resources to/from a specified target */ @profile -export class PowerHaulingOverlord extends HaulingOverlord { +export class PowerHaulingOverlord extends Overlord { haulers: Zerg[]; - directive: DirectiveHaul; + directive: DirectivePowerMine; powerBank: StructurePowerBank | undefined; tickToSpawnOn: number; numHaulers: number; @@ -28,8 +29,8 @@ export class PowerHaulingOverlord extends HaulingOverlord { // Allow time for body to spawn prespawnAmount = 200; - constructor(directive: DirectiveHaul, priority = OverlordPriority.collectionUrgent.haul) { - super(directive, priority); // Removed 'haul' string + constructor(directive: DirectivePowerMine, priority = OverlordPriority.collectionUrgent.haul) { + super(directive, 'powerHaul', priority); this.directive = directive; this.haulers = this.zerg(Roles.transport); } @@ -85,20 +86,19 @@ export class PowerHaulingOverlord extends HaulingOverlord { } } // Withdraw from store structure - if (this.directive.storeStructure) { - let store: { [resourceType: string]: number } = {}; - if (isStoreStructure(this.directive.storeStructure)) { - store = this.directive.storeStructure.store; - } else { - store = {'energy': this.directive.storeStructure.energy}; - } - for (let resourceType in store) { - if (store[resourceType] > 0) { - hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType); - return; - } - } - } + // if (this.directive.storeStructure) { + // let store: { [resourceType: string]: number } = {}; + // if (this.directive.storeStructure instanceof StructurePowerBank) { + // store = {'power': this.directive.storeStructure.power}; + // } + // for (let resourceType in store) { + // if (store[resourceType] > 0) { + // //hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType); + // // Wait for it to die + // return; + // } + // } + // } // Shouldn't reach here log.warning(`${hauler.name} in ${hauler.room.print}: nothing to collect!`); } else { From a037189d1e6880b74ebd24533bc9af4f3e0d62ab Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 24 Apr 2019 16:15:17 -0700 Subject: [PATCH 023/137] fixing some power harvesting (honestly can't remember) --- src/directives/resource/haul.ts | 15 ++----- src/directives/resource/powerMine.ts | 10 +++-- src/overlords/powerMining/PowerDrill.ts | 53 +++++++++++++++++++++++-- src/overlords/situational/hauler.ts | 2 +- 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 96161a301..91db6f2db 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -31,11 +31,7 @@ export class DirectiveHaul extends Directive { } spawnMoarOverlords() { - if (this.room && this.pos.lookForStructure(STRUCTURE_POWER_BANK)) { - this.overlords.haul = new PowerHaulingOverlord(this); - } else { - this.overlords.haul = new HaulingOverlord(this); - } + this.overlords.haul = new HaulingOverlord(this); } get targetedBy(): string[] { @@ -57,12 +53,11 @@ export class DirectiveHaul extends Directive { return _.keys(this.drops).length > 0; } - get storeStructure(): StructureStorage | StructureTerminal | StructureNuker | StructurePowerBank | undefined { + get storeStructure(): StructureStorage | StructureTerminal | StructureNuker | undefined { if (this.pos.isVisible) { return this.pos.lookForStructure(STRUCTURE_STORAGE) || this.pos.lookForStructure(STRUCTURE_TERMINAL) || - this.pos.lookForStructure(STRUCTURE_NUKER) || - this.pos.lookForStructure(STRUCTURE_POWER_BANK); + this.pos.lookForStructure(STRUCTURE_NUKER); } return undefined; } @@ -74,8 +69,6 @@ export class DirectiveHaul extends Directive { if (this.storeStructure) { if (isStoreStructure(this.storeStructure)) { store = this.storeStructure.store; - } else if (this.storeStructure instanceof StructurePowerBank) { - store = {'power': this.storeStructure.power}; } else { store = {'energy': this.storeStructure.energy}; } @@ -118,7 +111,7 @@ export class DirectiveHaul extends Directive { if (_.sum(this.store) == 0 && this.pos.isVisible) { //this.remove(); this.finishAtTime = Game.time + 1000; - this.overlords + //this.overlords } if (Game.time >= this.finishAtTime) { this.remove(); diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index c290d9ff9..60b6ed715 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -23,6 +23,7 @@ export class DirectivePowerMine extends Directive { static secondaryColor = COLOR_RED; expectedSpawnTime = 200; + haulDirectiveCreated: boolean; private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; @@ -85,8 +86,9 @@ export class DirectivePowerMine extends Directive { } spawnHaulers() { - if (this.room && (!this._powerBank || (this.calculateRemainingLifespan()! < Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime))) { + Game.notify('Spawning haulers for power mining in room ' + this.room.name); + this.haulDirectiveCreated = true; this.overlords.powerHaul = new PowerHaulingOverlord(this); } } @@ -100,8 +102,10 @@ export class DirectivePowerMine extends Directive { run(): void { - + if (Game.time % 100 == 0 && !this.haulDirectiveCreated) { + Game.notify('Checking if should spawn haulers'); + this.spawnHaulers(); + } } - } diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index a56d92524..055215290 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -13,6 +13,7 @@ import {OverlordMemory} from '../Overlord'; import {DirectivePowerMine} from "../../directives/resource/powerMine"; import {DirectiveHaul} from "../../directives/resource/haul"; import {calculateFormationStrength} from "../../utilities/creepUtils"; +import {Zerg} from "../../zerg/Zerg"; interface PowerDrillOverlordMemory extends OverlordMemory { targetPBID?: string; @@ -31,6 +32,8 @@ export class PowerDrillOverlord extends CombatOverlord { targetPowerBank: StructurePowerBank | undefined; haulDirectiveCreated: boolean; + partnerMap: Map; + drills: CombatZerg[]; coolant: CombatZerg[]; @@ -42,6 +45,7 @@ export class PowerDrillOverlord extends CombatOverlord { this.coolant = this.combatZerg(Roles.coolant); this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); this.haulDirectiveCreated = false; + this.partnerMap = new Map(); } refresh() { @@ -51,7 +55,7 @@ export class PowerDrillOverlord extends CombatOverlord { } init() { - this.wishlist(3, CombatSetups.drill.default); + this.wishlist(2, CombatSetups.drill.default); this.wishlist(4, CombatSetups.coolant.default); } @@ -73,9 +77,16 @@ export class PowerDrillOverlord extends CombatOverlord { this.targetPowerBank = bank; // If power bank is dead if (bank == undefined) { - Game.notify("Power bank in " + this.room + " is dead."); + if (this.pos.lookFor(LOOK_RESOURCES).length == 0) { + // Well shit, we didn't finish mining + Game.notify("WE FUCKING FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); this.directive.remove(); + return; + } + Game.notify("Power bank in " + this.room + " is dead."); + //this.directive.remove(); Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); + drill.say('💀 RIP 💀'); drill.suicide(); return; } @@ -98,8 +109,10 @@ export class PowerDrillOverlord extends CombatOverlord { // Handle killing bank if (drill.pos.isNearTo(this.targetPowerBank)) { + PowerDrillOverlord.periodicSay(drill,'Drilling⚒️'); drill.attack(this.targetPowerBank); } else { + PowerDrillOverlord.periodicSay(drill,'🚗Traveling🚗'); drill.goTo(this.targetPowerBank); } } @@ -107,13 +120,14 @@ export class PowerDrillOverlord extends CombatOverlord { private handleCoolant(coolant: CombatZerg) { // Go to powerbank room if (!this.room || coolant.room != this.room || coolant.pos.isEdge) { - // log.debugCreep(reaper, `Going to room!`); + // log.debugCreep(coolant, `Going to room!`); coolant.healSelfIfPossible(); coolant.goTo(this.pos); return; } else if (!this.targetPowerBank) { // If power bank is dead Game.notify("Power bank in " + this.room + " is dead."); + coolant.say('💀 RIP 💀'); coolant.suicide(); return; } @@ -121,22 +135,53 @@ export class PowerDrillOverlord extends CombatOverlord { if (coolant.memory.partner) { let drill = Game.creeps[coolant.memory.partner]; if (!drill) { + // Partner is dead coolant.memory.partner = undefined; + this.findDrillToPartner(coolant) } else if (!coolant.pos.isNearTo(drill)) { + PowerDrillOverlord.periodicSay(coolant,'🚗Traveling️'); coolant.goTo(drill); } else { + PowerDrillOverlord.periodicSay(coolant,'❄️Cooling❄️'); coolant.heal(drill); } + if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(coolant)) { + this.findDrillToPartner(coolant); + } + return; + } else { + this.findDrillToPartner(coolant); } if (coolant.pos.getRangeTo(this.targetPowerBank) > 2) { coolant.goTo(this.targetPowerBank); + } else if (coolant.pos.getRangeTo(this.targetPowerBank) == 1) { + coolant.flee([this.targetPowerBank.pos]); + } else { + coolant.goTo(_.sample(_.filter(this.drills, drill => drill.hits < drill.hitsMax))); } - coolant.autoHeal(false); + coolant.autoHeal(); } private findDrillToPartner(coolant: CombatZerg) { + let needsHealing = _.min(Array.from(this.partnerMap.keys()), key => this.partnerMap.get(key)!.length); + if (this.partnerMap.get(needsHealing)) { + this.partnerMap.get(needsHealing)!.concat(coolant.name); + coolant.memory.partner = needsHealing; + } + // let newPartner = _.sample(_.filter(this.drills, drill => this.room == drill.room)); + // coolant.memory.partner = newPartner != undefined ? newPartner.name : undefined; + coolant.say('Partnering!'); + } + + static periodicSay(zerg: CombatZerg, text: string) { + if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(zerg)) { + zerg.say(text, true); + } + } + static getCreepNameOffset(creep: Zerg) { + return parseInt(creep.name.charAt(creep.name.length-1)) || 0; } run() { diff --git a/src/overlords/situational/hauler.ts b/src/overlords/situational/hauler.ts index ffd527cca..9655717ab 100644 --- a/src/overlords/situational/hauler.ts +++ b/src/overlords/situational/hauler.ts @@ -48,7 +48,7 @@ export class HaulingOverlord extends Overlord { this.wishlist(numHaulers, Setups.transporters.early); } - protected handleHauler(hauler: Zerg) { + private handleHauler(hauler: Zerg) { if (_.sum(hauler.carry) == 0) { // Travel to directive and collect resources if (hauler.inSameRoomAs(this.directive)) { From 35476b207802d2c1414cd6e65ef22ba4757fb392 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 25 Apr 2019 00:38:34 -0700 Subject: [PATCH 024/137] Adding hauling --- src/directives/resource/powerMine.ts | 19 +++++++++++++------ src/overlords/powerMining/PowerDrill.ts | 5 +++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 60b6ed715..0a87b5230 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -23,6 +23,7 @@ export class DirectivePowerMine extends Directive { static secondaryColor = COLOR_RED; expectedSpawnTime = 200; + miningDone: boolean; haulDirectiveCreated: boolean; private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; @@ -34,7 +35,10 @@ export class DirectivePowerMine extends Directive { } spawnMoarOverlords() { - this.overlords.powerMine = new PowerDrillOverlord(this); + if (!this.miningDone) { + this.overlords.powerMine = new PowerDrillOverlord(this); + } + this.spawnHaulers(); } get targetedBy(): string[] { @@ -93,7 +97,10 @@ export class DirectivePowerMine extends Directive { } } - + setMiningDone(name: string) { + delete this.overlords[name]; + this.miningDone = true; + } init(): void { this.alert(`PowerMine directive active`); @@ -102,10 +109,10 @@ export class DirectivePowerMine extends Directive { run(): void { - if (Game.time % 100 == 0 && !this.haulDirectiveCreated) { - Game.notify('Checking if should spawn haulers'); - this.spawnHaulers(); - } + // if (Game.time % 100 == 0 && !this.haulDirectiveCreated) { + // Game.notify('Checking if should spawn haulers'); + // this.spawnHaulers(); + // } } } diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 055215290..56e711bc5 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -33,6 +33,7 @@ export class PowerDrillOverlord extends CombatOverlord { haulDirectiveCreated: boolean; partnerMap: Map; + isDone: boolean; drills: CombatZerg[]; coolant: CombatZerg[]; @@ -128,6 +129,7 @@ export class PowerDrillOverlord extends CombatOverlord { // If power bank is dead Game.notify("Power bank in " + this.room + " is dead."); coolant.say('💀 RIP 💀'); + this.isDone = true; coolant.suicide(); return; } @@ -187,6 +189,9 @@ export class PowerDrillOverlord extends CombatOverlord { run() { this.autoRun(this.drills, drill => this.handleDrill(drill)); this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); + if (this.isDone) { + this.directive.setMiningDone(this.name); + } } visuals() { From 5bc27cb319fca75a24d2e4ff163e1c12019d944b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 25 Apr 2019 11:30:41 -0700 Subject: [PATCH 025/137] Removing notification --- src/Overseer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Overseer.ts b/src/Overseer.ts index 8f5b16f5d..a7c77f28c 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -246,7 +246,7 @@ export class Overseer implements IOverseer { let alreadyReserved = RoomIntel.roomReservedBy(roomName); let isBlocked = Game.flags[roomName+"-Blocked"] != null; if (isBlocked) { - Game.notify("Room " + roomName + " is blocked, not expanding there."); + //Game.notify("Room " + roomName + " is blocked, not expanding there."); } let disregardReservations = !onPublicServer() || MY_USERNAME == MUON; if (alreadyOwned || (alreadyReserved && !disregardReservations) || isBlocked) { From c5c0ebf7d624374e8b3471fc5d0315c265d6618c Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 25 Apr 2019 11:43:35 -0700 Subject: [PATCH 026/137] Look at this point I'm just storing more code --- src/directives/resource/powerMine.ts | 14 +++++++------ src/overlords/powerMining/PowerDrill.ts | 24 +++++++++++++++++------ src/overlords/powerMining/PowerHauler.ts | 25 ++++++++++++++++++++---- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 0a87b5230..d28496d4e 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -25,7 +25,7 @@ export class DirectivePowerMine extends Directive { expectedSpawnTime = 200; miningDone: boolean; haulDirectiveCreated: boolean; - private _powerBank: StructurePowerBank | undefined; + powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; memory: DirectivePowerMineMemory; @@ -37,6 +37,8 @@ export class DirectivePowerMine extends Directive { spawnMoarOverlords() { if (!this.miningDone) { this.overlords.powerMine = new PowerDrillOverlord(this); + } else { + console.log('Mining is done!'); } this.spawnHaulers(); } @@ -68,7 +70,7 @@ export class DirectivePowerMine extends Directive { return 5000; // pick some non-zero number so that powerMiners will spawn } if (this.pos.isVisible) { - this.memory.totalResources = this._powerBank ? this._powerBank.power : this.memory.totalResources; // update total amount remaining + this.memory.totalResources = this.powerBank ? this.powerBank.power : this.memory.totalResources; // update total amount remaining } return this.memory.totalResources; } @@ -76,21 +78,21 @@ export class DirectivePowerMine extends Directive { calculateRemainingLifespan() { if (!this.room) { return undefined; - } else if (this._powerBank == undefined) { + } else if (this.powerBank == undefined) { // Power Bank is gone return 0; } else { - let tally = calculateFormationStrength(this._powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); + let tally = calculateFormationStrength(this.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); let healStrength: number = tally.heal * HEAL_POWER || 0; let attackStrength: number = tally.attack * ATTACK_POWER || 0; // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing let avgDamagePerTick = Math.min(attackStrength, healStrength*2); - return this._powerBank.hits / avgDamagePerTick; + return this.powerBank.hits / avgDamagePerTick; } } spawnHaulers() { - if (this.room && (!this._powerBank || (this.calculateRemainingLifespan()! < Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime))) { + if (this.room && (!this.powerBank || (this.calculateRemainingLifespan()! < (Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime)))) { Game.notify('Spawning haulers for power mining in room ' + this.room.name); this.haulDirectiveCreated = true; this.overlords.powerHaul = new PowerHaulingOverlord(this); diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 56e711bc5..eaa39bcf0 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -56,8 +56,10 @@ export class PowerDrillOverlord extends CombatOverlord { } init() { - this.wishlist(2, CombatSetups.drill.default); - this.wishlist(4, CombatSetups.coolant.default); + this.wishlist(1, CombatSetups.drill.default); + this.wishlist(2, CombatSetups.coolant.default); + this.wishlist(1, CombatSetups.drill.default); + this.wishlist(2, CombatSetups.coolant.default); } private getHostileDrill(powerBank: StructurePowerBank) { @@ -74,10 +76,12 @@ export class PowerDrillOverlord extends CombatOverlord { if (!this.room) { // We are not there yet } else { - var bank = this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank; - this.targetPowerBank = bank; // If power bank is dead - if (bank == undefined) { + if (this.targetPowerBank == undefined) { + this.targetPowerBank = this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank; + if (this.targetPowerBank) { + return; + } if (this.pos.lookFor(LOOK_RESOURCES).length == 0) { // Well shit, we didn't finish mining Game.notify("WE FUCKING FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); @@ -88,13 +92,14 @@ export class PowerDrillOverlord extends CombatOverlord { //this.directive.remove(); Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); drill.say('💀 RIP 💀'); + this.isDone = true; drill.suicide(); return; } } } - // Go to keeper room + // Go to power room if (!this.room || drill.room != this.room || drill.pos.isEdge || !this.targetPowerBank) { // log.debugCreep(drill, `Going to room!`); Game.notify("Drill is moving to power site in " + this.room + "."); @@ -110,6 +115,9 @@ export class PowerDrillOverlord extends CombatOverlord { // Handle killing bank if (drill.pos.isNearTo(this.targetPowerBank)) { + if (!this.partnerMap.get(drill.name)) { + this.partnerMap.set(drill.name, []); + } PowerDrillOverlord.periodicSay(drill,'Drilling⚒️'); drill.attack(this.targetPowerBank); } else { @@ -169,8 +177,12 @@ export class PowerDrillOverlord extends CombatOverlord { let needsHealing = _.min(Array.from(this.partnerMap.keys()), key => this.partnerMap.get(key)!.length); if (this.partnerMap.get(needsHealing)) { this.partnerMap.get(needsHealing)!.concat(coolant.name); + coolant.say(needsHealing.toString()); coolant.memory.partner = needsHealing; + } else { + } + //console.log(JSON.stringify(this.partnerMap)); // let newPartner = _.sample(_.filter(this.drills, drill => this.room == drill.room)); // coolant.memory.partner = newPartner != undefined ? newPartner.name : undefined; coolant.say('Partnering!'); diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index d2dd6d246..8c73d22c0 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -21,7 +21,6 @@ export class PowerHaulingOverlord extends Overlord { haulers: Zerg[]; directive: DirectivePowerMine; - powerBank: StructurePowerBank | undefined; tickToSpawnOn: number; numHaulers: number; @@ -59,16 +58,16 @@ export class PowerHaulingOverlord extends Overlord { calculateRemainingLifespan() { if (!this.room) { return undefined; - } else if (this.powerBank == undefined) { + } else if (this.directive.powerBank == undefined) { // Power Bank is gone return 0; } else { - let tally = calculateFormationStrength(this.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); + let tally = calculateFormationStrength(this.directive.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); let healStrength: number = tally.heal * HEAL_POWER || 0; let attackStrength: number = tally.attack * ATTACK_POWER || 0; // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing let avgDamagePerTick = Math.min(attackStrength, healStrength*2); - return this.powerBank.hits / avgDamagePerTick; + return this.directive.powerBank.hits / avgDamagePerTick; } } @@ -84,6 +83,24 @@ export class PowerHaulingOverlord extends Overlord { hauler.task = Tasks.pickup(drop); return; } + } else if (this.directive.powerBank) { + if (hauler.pos.getRangeTo(this.directive.powerBank) > 4) { + hauler.goTo(this.directive.powerBank); + } else { + hauler.say('🚬'); + } + return; + } else if (this.room && this.room.drops) { + let allDrops: Resource[] = _.flatten(_.values(this.room.drops)); + let drop = allDrops[0]; + if (drop) { + hauler.task = Tasks.pickup(drop); + return; + } else { + hauler.say('💀 RIP 💀'); + hauler.suicide(); + return; + } } // Withdraw from store structure // if (this.directive.storeStructure) { From 2f45e86d900ae5bd34b6dd1f8eaeca1455a13b72 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 25 Apr 2019 11:55:45 -0700 Subject: [PATCH 027/137] Cleaning up some code --- src/directives/resource/haul.ts | 5 ++--- src/directives/resource/powerMine.ts | 2 +- src/overlords/Overlord.ts | 13 +++++++++---- src/overlords/powerMining/PowerHauler.ts | 14 -------------- src/overlords/situational/hauler.ts | 2 +- 5 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 91db6f2db..830893856 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -109,9 +109,8 @@ export class DirectiveHaul extends Directive { run(): void { if (_.sum(this.store) == 0 && this.pos.isVisible) { - //this.remove(); - this.finishAtTime = Game.time + 1000; - //this.overlords + // If everything is picked up, crudely give enough time to bring it back + this.finishAtTime = Game.time + 800; } if (Game.time >= this.finishAtTime) { this.remove(); diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index d28496d4e..e78734a6a 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -22,7 +22,7 @@ export class DirectivePowerMine extends Directive { static color = COLOR_YELLOW; static secondaryColor = COLOR_RED; - expectedSpawnTime = 200; + expectedSpawnTime = 150; miningDone: boolean; haulDirectiveCreated: boolean; powerBank: StructurePowerBank | undefined; diff --git a/src/overlords/Overlord.ts b/src/overlords/Overlord.ts index f5a455ef2..3fa2ab1d9 100644 --- a/src/overlords/Overlord.ts +++ b/src/overlords/Overlord.ts @@ -45,10 +45,10 @@ export interface OverlordMemory { } enum overlordLifecyleState { - preparing, - active, - paused, - finishing + preparing, // Waiting to begin at correct time + active, // Currently running + paused, // Temporarily paused + finishing // Spawning is now disabled, run overlord until all creeps are dead } const OverlordMemoryDefaults: OverlordMemory = {}; @@ -326,6 +326,11 @@ export abstract class Overlord { protected requestCreep(setup: CreepSetup, opts = {} as CreepRequestOptions) { _.defaults(opts, {priority: this.priority, prespawn: DEFAULT_PRESPAWN}); let spawner = this.spawnGroup || this.colony.spawnGroup || this.colony.hatchery; + if (this.lifeCycle == overlordLifecyleState.paused || this.lifeCycle == overlordLifecyleState.finishing) { + // Don't + log.warning(`Overlord ${this.ref} @ ${this.pos.print}: State is ${this.lifeCycle}, not spawning!`); + return; + } if (spawner) { let request: SpawnRequest = { setup : setup, diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 8c73d22c0..fa514e3c5 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -102,20 +102,6 @@ export class PowerHaulingOverlord extends Overlord { return; } } - // Withdraw from store structure - // if (this.directive.storeStructure) { - // let store: { [resourceType: string]: number } = {}; - // if (this.directive.storeStructure instanceof StructurePowerBank) { - // store = {'power': this.directive.storeStructure.power}; - // } - // for (let resourceType in store) { - // if (store[resourceType] > 0) { - // //hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType); - // // Wait for it to die - // return; - // } - // } - // } // Shouldn't reach here log.warning(`${hauler.name} in ${hauler.room.print}: nothing to collect!`); } else { diff --git a/src/overlords/situational/hauler.ts b/src/overlords/situational/hauler.ts index 9655717ab..86bfd43a3 100644 --- a/src/overlords/situational/hauler.ts +++ b/src/overlords/situational/hauler.ts @@ -19,7 +19,7 @@ export class HaulingOverlord extends Overlord { haulers: Zerg[]; directive: DirectiveHaul; - requiredRCL: number = 4; + requiredRCL: 4; constructor(directive: DirectiveHaul, priority = directive.hasDrops ? OverlordPriority.collectionUrgent.haul : OverlordPriority.collection.haul) { From e41fed1f3fff3ac53ff0d8675e6671c32c9d917c Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sat, 27 Apr 2019 11:13:17 -0700 Subject: [PATCH 028/137] Adding configurable ranges and cleaning up --- src/declarations/memory.d.ts | 6 ++ src/directives/resource/powerMine.ts | 33 ++++--- src/intel/RoomIntel.ts | 35 +++++++- src/memory/Memory.ts | 6 ++ src/overlords/powerMining/PowerDrill.ts | 109 ++++++++++++----------- src/overlords/powerMining/PowerHauler.ts | 20 +++-- 6 files changed, 134 insertions(+), 75 deletions(-) diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 270b52c4f..53061c0cd 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -21,6 +21,12 @@ interface Memory { operationMode: operationMode; log: LoggerMemory; enableVisuals: boolean; + powerCollection: { + enabled: boolean; + maxRange: number; + minPower: number; + }; + resourceCollectionMode: resourceCollectionMode; } profiler?: any; stats: any; diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index e78734a6a..399f6bec2 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -1,10 +1,10 @@ import {Directive} from '../Directive'; import {profile} from '../../profiler/decorator'; -import {isStoreStructure} from '../../declarations/typeGuards'; import {PowerDrillOverlord} from '../../overlords/powerMining/PowerDrill'; import {Pathing} from "../../movement/Pathing"; import {calculateFormationStrength} from "../../utilities/creepUtils"; import {PowerHaulingOverlord} from "../../overlords/powerMining/PowerHauler"; +import {log} from "../../console/log"; interface DirectivePowerMineMemory extends FlagMemory { @@ -24,14 +24,16 @@ export class DirectivePowerMine extends Directive { expectedSpawnTime = 150; miningDone: boolean; + haulingDone: boolean; haulDirectiveCreated: boolean; - powerBank: StructurePowerBank | undefined; + private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; memory: DirectivePowerMineMemory; constructor(flag: Flag) { super(flag); + this._powerBank = this.room != undefined ? this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank : undefined; } spawnMoarOverlords() { @@ -62,6 +64,11 @@ export class DirectivePowerMine extends Directive { return _.keys(this.drops).length > 0; } + get powerBank(): StructurePowerBank | undefined { + this._powerBank = this._powerBank || this.room != undefined ? this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank : undefined; + return this._powerBank; + } + /** * Total amount of resources remaining to be transported; cached into memory in case room loses visibility */ @@ -72,49 +79,51 @@ export class DirectivePowerMine extends Directive { if (this.pos.isVisible) { this.memory.totalResources = this.powerBank ? this.powerBank.power : this.memory.totalResources; // update total amount remaining } + console.log("Directive total resources = " + this.totalResources); return this.memory.totalResources; } calculateRemainingLifespan() { + console.log(this._powerBank); if (!this.room) { return undefined; } else if (this.powerBank == undefined) { - // Power Bank is gone - return 0; + if (this.miningDone) { + // Power Bank is gone + return 0; + } } else { let tally = calculateFormationStrength(this.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); let healStrength: number = tally.heal * HEAL_POWER || 0; let attackStrength: number = tally.attack * ATTACK_POWER || 0; // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing let avgDamagePerTick = Math.min(attackStrength, healStrength*2); + console.log("Calculating PB remaining lifespan: " + this.powerBank.hits / avgDamagePerTick); return this.powerBank.hits / avgDamagePerTick; } } spawnHaulers() { - if (this.room && (!this.powerBank || (this.calculateRemainingLifespan()! < (Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime)))) { - Game.notify('Spawning haulers for power mining in room ' + this.room.name); + log.info("Checking spawning haulers"); + if (this.haulDirectiveCreated || this.room && (!this.powerBank || (this.calculateRemainingLifespan()! < (Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime)))) { + Game.notify('Spawning haulers for power mining in room ' + this.pos.roomName); this.haulDirectiveCreated = true; this.overlords.powerHaul = new PowerHaulingOverlord(this); } } setMiningDone(name: string) { + Game.notify("Setting mining done and removing overlord for power mine in room " + this.room + " at time " + Game.time); delete this.overlords[name]; this.miningDone = true; + this._powerBank = undefined; } init(): void { this.alert(`PowerMine directive active`); } - - run(): void { - // if (Game.time % 100 == 0 && !this.haulDirectiveCreated) { - // Game.notify('Checking if should spawn haulers'); - // this.spawnHaulers(); - // } } } diff --git a/src/intel/RoomIntel.ts b/src/intel/RoomIntel.ts index b3a6bf0ec..c587a90da 100644 --- a/src/intel/RoomIntel.ts +++ b/src/intel/RoomIntel.ts @@ -6,6 +6,9 @@ import {Zerg} from '../zerg/Zerg'; import {profile} from '../profiler/decorator'; import {MY_USERNAME} from '../~settings'; import {bodyCost} from '../creepSetups/CreepSetup'; +import {Cartographer, ROOMTYPE_ALLEY} from "../utilities/Cartographer"; +import {getAllColonies} from "../Colony"; +import {DirectivePowerMine} from "../directives/resource/powerMine"; const RECACHE_TIME = 2500; const OWNED_RECACHE_TIME = 1000; @@ -315,6 +318,36 @@ export class RoomIntel { return 0; } + /** + * Find powerbanks within range of maxRange and power above minPower to mine + * Creates directive to mine it + * TODO refactor when factory resources come out to be more generic + */ + private static minePowerBanks(room: Room) { + let powerSetting = Memory.settings.powerCollection; + if (powerSetting.enabled && Game.time % 300 == 0 && Cartographer.roomType(room.name) == ROOMTYPE_ALLEY) { + let powerBank = _.first(room.find(FIND_STRUCTURES).filter(struct => struct.structureType == STRUCTURE_POWER_BANK)) as StructurePowerBank; + if (powerBank != undefined && powerBank.ticksToDecay > 4000 && powerBank.power >= powerSetting.minPower) { + Game.notify(`Looking for power banks in ${room} found ${powerBank} with power ${powerBank.power} and ${powerBank.ticksToDecay} TTL.`); + if (DirectivePowerMine.isPresent(powerBank.pos, 'pos')) { + Game.notify(`Already mining room ${powerBank.room}!`); + return; + } + + let colonies = getAllColonies().filter(colony => colony.level > 6); + + for (let colony of colonies) { + let route = Game.map.findRoute(colony.room, powerBank.room); + if (route != -2 && route.length <= powerSetting.maxRange) { + Game.notify(`FOUND POWER BANK IN RANGE ${route.length}, STARTING MINING ${powerBank.room}`); + DirectivePowerMine.create(powerBank.pos); + return; + } + } + + } + } + } static run(): void { let alreadyComputedScore = false; @@ -353,7 +386,7 @@ export class RoomIntel { if (room.controller && Game.time % 5 == 0) { this.recordControllerInfo(room.controller); } - + this.minePowerBanks(room); } } diff --git a/src/memory/Memory.ts b/src/memory/Memory.ts index 67c9f751b..9ee428044 100644 --- a/src/memory/Memory.ts +++ b/src/memory/Memory.ts @@ -197,6 +197,12 @@ export class Mem { operationMode: DEFAULT_OPERATION_MODE, log : {}, enableVisuals: true, + powerCollection: { + enabled: false, + maxRange: 5, + minPower: 5000, + }, + resourceCollectionMode: 0, }); if (!Memory.stats) { Memory.stats = {}; diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index eaa39bcf0..93847c607 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -14,6 +14,7 @@ import {DirectivePowerMine} from "../../directives/resource/powerMine"; import {DirectiveHaul} from "../../directives/resource/haul"; import {calculateFormationStrength} from "../../utilities/creepUtils"; import {Zerg} from "../../zerg/Zerg"; +import {MoveOptions} from "../../movement/Movement"; interface PowerDrillOverlordMemory extends OverlordMemory { targetPBID?: string; @@ -29,9 +30,6 @@ export class PowerDrillOverlord extends CombatOverlord { directive: DirectivePowerMine; memory: PowerDrillOverlordMemory; - targetPowerBank: StructurePowerBank | undefined; - haulDirectiveCreated: boolean; - partnerMap: Map; isDone: boolean; @@ -45,7 +43,6 @@ export class PowerDrillOverlord extends CombatOverlord { this.drills = this.combatZerg(Roles.drill); this.coolant = this.combatZerg(Roles.coolant); this.memory = Mem.wrap(this.directive.memory, 'powerDrill'); - this.haulDirectiveCreated = false; this.partnerMap = new Map(); } @@ -72,16 +69,15 @@ export class PowerDrillOverlord extends CombatOverlord { } private handleDrill(drill: CombatZerg) { - if (!this.targetPowerBank) { + if (drill.spawning) { + return; + } + if (!this.directive.powerBank) { if (!this.room) { // We are not there yet } else { // If power bank is dead - if (this.targetPowerBank == undefined) { - this.targetPowerBank = this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank; - if (this.targetPowerBank) { - return; - } + if (this.directive.powerBank == undefined) { if (this.pos.lookFor(LOOK_RESOURCES).length == 0) { // Well shit, we didn't finish mining Game.notify("WE FUCKING FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); @@ -89,51 +85,51 @@ export class PowerDrillOverlord extends CombatOverlord { return; } Game.notify("Power bank in " + this.room + " is dead."); - //this.directive.remove(); - Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); drill.say('💀 RIP 💀'); - this.isDone = true; - drill.suicide(); + this.directive.setMiningDone(this.name); + let result = drill.suicide(); + if (result == ERR_BUSY) { + drill.spawning + } + //this.directive.remove(); + Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString() + " result: " + result); return; } } } // Go to power room - if (!this.room || drill.room != this.room || drill.pos.isEdge || !this.targetPowerBank) { + if (!this.room || drill.room != this.room || drill.pos.isEdge || !this.directive.powerBank) { // log.debugCreep(drill, `Going to room!`); - Game.notify("Drill is moving to power site in " + this.room + "."); + Game.notify("Drill is moving to power site in " + this.pos.roomName + "."); drill.goTo(this.pos); return; - } else if (this.targetPowerBank.hits < 800000 && !this.haulDirectiveCreated) { - Game.notify("Power bank in " + this.room + " is almost dead"); - Game.notify("Power bank in " + this.room + ", beginning haul operation."); - //this.haulDirectiveCreated = typeof DirectiveHaul.create(this.pos) == "string"; } - // Spawn a hauler for this location - // Should make a 49 carry 1 move creep to hold some, and a bunch of creeps to pick up ground first then container creep // Handle killing bank - if (drill.pos.isNearTo(this.targetPowerBank)) { + if (drill.pos.isNearTo(this.directive.powerBank)) { if (!this.partnerMap.get(drill.name)) { this.partnerMap.set(drill.name, []); } PowerDrillOverlord.periodicSay(drill,'Drilling⚒️'); - drill.attack(this.targetPowerBank); + drill.attack(this.directive.powerBank); } else { PowerDrillOverlord.periodicSay(drill,'🚗Traveling🚗'); - drill.goTo(this.targetPowerBank); + drill.goTo(this.directive.powerBank); } } private handleCoolant(coolant: CombatZerg) { + if (coolant.spawning) { + return; + } // Go to powerbank room if (!this.room || coolant.room != this.room || coolant.pos.isEdge) { // log.debugCreep(coolant, `Going to room!`); coolant.healSelfIfPossible(); coolant.goTo(this.pos); return; - } else if (!this.targetPowerBank) { + } else if (!this.directive.powerBank) { // If power bank is dead Game.notify("Power bank in " + this.room + " is dead."); coolant.say('💀 RIP 💀'); @@ -141,31 +137,15 @@ export class PowerDrillOverlord extends CombatOverlord { coolant.suicide(); return; } - - if (coolant.memory.partner) { - let drill = Game.creeps[coolant.memory.partner]; - if (!drill) { - // Partner is dead - coolant.memory.partner = undefined; - this.findDrillToPartner(coolant) - } else if (!coolant.pos.isNearTo(drill)) { - PowerDrillOverlord.periodicSay(coolant,'🚗Traveling️'); - coolant.goTo(drill); - } else { - PowerDrillOverlord.periodicSay(coolant,'❄️Cooling❄️'); - coolant.heal(drill); + if (coolant.pos.getRangeTo(this.directive.powerBank) > 3) { + coolant.goTo(this.directive.powerBank); + } else if (coolant.pos.findInRange(FIND_MY_CREEPS, 1).filter(creep => _.contains(creep.name, "drill")).length == 0) { + let target = _.sample(_.filter(this.drills, drill => drill.hits < drill.hitsMax)); + if (target) { + coolant.goTo(target); } - if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(coolant)) { - this.findDrillToPartner(coolant); - } - return; - } else { - this.findDrillToPartner(coolant); - } - if (coolant.pos.getRangeTo(this.targetPowerBank) > 2) { - coolant.goTo(this.targetPowerBank); - } else if (coolant.pos.getRangeTo(this.targetPowerBank) == 1) { - coolant.flee([this.targetPowerBank.pos]); + } else if (coolant.pos.getRangeTo(this.directive.powerBank) == 1) { + coolant.move(Math.round(Math.random()*7) as DirectionConstant); } else { coolant.goTo(_.sample(_.filter(this.drills, drill => drill.hits < drill.hitsMax))); } @@ -188,6 +168,29 @@ export class PowerDrillOverlord extends CombatOverlord { coolant.say('Partnering!'); } + private runPartnerHealing(coolant: CombatZerg) { + // if (coolant.memory.partner) { + // let drill = Game.creeps[coolant.memory.partner]; + // if (!drill) { + // // Partner is dead + // coolant.memory.partner = undefined; + // this.findDrillToPartner(coolant) + // } else if (!coolant.pos.isNearTo(drill)) { + // PowerDrillOverlord.periodicSay(coolant,'🚗Traveling️'); + // coolant.goTo(drill); + // } else { + // PowerDrillOverlord.periodicSay(coolant,'❄️Cooling❄️'); + // coolant.heal(drill); + // } + // if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(coolant)) { + // this.findDrillToPartner(coolant); + // } + // return; + // } else { + // this.findDrillToPartner(coolant); + // } + } + static periodicSay(zerg: CombatZerg, text: string) { if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(zerg)) { zerg.say(text, true); @@ -201,14 +204,14 @@ export class PowerDrillOverlord extends CombatOverlord { run() { this.autoRun(this.drills, drill => this.handleDrill(drill)); this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); - if (this.isDone) { + if (this.isDone && !this.directive.miningDone) { this.directive.setMiningDone(this.name); } } visuals() { - if (this.room && this.targetPowerBank) { - Visualizer.marker(this.targetPowerBank.pos); + if (this.room && this.directive.powerBank) { + Visualizer.marker(this.directive.powerBank.pos); } } diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index fa514e3c5..bcf68a3eb 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -1,15 +1,12 @@ import {Overlord} from '../Overlord'; import {OverlordPriority} from '../../priorities/priorities_overlords'; import {Zerg} from '../../zerg/Zerg'; -import {DirectiveHaul} from '../../directives/resource/haul'; import {Tasks} from '../../tasks/Tasks'; -import {isStoreStructure} from '../../declarations/typeGuards'; import {log} from '../../console/log'; import {Pathing} from '../../movement/Pathing'; import {Energetics} from '../../logistics/Energetics'; import {profile} from '../../profiler/decorator'; import {Roles, Setups} from '../../creepSetups/setups'; -import {HaulingOverlord} from "../situational/hauler"; import {calculateFormationStrength} from "../../utilities/creepUtils"; import {DirectivePowerMine} from "../../directives/resource/powerMine"; @@ -49,7 +46,7 @@ export class PowerHaulingOverlord extends Overlord { let haulerCarryParts = Setups.transporters.early.getBodyPotential(CARRY, this.colony); let haulingPowerPerLifetime = CREEP_LIFE_TIME * haulerCarryParts * CARRY_CAPACITY; // Calculate number of haulers - this.numHaulers = Math.min(Math.ceil(haulingPowerNeeded / haulingPowerPerLifetime), MAX_HAULERS); + this.numHaulers = Math.min(Math.ceil(haulingPowerNeeded / haulingPowerPerLifetime), MAX_HAULERS) * 2; // Request the haulers this.tickToSpawnOn = Game.time + (this.calculateRemainingLifespan() || 0) - this.prespawnAmount; } @@ -74,6 +71,12 @@ export class PowerHaulingOverlord extends Overlord { protected handleHauler(hauler: Zerg) { if (_.sum(hauler.carry) == 0) { // Travel to directive and collect resources + if (this.directive.haulingDone) { + hauler.say('💀 RIP 💀',true); + log.warning(`${hauler.name} is committing suicide as directive is done!`); + this.numHaulers = 0; + hauler.suicide(); + } if (hauler.inSameRoomAs(this.directive)) { // Pick up drops first if (this.directive.hasDrops) { @@ -87,7 +90,7 @@ export class PowerHaulingOverlord extends Overlord { if (hauler.pos.getRangeTo(this.directive.powerBank) > 4) { hauler.goTo(this.directive.powerBank); } else { - hauler.say('🚬'); + hauler.say('🚬', true); } return; } else if (this.room && this.room.drops) { @@ -97,7 +100,8 @@ export class PowerHaulingOverlord extends Overlord { hauler.task = Tasks.pickup(drop); return; } else { - hauler.say('💀 RIP 💀'); + hauler.say('💀 RIP 💀',true); + log.warning(`${hauler.name} is committing suicide!`); hauler.suicide(); return; } @@ -105,13 +109,11 @@ export class PowerHaulingOverlord extends Overlord { // Shouldn't reach here log.warning(`${hauler.name} in ${hauler.room.print}: nothing to collect!`); } else { - // hauler.task = Tasks.goTo(this.directive); hauler.goTo(this.directive); } } else { // Travel to colony room and deposit resources if (hauler.inSameRoomAs(this.colony)) { - // Put energy in storage and minerals in terminal if there is one for (let resourceType in hauler.carry) { if (hauler.carry[resourceType] == 0) continue; if (resourceType == RESOURCE_ENERGY) { // prefer to put energy in storage @@ -141,7 +143,7 @@ export class PowerHaulingOverlord extends Overlord { } run() { - if (Game.time >= this.tickToSpawnOn && this.haulers.length == 0) { + if (Game.time >= this.tickToSpawnOn) { this.wishlist(this.numHaulers, Setups.transporters.early); } for (let hauler of this.haulers) { From 45a68a907398815402299cf6c9ca482955d312f0 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sat, 27 Apr 2019 12:36:20 -0700 Subject: [PATCH 029/137] Finalizing --- src/creepSetups/setups.ts | 4 ++++ src/declarations/memory.d.ts | 1 - src/memory/Memory.ts | 1 - src/overlords/Overlord.ts | 13 ------------- src/overlords/powerMining/PowerDrill.ts | 16 +++++++--------- 5 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index f49de8b52..6dc917370 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -321,6 +321,10 @@ export const CombatSetups = { pattern : [HEAL, MOVE], sizeLimit: Infinity, }), + small: new CreepSetup(Roles.coolant, { + pattern : [HEAL, MOVE], + sizeLimit: 16, + }), } diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 53061c0cd..14b9e4380 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -26,7 +26,6 @@ interface Memory { maxRange: number; minPower: number; }; - resourceCollectionMode: resourceCollectionMode; } profiler?: any; stats: any; diff --git a/src/memory/Memory.ts b/src/memory/Memory.ts index 9ee428044..378924e9d 100644 --- a/src/memory/Memory.ts +++ b/src/memory/Memory.ts @@ -202,7 +202,6 @@ export class Mem { maxRange: 5, minPower: 5000, }, - resourceCollectionMode: 0, }); if (!Memory.stats) { Memory.stats = {}; diff --git a/src/overlords/Overlord.ts b/src/overlords/Overlord.ts index 3fa2ab1d9..a50b7998b 100644 --- a/src/overlords/Overlord.ts +++ b/src/overlords/Overlord.ts @@ -44,13 +44,6 @@ export interface OverlordMemory { suspendUntil?: number; } -enum overlordLifecyleState { - preparing, // Waiting to begin at correct time - active, // Currently running - paused, // Temporarily paused - finishing // Spawning is now disabled, run overlord until all creeps are dead -} - const OverlordMemoryDefaults: OverlordMemory = {}; /** @@ -76,7 +69,6 @@ export abstract class Overlord { private _combatZerg: { [roleName: string]: CombatZerg[] }; private boosts: { [roleName: string]: _ResourceConstantSansEnergy[] | undefined }; creepUsageReport: { [roleName: string]: [number, number] | undefined }; - lifeCycle: overlordLifecyleState; constructor(initializer: OverlordInitializer | Colony, name: string, priority: number) { this.initializer = initializer; @@ -326,11 +318,6 @@ export abstract class Overlord { protected requestCreep(setup: CreepSetup, opts = {} as CreepRequestOptions) { _.defaults(opts, {priority: this.priority, prespawn: DEFAULT_PRESPAWN}); let spawner = this.spawnGroup || this.colony.spawnGroup || this.colony.hatchery; - if (this.lifeCycle == overlordLifecyleState.paused || this.lifeCycle == overlordLifecyleState.finishing) { - // Don't - log.warning(`Overlord ${this.ref} @ ${this.pos.print}: State is ${this.lifeCycle}, not spawning!`); - return; - } if (spawner) { let request: SpawnRequest = { setup : setup, diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 93847c607..d882b1c0b 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -54,9 +54,7 @@ export class PowerDrillOverlord extends CombatOverlord { init() { this.wishlist(1, CombatSetups.drill.default); - this.wishlist(2, CombatSetups.coolant.default); - this.wishlist(1, CombatSetups.drill.default); - this.wishlist(2, CombatSetups.coolant.default); + this.wishlist(2, CombatSetups.coolant.small); } private getHostileDrill(powerBank: StructurePowerBank) { @@ -77,13 +75,13 @@ export class PowerDrillOverlord extends CombatOverlord { // We are not there yet } else { // If power bank is dead - if (this.directive.powerBank == undefined) { + if (this.directive.powerBank == undefined && !this.directive.haulDirectiveCreated) { if (this.pos.lookFor(LOOK_RESOURCES).length == 0) { - // Well shit, we didn't finish mining - Game.notify("WE FUCKING FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString()); - this.directive.remove(); - return; - } + // Well shit, we didn't finish mining + Game.notify(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING CREEP at time: ${Game.time}`); + this.directive.remove(); + return; + } Game.notify("Power bank in " + this.room + " is dead."); drill.say('💀 RIP 💀'); this.directive.setMiningDone(this.name); From 9f3d37883ce584a7fca573ceb69e6664d036ba53 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 2 May 2019 17:21:27 -0700 Subject: [PATCH 030/137] storing for transfer --- src/directives/resource/powerMine.ts | 9 ++++++++- src/intel/RoomIntel.ts | 2 +- src/overlords/powerMining/PowerHauler.ts | 25 +++++++++++------------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 399f6bec2..1da0e5b31 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -23,6 +23,13 @@ export class DirectivePowerMine extends Directive { static secondaryColor = COLOR_RED; expectedSpawnTime = 150; + const DrillStatus = { + drilling : 0, + AcquiringMinerals: 1, + LoadingLabs : 2, + Synthesizing : 3, + UnloadingLabs : 4, + }; miningDone: boolean; haulingDone: boolean; haulDirectiveCreated: boolean; @@ -106,7 +113,7 @@ export class DirectivePowerMine extends Directive { spawnHaulers() { log.info("Checking spawning haulers"); if (this.haulDirectiveCreated || this.room && (!this.powerBank || (this.calculateRemainingLifespan()! < (Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime)))) { - Game.notify('Spawning haulers for power mining in room ' + this.pos.roomName); + Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); this.haulDirectiveCreated = true; this.overlords.powerHaul = new PowerHaulingOverlord(this); } diff --git a/src/intel/RoomIntel.ts b/src/intel/RoomIntel.ts index c587a90da..7a3a7b8b5 100644 --- a/src/intel/RoomIntel.ts +++ b/src/intel/RoomIntel.ts @@ -319,7 +319,7 @@ export class RoomIntel { } /** - * Find powerbanks within range of maxRange and power above minPower to mine + * Find PowerBanks within range of maxRange and power above minPower to mine * Creates directive to mine it * TODO refactor when factory resources come out to be more generic */ diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index bcf68a3eb..8c8b71592 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -23,7 +23,7 @@ export class PowerHaulingOverlord extends Overlord { requiredRCL = 6; // Allow time for body to spawn - prespawnAmount = 200; + prespawnAmount = 250; constructor(directive: DirectivePowerMine, priority = OverlordPriority.collectionUrgent.haul) { super(directive, 'powerHaul', priority); @@ -35,23 +35,19 @@ export class PowerHaulingOverlord extends Overlord { if (!this.colony.storage || _.sum(this.colony.storage.store) > Energetics.settings.storage.total.cap) { return; } - // Spawn a number of haulers sufficient to move all resources within a lifetime, up to a max - let MAX_HAULERS = 5; - // Calculate total needed amount of hauling power as (resource amount * trip distance) - let tripDistance = 2 * Pathing.distance((this.colony.storage || this.colony).pos, this.directive.pos); - let haulingPowerNeeded = Math.min(this.directive.totalResources, - this.colony.storage.storeCapacity - - _.sum(this.colony.storage.store)) * tripDistance; + // Spawn haulers to collect ALL the power at the same time. + let haulingPartsNeeded = this.directive.totalResources/50; // Calculate amount of hauling each hauler provides in a lifetime - let haulerCarryParts = Setups.transporters.early.getBodyPotential(CARRY, this.colony); - let haulingPowerPerLifetime = CREEP_LIFE_TIME * haulerCarryParts * CARRY_CAPACITY; + let haulerCarryParts = Setups.transporters.default.getBodyPotential(CARRY, this.colony); // Calculate number of haulers - this.numHaulers = Math.min(Math.ceil(haulingPowerNeeded / haulingPowerPerLifetime), MAX_HAULERS) * 2; - // Request the haulers + this.numHaulers = Math.round(haulingPartsNeeded/haulerCarryParts); + // setup time to request the haulers this.tickToSpawnOn = Game.time + (this.calculateRemainingLifespan() || 0) - this.prespawnAmount; } - + /** + * Calculates how many remaining ticks the power bank has left at current kill rate + */ calculateRemainingLifespan() { if (!this.room) { return undefined; @@ -144,7 +140,8 @@ export class PowerHaulingOverlord extends Overlord { run() { if (Game.time >= this.tickToSpawnOn) { - this.wishlist(this.numHaulers, Setups.transporters.early); + Game.notify('Time to spawn haulers ' + this.pos.roomName); + this.wishlist(this.numHaulers, Setups.transporters.default); } for (let hauler of this.haulers) { if (hauler.isIdle) { From 3cbf89a4ac45999c618fed9cd1e7502b1a9f3180 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 6 May 2019 13:05:43 -0700 Subject: [PATCH 031/137] Adding mining termination --- src/directives/resource/powerMine.ts | 28 ++++++++++-------------- src/overlords/powerMining/PowerDrill.ts | 5 ++++- src/overlords/powerMining/PowerHauler.ts | 14 +++++++----- src/zerg/Zerg.ts | 11 ++++++++++ 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 1da0e5b31..3790c3d50 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -22,14 +22,8 @@ export class DirectivePowerMine extends Directive { static color = COLOR_YELLOW; static secondaryColor = COLOR_RED; - expectedSpawnTime = 150; - const DrillStatus = { - drilling : 0, - AcquiringMinerals: 1, - LoadingLabs : 2, - Synthesizing : 3, - UnloadingLabs : 4, - }; + expectedSpawnTime = 250; + _pathingDistance: number; miningDone: boolean; haulingDone: boolean; haulDirectiveCreated: boolean; @@ -46,14 +40,10 @@ export class DirectivePowerMine extends Directive { spawnMoarOverlords() { if (!this.miningDone) { this.overlords.powerMine = new PowerDrillOverlord(this); - } else { - console.log('Mining is done!'); } - this.spawnHaulers(); - } - - get targetedBy(): string[] { - return Overmind.cache.targets[this.ref]; + if (!this.haulingDone) { + this.spawnHaulers(); + } } get drops(): { [resourceType: string]: Resource[] } { @@ -110,9 +100,15 @@ export class DirectivePowerMine extends Directive { } } + get pathingDistance(): number { + this._pathingDistance = this._pathingDistance || Pathing.distance(this.colony.pos, this.flag.pos); + return this._pathingDistance; + } + spawnHaulers() { log.info("Checking spawning haulers"); - if (this.haulDirectiveCreated || this.room && (!this.powerBank || (this.calculateRemainingLifespan()! < (Pathing.distance(this.colony.pos, this.flag.pos) + this.expectedSpawnTime)))) { + // Begin checking for spawn haulers at 666 estimated ticks before PB destruction + if (this.haulDirectiveCreated || this.room && (!this.powerBank || this.powerBank.hits < 500000)) { Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); this.haulDirectiveCreated = true; this.overlords.powerHaul = new PowerHaulingOverlord(this); diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index d882b1c0b..eb62896f2 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -85,7 +85,7 @@ export class PowerDrillOverlord extends CombatOverlord { Game.notify("Power bank in " + this.room + " is dead."); drill.say('💀 RIP 💀'); this.directive.setMiningDone(this.name); - let result = drill.suicide(); + let result = drill.retire(); if (result == ERR_BUSY) { drill.spawning } @@ -203,6 +203,9 @@ export class PowerDrillOverlord extends CombatOverlord { this.autoRun(this.drills, drill => this.handleDrill(drill)); this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); if (this.isDone && !this.directive.miningDone) { + this.drills.forEach(drill => drill.retire()); + this.coolant.forEach(coolant => coolant.retire()); + delete this.directive.overlords[this.name]; this.directive.setMiningDone(this.name); } } diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 8c8b71592..02ca4fd7c 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -42,7 +42,7 @@ export class PowerHaulingOverlord extends Overlord { // Calculate number of haulers this.numHaulers = Math.round(haulingPartsNeeded/haulerCarryParts); // setup time to request the haulers - this.tickToSpawnOn = Game.time + (this.calculateRemainingLifespan() || 0) - this.prespawnAmount; + this.tickToSpawnOn = Game.time + (this.directive.calculateRemainingLifespan() || 0) - this.prespawnAmount; } /** @@ -65,13 +65,15 @@ export class PowerHaulingOverlord extends Overlord { } protected handleHauler(hauler: Zerg) { - if (_.sum(hauler.carry) == 0) { + if (_.sum(hauler.carry) == 0 && this.directive.haulingDone) { + hauler.retire(); + } else if (_.sum(hauler.carry) == 0) { // Travel to directive and collect resources if (this.directive.haulingDone) { hauler.say('💀 RIP 💀',true); log.warning(`${hauler.name} is committing suicide as directive is done!`); this.numHaulers = 0; - hauler.suicide(); + hauler.retire(); } if (hauler.inSameRoomAs(this.directive)) { // Pick up drops first @@ -98,7 +100,7 @@ export class PowerHaulingOverlord extends Overlord { } else { hauler.say('💀 RIP 💀',true); log.warning(`${hauler.name} is committing suicide!`); - hauler.suicide(); + hauler.retire(); return; } } @@ -139,9 +141,11 @@ export class PowerHaulingOverlord extends Overlord { } run() { - if (Game.time >= this.tickToSpawnOn) { + if (Game.time >= this.tickToSpawnOn && !this.directive.haulingDone) { Game.notify('Time to spawn haulers ' + this.pos.roomName); this.wishlist(this.numHaulers, Setups.transporters.default); + } else if (this.directive.haulingDone && this.haulers.length == 0) { + this.directive.remove(); } for (let hauler of this.haulers) { if (hauler.isIdle) { diff --git a/src/zerg/Zerg.ts b/src/zerg/Zerg.ts index 26cc22dc6..feea2d555 100644 --- a/src/zerg/Zerg.ts +++ b/src/zerg/Zerg.ts @@ -508,6 +508,17 @@ export class Zerg { setOverlord(this, newOverlord); } + // TODO add retire/reassignment logic + // Eg. creep get repurposed, it gets recycled, etc + /** + * When a zerg has no more use for it's current overlord, it will be retired. + * For now, that means RIP + */ + retire() { + this.say('💀 RIP 💀', true); + return this.suicide(); + } + /* Reassigns the creep to work under a new overlord and as a new role. */ reassign(newOverlord: Overlord | null, newRole: string, invalidateTask = true) { this.overlord = newOverlord; From 1901aaeb5642d7e46865ccb2161b891be6c8865f Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 7 May 2019 14:48:46 -0700 Subject: [PATCH 032/137] Adding termination logic --- src/directives/resource/powerMine.ts | 17 +++-- src/overlords/powerMining/PowerDrill.ts | 88 ++++++++++++------------ src/overlords/powerMining/PowerHauler.ts | 22 +----- 3 files changed, 56 insertions(+), 71 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 3790c3d50..07cbb46b3 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -22,8 +22,6 @@ export class DirectivePowerMine extends Directive { static color = COLOR_YELLOW; static secondaryColor = COLOR_RED; - expectedSpawnTime = 250; - _pathingDistance: number; miningDone: boolean; haulingDone: boolean; haulDirectiveCreated: boolean; @@ -100,11 +98,6 @@ export class DirectivePowerMine extends Directive { } } - get pathingDistance(): number { - this._pathingDistance = this._pathingDistance || Pathing.distance(this.colony.pos, this.flag.pos); - return this._pathingDistance; - } - spawnHaulers() { log.info("Checking spawning haulers"); // Begin checking for spawn haulers at 666 estimated ticks before PB destruction @@ -122,6 +115,16 @@ export class DirectivePowerMine extends Directive { this._powerBank = undefined; } + /** + * This states when all the power has been picked up. Once all power has been picked up and delivered remove the directive + */ + isHaulingDone(): boolean { + if (!this.haulingDone && this.miningDone && this.pos.isVisible && this.hasDrops) { + this.haulingDone = true; + } + return this.haulingDone; + } + init(): void { this.alert(`PowerMine directive active`); } diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index eb62896f2..fa837a29a 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -132,7 +132,7 @@ export class PowerDrillOverlord extends CombatOverlord { Game.notify("Power bank in " + this.room + " is dead."); coolant.say('💀 RIP 💀'); this.isDone = true; - coolant.suicide(); + coolant.retire(); return; } if (coolant.pos.getRangeTo(this.directive.powerBank) > 3) { @@ -140,54 +140,56 @@ export class PowerDrillOverlord extends CombatOverlord { } else if (coolant.pos.findInRange(FIND_MY_CREEPS, 1).filter(creep => _.contains(creep.name, "drill")).length == 0) { let target = _.sample(_.filter(this.drills, drill => drill.hits < drill.hitsMax)); if (target) { - coolant.goTo(target); + coolant.goTo(target, {range: 1, noPush: true}); } - } else if (coolant.pos.getRangeTo(this.directive.powerBank) == 1) { - coolant.move(Math.round(Math.random()*7) as DirectionConstant); - } else { + } + // else if (coolant.pos.getRangeTo(this.directive.powerBank) == 1) { + // coolant.move(Math.round(Math.random()*7) as DirectionConstant); + // } + else { coolant.goTo(_.sample(_.filter(this.drills, drill => drill.hits < drill.hitsMax))); } coolant.autoHeal(); } - private findDrillToPartner(coolant: CombatZerg) { - let needsHealing = _.min(Array.from(this.partnerMap.keys()), key => this.partnerMap.get(key)!.length); - if (this.partnerMap.get(needsHealing)) { - this.partnerMap.get(needsHealing)!.concat(coolant.name); - coolant.say(needsHealing.toString()); - coolant.memory.partner = needsHealing; - } else { - - } - //console.log(JSON.stringify(this.partnerMap)); - // let newPartner = _.sample(_.filter(this.drills, drill => this.room == drill.room)); - // coolant.memory.partner = newPartner != undefined ? newPartner.name : undefined; - coolant.say('Partnering!'); - } - - private runPartnerHealing(coolant: CombatZerg) { - // if (coolant.memory.partner) { - // let drill = Game.creeps[coolant.memory.partner]; - // if (!drill) { - // // Partner is dead - // coolant.memory.partner = undefined; - // this.findDrillToPartner(coolant) - // } else if (!coolant.pos.isNearTo(drill)) { - // PowerDrillOverlord.periodicSay(coolant,'🚗Traveling️'); - // coolant.goTo(drill); - // } else { - // PowerDrillOverlord.periodicSay(coolant,'❄️Cooling❄️'); - // coolant.heal(drill); - // } - // if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(coolant)) { - // this.findDrillToPartner(coolant); - // } - // return; - // } else { - // this.findDrillToPartner(coolant); - // } - } + // private findDrillToPartner(coolant: CombatZerg) { + // let needsHealing = _.min(Array.from(this.partnerMap.keys()), key => this.partnerMap.get(key)!.length); + // if (this.partnerMap.get(needsHealing)) { + // this.partnerMap.get(needsHealing)!.concat(coolant.name); + // coolant.say(needsHealing.toString()); + // coolant.memory.partner = needsHealing; + // } else { + // + // } + // //console.log(JSON.stringify(this.partnerMap)); + // // let newPartner = _.sample(_.filter(this.drills, drill => this.room == drill.room)); + // // coolant.memory.partner = newPartner != undefined ? newPartner.name : undefined; + // coolant.say('Partnering!'); + // } + // + // private runPartnerHealing(coolant: CombatZerg) { + // if (coolant.memory.partner) { + // let drill = Game.creeps[coolant.memory.partner]; + // if (!drill) { + // // Partner is dead + // coolant.memory.partner = undefined; + // this.findDrillToPartner(coolant) + // } else if (!coolant.pos.isNearTo(drill)) { + // PowerDrillOverlord.periodicSay(coolant,'🚗Traveling️'); + // coolant.goTo(drill); + // } else { + // PowerDrillOverlord.periodicSay(coolant,'❄️Cooling❄️'); + // coolant.heal(drill); + // } + // if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(coolant)) { + // this.findDrillToPartner(coolant); + // } + // return; + // } else { + // this.findDrillToPartner(coolant); + // } + // } static periodicSay(zerg: CombatZerg, text: string) { if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(zerg)) { @@ -205,8 +207,8 @@ export class PowerDrillOverlord extends CombatOverlord { if (this.isDone && !this.directive.miningDone) { this.drills.forEach(drill => drill.retire()); this.coolant.forEach(coolant => coolant.retire()); - delete this.directive.overlords[this.name]; this.directive.setMiningDone(this.name); + delete this.directive.overlords[this.name]; } } diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 02ca4fd7c..cc0e606b1 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -3,7 +3,6 @@ import {OverlordPriority} from '../../priorities/priorities_overlords'; import {Zerg} from '../../zerg/Zerg'; import {Tasks} from '../../tasks/Tasks'; import {log} from '../../console/log'; -import {Pathing} from '../../movement/Pathing'; import {Energetics} from '../../logistics/Energetics'; import {profile} from '../../profiler/decorator'; import {Roles, Setups} from '../../creepSetups/setups'; @@ -36,7 +35,7 @@ export class PowerHaulingOverlord extends Overlord { return; } // Spawn haulers to collect ALL the power at the same time. - let haulingPartsNeeded = this.directive.totalResources/50; + let haulingPartsNeeded = this.directive.totalResources/CARRY_CAPACITY; // Calculate amount of hauling each hauler provides in a lifetime let haulerCarryParts = Setups.transporters.default.getBodyPotential(CARRY, this.colony); // Calculate number of haulers @@ -45,25 +44,6 @@ export class PowerHaulingOverlord extends Overlord { this.tickToSpawnOn = Game.time + (this.directive.calculateRemainingLifespan() || 0) - this.prespawnAmount; } - /** - * Calculates how many remaining ticks the power bank has left at current kill rate - */ - calculateRemainingLifespan() { - if (!this.room) { - return undefined; - } else if (this.directive.powerBank == undefined) { - // Power Bank is gone - return 0; - } else { - let tally = calculateFormationStrength(this.directive.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); - let healStrength: number = tally.heal * HEAL_POWER || 0; - let attackStrength: number = tally.attack * ATTACK_POWER || 0; - // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing - let avgDamagePerTick = Math.min(attackStrength, healStrength*2); - return this.directive.powerBank.hits / avgDamagePerTick; - } - } - protected handleHauler(hauler: Zerg) { if (_.sum(hauler.carry) == 0 && this.directive.haulingDone) { hauler.retire(); From b7dc16511beabf60a3adb073d513525c6549074a Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 7 May 2019 16:30:32 -0700 Subject: [PATCH 033/137] Adding listing directive console command --- src/console/Console.ts | 28 ++++++++++++++++++++++++++++ src/overlords/Overlord.ts | 4 ++++ tsconfig.json | 3 ++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/console/Console.ts b/src/console/Console.ts index 7b5446dce..6f4ec395c 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -5,6 +5,7 @@ import {log} from './log'; import {alignedNewline, bullet} from '../utilities/stringConstants'; import {DEFAULT_OVERMIND_SIGNATURE, MY_USERNAME, USE_PROFILER} from '../~settings'; import {Directive} from '../directives/Directive'; +import {Overlord} from "../overlords/Overlord"; type RecursiveObject = { [key: string]: number | RecursiveObject } @@ -36,6 +37,7 @@ export class OvermindConsole { global.listConstructionSites = this.listConstructionSites; global.listDirectives = this.listDirectives; global.listPersistentDirectives = this.listPersistentDirectives; + global.directiveInfo = this.directiveInfo; global.removeAllLogisticsDirectives = this.removeAllLogisticsDirectives; global.removeFlagsByColor = this.removeFlagsByColor; global.removeErrantFlags = this.removeErrantFlags; @@ -78,6 +80,7 @@ export class OvermindConsole { descr['listConstructionSites(filter?)'] = 'list all construction sites matching an optional filter'; descr['listDirectives(filter?)'] = 'list directives, matching a filter if specified'; descr['listPersistentDirectives()'] = 'print type, name, pos of every persistent directive'; + descr['directiveInfo(directiveFlag)'] = 'print type, name, pos of every creep in directive'; descr['removeFlagsByColor(color, secondaryColor)'] = 'remove flags that match the specified colors'; descr['removeErrantFlags()'] = 'remove all flags which don\'t match a directive'; descr['deepCleanMemory()'] = 'deletes all non-critical portions of memory (be careful!)'; @@ -371,6 +374,31 @@ export class OvermindConsole { return `Removed ${count} flags.`; } + static directiveInfo(flagName: string): string { + let msg = ''; + let directive = Overmind.directives[flagName]; + if (!directive) { + return `ERROR: Name is not a current directive` + } + msg += `Type: ${directive.directiveName}`.padRight(20) + + `Name: ${directive.name}`.padRight(25) + + `Pos: ${directive.pos.print}\n`; + for (let overlordName of Object.keys(directive.overlords)) { + let overlord = directive.overlords[overlordName] as Overlord; + msg += JSON.stringify(overlord.creepUsageReport) + `\n`; + for (let [roleName, zergArray] of Object.entries(overlord.getZerg())) { + msg += `Role: ${roleName} \n`; + for (let zerg of zergArray) { + msg += `Name: ${zerg.name} Room: ${zerg.pos.print} TTL/Spawning: ${zerg.ticksToLive || zerg.spawning} \n`; + } + } + } + + return msg; + } + + + // Structure management ============================================================================================ diff --git a/src/overlords/Overlord.ts b/src/overlords/Overlord.ts index a50b7998b..115a630f8 100644 --- a/src/overlords/Overlord.ts +++ b/src/overlords/Overlord.ts @@ -447,6 +447,10 @@ export abstract class Overlord { } } + getZerg() { + return this._zerg; + } + /* Request any needed boosting resources from terminal network */ private requestBoosts(creeps: Zerg[]): void { for (let creep of creeps) { diff --git a/tsconfig.json b/tsconfig.json index 46f462e7e..0002602a6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,8 @@ "target": "es6", "lib": [ "dom", - "es2016" + "es2016", + "es2017.object" ], "newLine": "CRLF", "outDir": "tsc-out", From 1781f15b5e099862ff9f51648f93f57acfe36a44 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 8 May 2019 09:46:09 -0700 Subject: [PATCH 034/137] Moar stuff --- src/directives/resource/powerMine.ts | 3 +-- src/overlords/powerMining/PowerDrill.ts | 6 +++--- src/overlords/powerMining/PowerHauler.ts | 13 +++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 07cbb46b3..f1f76509f 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -79,7 +79,6 @@ export class DirectivePowerMine extends Directive { } calculateRemainingLifespan() { - console.log(this._powerBank); if (!this.room) { return undefined; } else if (this.powerBank == undefined) { @@ -119,7 +118,7 @@ export class DirectivePowerMine extends Directive { * This states when all the power has been picked up. Once all power has been picked up and delivered remove the directive */ isHaulingDone(): boolean { - if (!this.haulingDone && this.miningDone && this.pos.isVisible && this.hasDrops) { + if (!this.haulingDone && this.miningDone && this.pos.isVisible && !this.hasDrops) { this.haulingDone = true; } return this.haulingDone; diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index fa837a29a..79f7560c9 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -191,14 +191,14 @@ export class PowerDrillOverlord extends CombatOverlord { // } // } - static periodicSay(zerg: CombatZerg, text: string) { + static periodicSay(zerg: Zerg, text: string) { if (Game.time % 10 == PowerDrillOverlord.getCreepNameOffset(zerg)) { zerg.say(text, true); } } - static getCreepNameOffset(creep: Zerg) { - return parseInt(creep.name.charAt(creep.name.length-1)) || 0; + static getCreepNameOffset(zerg: Zerg) { + return parseInt(zerg.name.charAt(zerg.name.length-1)) || 0; } run() { diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index cc0e606b1..16735e74b 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -28,12 +28,6 @@ export class PowerHaulingOverlord extends Overlord { super(directive, 'powerHaul', priority); this.directive = directive; this.haulers = this.zerg(Roles.transport); - } - - init() { - if (!this.colony.storage || _.sum(this.colony.storage.store) > Energetics.settings.storage.total.cap) { - return; - } // Spawn haulers to collect ALL the power at the same time. let haulingPartsNeeded = this.directive.totalResources/CARRY_CAPACITY; // Calculate amount of hauling each hauler provides in a lifetime @@ -44,6 +38,12 @@ export class PowerHaulingOverlord extends Overlord { this.tickToSpawnOn = Game.time + (this.directive.calculateRemainingLifespan() || 0) - this.prespawnAmount; } + init() { + if (!this.colony.storage || _.sum(this.colony.storage.store) > Energetics.settings.storage.total.cap) { + return; + } + } + protected handleHauler(hauler: Zerg) { if (_.sum(hauler.carry) == 0 && this.directive.haulingDone) { hauler.retire(); @@ -125,6 +125,7 @@ export class PowerHaulingOverlord extends Overlord { Game.notify('Time to spawn haulers ' + this.pos.roomName); this.wishlist(this.numHaulers, Setups.transporters.default); } else if (this.directive.haulingDone && this.haulers.length == 0) { + Game.notify('Deleting Power Mining Directive at ' + this.pos.print); this.directive.remove(); } for (let hauler of this.haulers) { From f49a075fd0d99acaad1e0f2bf1d5af480e1b7b27 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 8 May 2019 15:20:59 -0700 Subject: [PATCH 035/137] PR comments --- src/console/Console.ts | 1 + src/directives/colony/clearRoom.ts | 14 ++++++-------- src/utilities/utils.ts | 9 --------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/console/Console.ts b/src/console/Console.ts index 14a13c2b1..b32fb88cc 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -69,6 +69,7 @@ export class OvermindConsole { descr['setLogLevel(int)'] = 'set the logging level from 0 - 4'; descr['suspendColony(roomName)'] = 'suspend operations within a colony'; descr['unsuspendColony(roomName)'] = 'resume operations within a suspended colony'; + descr['listSuspendedColonies()'] = 'Prints all suspended colonies'; descr['openRoomPlanner(roomName)'] = 'open the room planner for a room'; descr['closeRoomPlanner(roomName)'] = 'close the room planner and save changes'; descr['cancelRoomPlanner(roomName)'] = 'close the room planner and discard changes'; diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 076be1b85..2e7de2e52 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -60,9 +60,9 @@ export class DirectiveClearRoom extends Directive { for (let s of allStructures) { if (s.structureType == STRUCTURE_CONTROLLER) continue; if (keepStorageStructures && - (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && hasContents(s.store)) { + (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && ~s.isEmpty) { // Create a collection flag - let result = s.pos.createFlag(undefined, DirectiveHaul.color, DirectiveHaul.secondaryColor); + DirectiveHaul.create(s.pos); continue; } if (keepRoads && s.structureType == STRUCTURE_ROAD) { @@ -95,18 +95,16 @@ export class DirectiveClearRoom extends Directive { } // Clear path if controller is not reachable } else if (this.room && this.room.creeps.length > 1) { - let currentlyDismantling = _.find(this.room.flags, function(flag) { - return (flag.color == DirectiveDismantle.color && flag.secondaryColor == DirectiveDismantle.secondaryColor) - }); + let currentlyDismantlingLocations = DirectiveDismantle.find(this.room.flags); - if (!currentlyDismantling) { + if (currentlyDismantlingLocations.length == 0) { let pathablePos = this.room.creeps[0] ? this.room.creeps[0].pos : Pathing.findPathablePosition(this.room.name); let blockingLocation = Pathing.findBlockingPos(pathablePos, this.room.controller!.pos, _.filter(this.room.structures, s => !s.isWalkable)); - if (blockingLocation && blockingLocation.lookFor(LOOK_FLAGS).length <= 0) { + if (blockingLocation && !Directive.isPresent(blockingLocation, 'pos')) { log.notify(`Adding dismantle directive for ${this.pos.roomName} to reach controller.`); - blockingLocation!.createFlag(undefined, DirectiveDismantle.color, DirectiveDismantle.secondaryColor); + DirectiveDismantle.create(blockingLocation); } } } diff --git a/src/utilities/utils.ts b/src/utilities/utils.ts index 47341cd81..33b465b82 100644 --- a/src/utilities/utils.ts +++ b/src/utilities/utils.ts @@ -34,15 +34,6 @@ export function hasMinerals(store: { [resourceType: string]: number }): boolean return false; } -export function hasContents(store: { [resourceType: string]: number }): boolean { - for (let resourceType in store) { - if ((store[resourceType] || 0) > 0) { - return true; - } - } - return false; -} - /** * Obtain the username of the player */ From 9261391c1b645e68c9d46f770ebd7468d38a798a Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 8 May 2019 16:30:03 -0700 Subject: [PATCH 036/137] fixing a NPE on case with no drills --- src/overlords/powerMining/PowerDrill.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 79f7560c9..6731fc6b4 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -147,7 +147,8 @@ export class PowerDrillOverlord extends CombatOverlord { // coolant.move(Math.round(Math.random()*7) as DirectionConstant); // } else { - coolant.goTo(_.sample(_.filter(this.drills, drill => drill.hits < drill.hitsMax))); + let drill = _.sample(_.filter(this.drills, drill => drill.hits < drill.hitsMax)); + if (drill) { coolant.goTo(drill); } } coolant.autoHeal(); From 67fa7efa90d0140125aaacdf163cac660d3ae3cd Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 9 May 2019 16:32:27 -0700 Subject: [PATCH 037/137] Invasion defense improvements --- src/directives/defense/invasionDefense.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 2e0c26899..9302d4f39 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -36,15 +36,16 @@ export class DirectiveInvasionDefense extends Directive { if (!this.room) { return; } - let expectedDamage = CombatIntel.maxDamageByCreeps(this.room.dangerousHostiles); - let useBoosts = (expectedDamage > ATTACK_POWER * 75) + let expectedDamage = CombatIntel.maxDamageByCreeps(this.room.dangerousPlayerHostiles); + console.log(`ATTACK POWER EXPECTED IS ${expectedDamage} in room ${this.room.print}, safe for ${Game.time - this.memory.safeSince}`); + let useBoosts = (expectedDamage > ATTACK_POWER * 14) && !!this.colony.terminal && !!this.colony.evolutionChamber; - let percentWalls = _.filter(this.room.barriers, s => s.structureType == STRUCTURE_WALL).length / - this.room.barriers.length; - let meleeHostiles = _.filter(this.room.hostiles, hostile => hostile.getActiveBodyparts(ATTACK) > 0 || - hostile.getActiveBodyparts(WORK) > 0); - let rangedHostiles = _.filter(this.room.hostiles, hostile => hostile.getActiveBodyparts(RANGED_ATTACK) > 0); + // let percentWalls = _.filter(this.room.barriers, s => s.structureType == STRUCTURE_WALL).length / + // this.room.barriers.length; + // let meleeHostiles = _.filter(this.room.hostiles, hostile => hostile.getActiveBodyparts(ATTACK) > 0 || + // hostile.getActiveBodyparts(WORK) > 0); + // let rangedHostiles = _.filter(this.room.hostiles, hostile => hostile.getActiveBodyparts(RANGED_ATTACK) > 0); if (this.colony.stage > ColonyStage.Larva) { this.overlords.rangedDefense = new RangedDefenseOverlord(this, useBoosts); } else { @@ -64,7 +65,7 @@ export class DirectiveInvasionDefense extends Directive { } // If there are no hostiles left in the room and everyone's healed, then remove the flag if (this.room && this.room.hostiles.length == 0 && - Game.time - this.memory.safeSince > 100 && this.room.hostileStructures.length == 0) { + Game.time - this.memory.safeSince > 300 && this.room.hostileStructures.length == 0) { if (_.filter(this.room.creeps, creep => creep.hits < creep.hitsMax).length == 0) { this.remove(); } From d951040197f7d063537c54a13454104dfec1ebaa Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 9 May 2019 16:53:36 -0700 Subject: [PATCH 038/137] Updating info for combat creeps as well --- src/console/Console.ts | 11 ++++++++++- src/overlords/Overlord.ts | 8 +++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/console/Console.ts b/src/console/Console.ts index 6f4ec395c..0f7ab12c1 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -386,7 +386,16 @@ export class OvermindConsole { for (let overlordName of Object.keys(directive.overlords)) { let overlord = directive.overlords[overlordName] as Overlord; msg += JSON.stringify(overlord.creepUsageReport) + `\n`; - for (let [roleName, zergArray] of Object.entries(overlord.getZerg())) { + let zerg = overlord.getZerg(); + let combatZerg = overlord.getCombatZerg(); + for (let [roleName, zergArray] of Object.entries(zerg)) { + msg += `Role: ${roleName} \n`; + for (let zerg of zergArray) { + msg += `Name: ${zerg.name} Room: ${zerg.pos.print} TTL/Spawning: ${zerg.ticksToLive || zerg.spawning} \n`; + } + } + msg += `Combat zerg \n`; + for (let [roleName, zergArray] of Object.entries(combatZerg)) { msg += `Role: ${roleName} \n`; for (let zerg of zergArray) { msg += `Name: ${zerg.name} Room: ${zerg.pos.print} TTL/Spawning: ${zerg.ticksToLive || zerg.spawning} \n`; diff --git a/src/overlords/Overlord.ts b/src/overlords/Overlord.ts index 115a630f8..0709a6af8 100644 --- a/src/overlords/Overlord.ts +++ b/src/overlords/Overlord.ts @@ -451,7 +451,13 @@ export abstract class Overlord { return this._zerg; } - /* Request any needed boosting resources from terminal network */ + getCombatZerg() { + return this._combatZerg; + } + + /** + * Request any needed boosting resources from terminal network + */ private requestBoosts(creeps: Zerg[]): void { for (let creep of creeps) { if (this.shouldBoost(creep)) { From 73e6161bdd1c0f8ea5bed772eca4ebd83bc5115d Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 9 May 2019 20:11:01 -0700 Subject: [PATCH 039/137] Adding invasion improvements --- src/directives/defense/invasionDefense.ts | 11 ++++++++--- src/directives/offense/swarmDestroy.ts | 3 ++- src/logistics/SpawnGroup.ts | 2 +- src/overlords/defense/rangedDefense.ts | 9 ++++++++- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 9302d4f39..3d0a98744 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -5,6 +5,10 @@ import {ColonyStage} from '../../Colony'; import {CombatIntel} from '../../intel/CombatIntel'; import {MeleeDefenseOverlord} from '../../overlords/defense/meleeDefense'; import {NotifierPriority} from '../Notifier'; +import {isCombatZerg, isZerg} from "../../declarations/typeGuards"; +import {getOverlord, normalizeZerg, toCreep, Zerg} from "../../zerg/Zerg"; +import {CombatZerg} from "../../zerg/CombatZerg"; +import {isUndefined} from "util"; interface DirectiveInvasionDefenseMemory extends FlagMemory { persistent?: boolean; @@ -25,6 +29,9 @@ export class DirectiveInvasionDefense extends Directive { memory: DirectiveInvasionDefenseMemory; room: Room | undefined; + safeEndTime: 800; + safeSpawnHaltTime: 100; + private relocateFrequency: number; constructor(flag: Flag) { @@ -32,7 +39,6 @@ export class DirectiveInvasionDefense extends Directive { } spawnMoarOverlords() { - if (!this.room) { return; } @@ -51,7 +57,6 @@ export class DirectiveInvasionDefense extends Directive { } else { this.overlords.meleeDefense = new MeleeDefenseOverlord(this, useBoosts); } - } init(): void { @@ -65,7 +70,7 @@ export class DirectiveInvasionDefense extends Directive { } // If there are no hostiles left in the room and everyone's healed, then remove the flag if (this.room && this.room.hostiles.length == 0 && - Game.time - this.memory.safeSince > 300 && this.room.hostileStructures.length == 0) { + Game.time - this.memory.safeSince > this.safeEndTime && this.room.hostileStructures.length == 0) { if (_.filter(this.room.creeps, creep => creep.hits < creep.hitsMax).length == 0) { this.remove(); } diff --git a/src/directives/offense/swarmDestroy.ts b/src/directives/offense/swarmDestroy.ts index a3f0b50ec..4ff4d7fa2 100644 --- a/src/directives/offense/swarmDestroy.ts +++ b/src/directives/offense/swarmDestroy.ts @@ -13,13 +13,14 @@ export class DirectiveSwarmDestroy extends Directive { static directiveName = 'destroy'; static color = COLOR_RED; static secondaryColor = COLOR_RED; + static requiredRCL = 6; overlords: { destroy: SwarmDestroyerOverlord; }; constructor(flag: Flag) { - super(flag); + super(flag, colony => colony.level >= DirectiveSwarmDestroy.requiredRCL); } spawnMoarOverlords() { diff --git a/src/logistics/SpawnGroup.ts b/src/logistics/SpawnGroup.ts index 47692155c..28a0a5b2c 100644 --- a/src/logistics/SpawnGroup.ts +++ b/src/logistics/SpawnGroup.ts @@ -152,7 +152,7 @@ export class SpawnGroup { if (bestHatchery) { bestHatchery.enqueue(request); } else { - log.warning(`Could not enqueue creep ${request.setup.role} in ${this.roomName}, no hatchery with ${maxCost} energy capacity`); + log.warning(`Could not enqueue creep ${request.setup.role} in ${this.roomName} for Overlord ${request.overlord.name}, no hatchery with ${maxCost} energy capacity.`); } } } diff --git a/src/overlords/defense/rangedDefense.ts b/src/overlords/defense/rangedDefense.ts index b6b5d086a..a27afef6d 100644 --- a/src/overlords/defense/rangedDefense.ts +++ b/src/overlords/defense/rangedDefense.ts @@ -15,7 +15,9 @@ import {CombatSetups, Roles} from '../../creepSetups/setups'; export class RangedDefenseOverlord extends CombatOverlord { hydralisks: CombatZerg[]; + room: Room; + directive: DirectiveInvasionDefense; static settings = { retreatHitsPercent : 0.85, @@ -47,7 +49,12 @@ export class RangedDefenseOverlord extends CombatOverlord { let towerDamage = this.room.hostiles[0] ? CombatIntel.towerDamageAtPos(this.room.hostiles[0].pos) || 0 : 0; let worstDamageMultiplier = _.min(_.map(this.room.hostiles, creep => CombatIntel.minimumDamageTakenMultiplier(creep))); - return Math.ceil(.5 + 1.5 * healAmount / (worstDamageMultiplier * (hydraliskDamage + towerDamage + 1))); + let finalValue = Math.ceil(.5 + 1.5 * healAmount / (worstDamageMultiplier * (hydraliskDamage + towerDamage + 1))); + if ((Game.time - this.directive.memory.safeSince) > this.directive.safeSpawnHaltTime) { + console.log(`Been safe for ${(Game.time - this.directive.memory.safeSince)}, setting hydralisk wishlist in ${this.room.print} to 0.`) + return 0 + } + return finalValue; } init() { From fa0a21c58c530eea5ebf18baff3affff163e2016 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 9 May 2019 20:22:36 -0700 Subject: [PATCH 040/137] Fixing termination again --- src/directives/resource/powerMine.ts | 13 +++++++------ src/overlords/powerMining/PowerHauler.ts | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index f1f76509f..f3fa5bee2 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -23,7 +23,7 @@ export class DirectivePowerMine extends Directive { static secondaryColor = COLOR_RED; miningDone: boolean; - haulingDone: boolean; + pickupDone: boolean; haulDirectiveCreated: boolean; private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; @@ -39,7 +39,7 @@ export class DirectivePowerMine extends Directive { if (!this.miningDone) { this.overlords.powerMine = new PowerDrillOverlord(this); } - if (!this.haulingDone) { + if (!this.pickupDone) { this.spawnHaulers(); } } @@ -117,11 +117,12 @@ export class DirectivePowerMine extends Directive { /** * This states when all the power has been picked up. Once all power has been picked up and delivered remove the directive */ - isHaulingDone(): boolean { - if (!this.haulingDone && this.miningDone && this.pos.isVisible && !this.hasDrops) { - this.haulingDone = true; + isPickupDone(): boolean { + if (!this.pickupDone && this.miningDone && this.room && this.pos.isVisible && !this.hasDrops) { + this.pickupDone = true; + Game.notify(`Hauling is done for ${this.room.print} at time ${Game.time}`); } - return this.haulingDone; + return this.pickupDone; } init(): void { diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 16735e74b..715d3ca62 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -45,11 +45,11 @@ export class PowerHaulingOverlord extends Overlord { } protected handleHauler(hauler: Zerg) { - if (_.sum(hauler.carry) == 0 && this.directive.haulingDone) { + if (_.sum(hauler.carry) == 0 && this.directive.pickupDone) { hauler.retire(); } else if (_.sum(hauler.carry) == 0) { // Travel to directive and collect resources - if (this.directive.haulingDone) { + if (this.directive.pickupDone) { hauler.say('💀 RIP 💀',true); log.warning(`${hauler.name} is committing suicide as directive is done!`); this.numHaulers = 0; @@ -121,12 +121,18 @@ export class PowerHaulingOverlord extends Overlord { } run() { - if (Game.time >= this.tickToSpawnOn && !this.directive.haulingDone) { + if (Game.time >= this.tickToSpawnOn && !this.directive.pickupDone) { Game.notify('Time to spawn haulers ' + this.pos.roomName); this.wishlist(this.numHaulers, Setups.transporters.default); - } else if (this.directive.haulingDone && this.haulers.length == 0) { - Game.notify('Deleting Power Mining Directive at ' + this.pos.print); - this.directive.remove(); + } + // Check hauling is done + !this.directive.pickupDone && this.directive.isPickupDone(); + if (this.directive.pickupDone) { + let stillCarryingPower = _.find(this.haulers, hauler => hauler.carry.power != undefined && hauler.carry.power > 0); + if (!stillCarryingPower) { + Game.notify('Deleting Power Mining Directive as no haulers are left carrying ' + this.pos.print); + this.directive.remove(); + } } for (let hauler of this.haulers) { if (hauler.isIdle) { From dcbcdddbccca2905fe9a68ac284f5bc9c2b60b30 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 9 May 2019 21:12:12 -0700 Subject: [PATCH 041/137] Tiny log statement --- src/overlords/powerMining/PowerHauler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 715d3ca62..d056193df 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -132,6 +132,8 @@ export class PowerHaulingOverlord extends Overlord { if (!stillCarryingPower) { Game.notify('Deleting Power Mining Directive as no haulers are left carrying ' + this.pos.print); this.directive.remove(); + } else { + console.log(`Still carrying power back with ${stillCarryingPower.print}`); } } for (let hauler of this.haulers) { From ba29f3799e0971e44a7a04f54a6ae97e8d84d48c Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 9 May 2019 23:27:14 -0700 Subject: [PATCH 042/137] Fix bug --- src/directives/defense/invasionDefense.ts | 4 ++-- src/overlords/defense/rangedDefense.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 3d0a98744..0cfd1a4c6 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -44,7 +44,7 @@ export class DirectiveInvasionDefense extends Directive { } let expectedDamage = CombatIntel.maxDamageByCreeps(this.room.dangerousPlayerHostiles); console.log(`ATTACK POWER EXPECTED IS ${expectedDamage} in room ${this.room.print}, safe for ${Game.time - this.memory.safeSince}`); - let useBoosts = (expectedDamage > ATTACK_POWER * 14) + let useBoosts = (expectedDamage > ATTACK_POWER * 13) && !!this.colony.terminal && !!this.colony.evolutionChamber; // let percentWalls = _.filter(this.room.barriers, s => s.structureType == STRUCTURE_WALL).length / @@ -70,7 +70,7 @@ export class DirectiveInvasionDefense extends Directive { } // If there are no hostiles left in the room and everyone's healed, then remove the flag if (this.room && this.room.hostiles.length == 0 && - Game.time - this.memory.safeSince > this.safeEndTime && this.room.hostileStructures.length == 0) { + (Game.time - this.memory.safeSince) > this.safeEndTime && this.room.hostileStructures.length == 0) { if (_.filter(this.room.creeps, creep => creep.hits < creep.hitsMax).length == 0) { this.remove(); } diff --git a/src/overlords/defense/rangedDefense.ts b/src/overlords/defense/rangedDefense.ts index a27afef6d..73e8cd20b 100644 --- a/src/overlords/defense/rangedDefense.ts +++ b/src/overlords/defense/rangedDefense.ts @@ -50,10 +50,10 @@ export class RangedDefenseOverlord extends CombatOverlord { let worstDamageMultiplier = _.min(_.map(this.room.hostiles, creep => CombatIntel.minimumDamageTakenMultiplier(creep))); let finalValue = Math.ceil(.5 + 1.5 * healAmount / (worstDamageMultiplier * (hydraliskDamage + towerDamage + 1))); - if ((Game.time - this.directive.memory.safeSince) > this.directive.safeSpawnHaltTime) { - console.log(`Been safe for ${(Game.time - this.directive.memory.safeSince)}, setting hydralisk wishlist in ${this.room.print} to 0.`) - return 0 - } + // if ((Game.time - this.directive.memory.safeSince) > this.directive.safeSpawnHaltTime) { + // console.log(`Been safe for ${(Game.time - this.directive.memory.safeSince)}, setting hydralisk wishlist in ${this.room.print} to 0.`) + // return 0 + // } return finalValue; } From b32c4413f3093123f9252b5bd0eb47c1cb0d59e7 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 10 May 2019 00:35:34 -0700 Subject: [PATCH 043/137] Double mining --- src/overlords/mining/miner.ts | 100 +++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index 3404b86ed..67e8d5b65 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -32,6 +32,7 @@ export class MiningOverlord extends Overlord { directive: DirectiveHarvest; room: Room | undefined; source: Source | undefined; + secondSource: Source | undefined; container: StructureContainer | undefined; link: StructureLink | undefined; constructionSite: ConstructionSite | undefined; @@ -65,6 +66,9 @@ export class MiningOverlord extends Overlord { this.energyPerTick = SOURCE_ENERGY_NEUTRAL_CAPACITY / ENERGY_REGEN_TIME; } this.miningPowerNeeded = Math.ceil(this.energyPerTick / HARVEST_POWER) + 1; + let doubleMine = this.checkIfDoubleMine(); + + // Decide operating mode if (Cartographer.roomType(this.pos.roomName) == ROOMTYPE_SOURCEKEEPER) { this.mode = 'SK'; @@ -72,7 +76,11 @@ export class MiningOverlord extends Overlord { } else if (this.colony.room.energyCapacityAvailable < StandardMinerSetupCost) { this.mode = 'early'; this.setup = Setups.drones.miners.default; - } else if (this.link) { + } else if (doubleMine && this.colony.room.energyCapacityAvailable <= DoubleMinerSetupCost) { + this.mode = 'double'; + this.setup = Setups.drones.miners.double; + } + else if (this.link) { this.mode = 'link'; this.setup = Setups.drones.miners.default; } else { @@ -97,6 +105,43 @@ export class MiningOverlord extends Overlord { } } + checkIfDoubleMine() { + if (Game.rooms[this.pos.roomName]) { + this.source = _.first(this.pos.lookFor(LOOK_SOURCES)); + let nearby = this.source.pos.findInRange(FIND_SOURCES, 2); + if (nearby.length > 0) { + this.secondSource = nearby[0]; + return true; + } + } + return false; + } + + + + + + + // let sources = this.room.find(FIND_SOURCES); + // for (let i = 0; i = Math.min(miner.carryCapacity, REPAIR_POWER * miner.getActiveBodyparts(WORK))) { + return miner.repair(this.container); + } else if (this.source && this.source.energy == 0) { + return miner.harvest(this.source!); + } else if (this.secondSource != undefined) { + return miner.harvest(this.secondSource!); + } + } + + // Build output site + if (this.constructionSite) { + if (miner.carry.energy >= Math.min(miner.carryCapacity, BUILD_POWER * miner.getActiveBodyparts(WORK))) { + return miner.build(this.constructionSite); + } else { + return miner.harvest(this.source!); + } + } + + // Drop mining + if (this.allowDropMining) { + miner.harvest(this.source!); + if (miner.carry.energy > 0.8 * miner.carryCapacity) { // move over the drop when you're close to full + let biggestDrop = maxBy(miner.pos.findInRange(miner.room.droppedEnergy, 1), drop => drop.amount); + if (biggestDrop) { + miner.goTo(biggestDrop); + } + } + if (miner.carry.energy == miner.carryCapacity) { // drop when you are full + miner.drop(RESOURCE_ENERGY); + } + return; + } + } + + private doubleMine() { + // if (this.source && this.source.energy == 0) { + // return miner.harvest(this.source!); + // } else if (this.secondSource != undefined) { + // return miner.harvest(this.secondSource!); + // } + } + /** * Move onto harvesting position or near to source (depending on early/standard mode) */ From 63d90cc5f0bf640e1937403d23169def5b95ab6b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 10 May 2019 09:30:08 -0700 Subject: [PATCH 044/137] More logic --- src/overlords/mining/miner.ts | 67 +++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index 67e8d5b65..b23ae4f37 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -12,6 +12,7 @@ import {Pathing} from '../../movement/Pathing'; import {ColonyStage} from '../../Colony'; import {maxBy, minBy} from '../../utilities/utils'; import {Roles, Setups} from '../../creepSetups/setups'; +import {Directive} from "../../directives/Directive"; export const StandardMinerSetupCost = bodyCost(Setups.drones.miners.standard.generateBody(Infinity)); @@ -33,6 +34,7 @@ export class MiningOverlord extends Overlord { room: Room | undefined; source: Source | undefined; secondSource: Source | undefined; + isDisabled: boolean; container: StructureContainer | undefined; link: StructureLink | undefined; constructionSite: ConstructionSite | undefined; @@ -76,11 +78,10 @@ export class MiningOverlord extends Overlord { } else if (this.colony.room.energyCapacityAvailable < StandardMinerSetupCost) { this.mode = 'early'; this.setup = Setups.drones.miners.default; - } else if (doubleMine && this.colony.room.energyCapacityAvailable <= DoubleMinerSetupCost) { + } else if (doubleMine && this.colony.room.energyCapacityAvailable > DoubleMinerSetupCost) { this.mode = 'double'; this.setup = Setups.drones.miners.double; - } - else if (this.link) { + } else if (this.link) { this.mode = 'link'; this.setup = Setups.drones.miners.default; } else { @@ -89,8 +90,10 @@ export class MiningOverlord extends Overlord { // todo: double miner condition } const miningPowerEach = this.setup.getBodyPotential(WORK, this.colony); + // this.minersNeeded = this.isDisabled ? 0 : Math.min(Math.ceil(this.miningPowerNeeded / miningPowerEach), + // this.pos.availableNeighbors(true).length); this.minersNeeded = Math.min(Math.ceil(this.miningPowerNeeded / miningPowerEach), - this.pos.availableNeighbors(true).length); + this.pos.availableNeighbors(true).length); // Allow drop mining at low levels this.allowDropMining = this.colony.level < MiningOverlord.settings.dropMineUntilRCL; if (this.mode != 'early' && !this.allowDropMining) { @@ -107,21 +110,27 @@ export class MiningOverlord extends Overlord { checkIfDoubleMine() { if (Game.rooms[this.pos.roomName]) { + //console.log('Double mining check'); this.source = _.first(this.pos.lookFor(LOOK_SOURCES)); - let nearby = this.source.pos.findInRange(FIND_SOURCES, 2); + let nearby = this.source.pos.findInRange(FIND_SOURCES, 2).filter(source => this.source != source); if (nearby.length > 0) { this.secondSource = nearby[0]; + let miningPos = this.source.pos.getPositionAtDirection(this.source.pos.getDirectionTo(this.secondSource.pos)); + if (miningPos.isWalkable()) { + console.log(`Double mining found but there is no spot between ${this.secondSource} ${this.secondSource.pos.print} isWalkable ${miningPos}`); + return false; + } + console.log(`Double mining found ${this.secondSource} ${this.secondSource.pos.print}`); + if (this.source.id > this.secondSource.id) { + console.log(`This is a disabled mining ${this.directive.name} via source id`); + this.isDisabled = true; + } return true; } } return false; } - - - - - // let sources = this.room.find(FIND_SOURCES); // for (let i = 0; i 0) { + miner.harvest(this.source!); + } else { + console.log('mining second source'); + miner.harvest(this.secondSource!); + } + if (miner.carry.energy > 0.9 * miner.carryCapacity) { + miner.transfer(this.link, RESOURCE_ENERGY); + } + return; + } else { + log.warning(`Link miner ${miner.print} has no link!`); + } // Container mining if (this.container) { if (this.container.hits < this.container.hitsMax && miner.carry.energy >= Math.min(miner.carryCapacity, REPAIR_POWER * miner.getActiveBodyparts(WORK))) { return miner.repair(this.container); - } else if (this.source && this.source.energy == 0) { + } else if (this.source && this.source.energy > 0) { return miner.harvest(this.source!); - } else if (this.secondSource != undefined) { + } else { + console.log('mining second source'); return miner.harvest(this.secondSource!); } } @@ -424,14 +455,6 @@ export class MiningOverlord extends Overlord { } } - private doubleMine() { - // if (this.source && this.source.energy == 0) { - // return miner.harvest(this.source!); - // } else if (this.secondSource != undefined) { - // return miner.harvest(this.secondSource!); - // } - } - /** * Move onto harvesting position or near to source (depending on early/standard mode) */ @@ -477,7 +500,7 @@ export class MiningOverlord extends Overlord { case 'SK': return this.standardMiningActions(miner); case 'double': - return this.standardMiningActions(miner); + return this.doubleMiningActions(miner); default: log.error(`UNHANDLED MINER STATE FOR ${miner.print} (MODE: ${this.mode})`); } From dedcf6299a4b89dc3092c68faa45d0f3b66e7490 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 13 May 2019 23:38:28 -0700 Subject: [PATCH 045/137] Fixing !walkable --- src/overlords/mining/miner.ts | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index fd2fc2557..349cdb643 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -93,6 +93,7 @@ export class MiningOverlord extends Overlord { // this.pos.availableNeighbors(true).length); this.minersNeeded = Math.min(Math.ceil(this.miningPowerNeeded / miningPowerEach), this.pos.availableNeighbors(true).length); + this.minersNeeded = this.isDisabled ? 0 : this.minersNeeded; // Allow drop mining at low levels this.allowDropMining = this.colony.level < MiningOverlord.settings.dropMineUntilRCL; if (this.mode != 'early' && !this.allowDropMining) { @@ -115,7 +116,7 @@ export class MiningOverlord extends Overlord { if (nearby.length > 0) { this.secondSource = nearby[0]; let miningPos = this.source.pos.getPositionAtDirection(this.source.pos.getDirectionTo(this.secondSource.pos)); - if (miningPos.isWalkable()) { + if (!miningPos.isWalkable()) { console.log(`Double mining found but there is no spot between ${this.secondSource} ${this.secondSource.pos.print} isWalkable ${miningPos}`); return false; } @@ -130,25 +131,6 @@ export class MiningOverlord extends Overlord { return false; } - // let sources = this.room.find(FIND_SOURCES); - // for (let i = 0; i Date: Tue, 14 May 2019 01:02:38 -0700 Subject: [PATCH 046/137] Adding a harass directive to annoy remote mines --- src/directives/initializer.ts | 3 + src/directives/offense/harass.ts | 83 ++++++++++++++++++++++++++ src/overlords/offense/harass.ts | 66 ++++++++++++++++++++ src/priorities/priorities_overlords.ts | 3 +- 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/directives/offense/harass.ts create mode 100644 src/overlords/offense/harass.ts diff --git a/src/directives/initializer.ts b/src/directives/initializer.ts index 081a081bd..8565d94bd 100644 --- a/src/directives/initializer.ts +++ b/src/directives/initializer.ts @@ -25,6 +25,7 @@ import {DirectiveTargetSiege} from './targeting/siegeTarget'; import {DirectiveTerminalEmergencyState} from './terminalState/terminalState_emergency'; import {DirectiveTerminalEvacuateState} from './terminalState/terminalState_evacuate'; import {DirectiveTerminalRebuildState} from './terminalState/terminalState_rebuild'; +import {DirectiveHarass} from "./offense/harass"; /** * This is the initializer for directives, which maps flags by their color code to the corresponding directive @@ -58,6 +59,8 @@ export function DirectiveWrapper(flag: Flag): Directive | undefined { return new DirectivePairDestroy(flag); case COLOR_PURPLE: return new DirectiveControllerAttack(flag); + case COLOR_WHITE: + return new DirectiveHarass(flag); } break; diff --git a/src/directives/offense/harass.ts b/src/directives/offense/harass.ts new file mode 100644 index 000000000..44366079e --- /dev/null +++ b/src/directives/offense/harass.ts @@ -0,0 +1,83 @@ +import {RoomIntel} from '../../intel/RoomIntel'; +import {profile} from '../../profiler/decorator'; +import {Directive} from '../Directive'; +import {HarassOverlord} from "../../overlords/offense/harass"; +import {log, Log} from "../../console/log"; +import {MY_USERNAME} from "../../~settings"; + +interface DirectiveHarassMemory extends FlagMemory { + enhanced?: boolean; + targetPlayer?: string; + roomsToHarass: string[]; +} + +/** + * Harass Directive that wanders through enemy rooms killing stuff + * Placed on an enemy room, it will harass all of it's remotes periodically + */ +@profile +export class DirectiveHarass extends Directive { + + static directiveName = 'harass'; + static color = COLOR_RED; + static secondaryColor = COLOR_WHITE; + + memory: DirectiveHarassMemory; + + constructor(flag: Flag) { + super(flag); + this.memory.targetPlayer = RoomIntel.roomOwnedBy(flag.pos.roomName); + if (this.memory.targetPlayer == MY_USERNAME) { + log.error(`Ahhhhhh harassing self in room ${flag.pos.roomName}`); + this.remove(); + } + if (this.memory.targetPlayer) { + this.memory.roomsToHarass = this.findNearbyReservedRooms(flag.pos.roomName, this.memory.targetPlayer); + } + } + + spawnMoarOverlords() { + // For now, just spawn from RCL 5+ rooms + this.overlords.harassOverlord = new HarassOverlord(this, this.memory.enhanced); + } + + init(): void { + // if + // if (!this.memory.roomsToHarass && this.memory.targetPlayer) + + + } + + /** + * Finds the rooms to harass + * + * @param roomName + * @param playerName + */ + findNearbyReservedRooms(roomName: string, playerName: string): string[] { + if (!this.memory.targetPlayer) { + log.error(`Unable to find which player to harass in room ${roomName}`); + return []; + } + let reservedByTargetPlayer: string[] = []; + let adjacentRooms = _.values(Game.map.describeExits(roomName)) as string[]; + adjacentRooms.forEach(room => { + if (RoomIntel.roomReservedBy(room) == playerName) { + reservedByTargetPlayer.push(room); + // This will double add rooms next to owned rooms, making it more likely to harass them + (_.values(Game.map.describeExits(room)) as string[]).forEach(room => { + if (RoomIntel.roomReservedBy(room) == playerName) { + reservedByTargetPlayer.push(room); + } + }) + } + }); + Game.notify(`Looking for nearby rooms to harass, found ${reservedByTargetPlayer}`); + return reservedByTargetPlayer; + } + + run(): void { + // Probably something modifying frequency of harassment + + } +} diff --git a/src/overlords/offense/harass.ts b/src/overlords/offense/harass.ts new file mode 100644 index 000000000..a20124a2a --- /dev/null +++ b/src/overlords/offense/harass.ts @@ -0,0 +1,66 @@ +import {CreepSetup} from '../../creepSetups/CreepSetup'; +import {CombatSetups, Roles} from '../../creepSetups/setups'; +import {DirectiveInvasionDefense} from '../../directives/defense/invasionDefense'; +import {CombatIntel} from '../../intel/CombatIntel'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {profile} from '../../profiler/decorator'; +import {boostResources} from '../../resources/map_resources'; +import {CombatZerg} from '../../zerg/CombatZerg'; +import {CombatOverlord} from '../CombatOverlord'; +import {DirectiveHarass} from "../../directives/offense/harass"; + +/** + * Spawns ranged harassers to stop mining for an enemy room + */ +@profile +export class HarassOverlord extends CombatOverlord { + + hydralisks: CombatZerg[]; + room: Room; + nextTarget: string; + directive: DirectiveHarass; + + + static settings = { + retreatHitsPercent : 0.85, + reengageHitsPercent: 0.95, + }; + + constructor(directive: DirectiveHarass, + boosted = false, + priority = OverlordPriority.offense.harass) { + super(directive, 'harass', priority, 1); + this.directive = directive; + this.hydralisks = this.combatZerg(Roles.ranged, { + boostWishlist: boosted ? [boostResources.ranged_attack[3], boostResources.heal[3], boostResources.move[3]] + : undefined + }); + } + + private handleHarass(hydralisk: CombatZerg): void { + if (this.nextTarget && this.room.name != this.nextTarget) { + hydralisk.goToRoom(this.nextTarget); + } else if (hydralisk.room.dangerousPlayerHostiles.length > 2) { + // Time to move on + this.moveToNearbyRoom(hydralisk, hydralisk.room.name); + } + hydralisk.autoCombat(this.room.name); + // Clean up infra then move on to another room + } + + moveToNearbyRoom(hydralisk: CombatZerg, currentRoom: string) { + this.nextTarget = _.sample(this.directive.memory.roomsToHarass); + hydralisk.goToRoom(this.nextTarget); + } + + init() { + this.reassignIdleCreeps(Roles.ranged); + const setup = CombatSetups.hydralisks.default; + this.wishlist(1, setup); + } + + run() { + console.log(`Matt: Running directive harass in ${this.room.print}`); + this.autoRun(this.hydralisks, hydralisk => this.handleHarass(hydralisk)); + } +} diff --git a/src/priorities/priorities_overlords.ts b/src/priorities/priorities_overlords.ts index bef3ab436..1ed197744 100644 --- a/src/priorities/priorities_overlords.ts +++ b/src/priorities/priorities_overlords.ts @@ -22,7 +22,8 @@ export let OverlordPriority = { destroy : 300, healPoint : 301, siege : 302, - controllerAttack: 399 + controllerAttack: 399, + harass : 571, }, colonization: { // Colonizing new rooms From cd4aa853a17cde71a7b3725fad6610019a57109b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 14 May 2019 01:20:46 -0700 Subject: [PATCH 047/137] Improve logging and nextdoor case --- src/overlords/mining/miner.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index 349cdb643..b3c4e4154 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -67,8 +67,6 @@ export class MiningOverlord extends Overlord { this.energyPerTick = SOURCE_ENERGY_NEUTRAL_CAPACITY / ENERGY_REGEN_TIME; } this.miningPowerNeeded = Math.ceil(this.energyPerTick / HARVEST_POWER) + 1; - let doubleMine = this.checkIfDoubleMine(); - // Decide operating mode if (Cartographer.roomType(this.pos.roomName) == ROOMTYPE_SOURCEKEEPER) { @@ -77,7 +75,7 @@ export class MiningOverlord extends Overlord { } else if (this.colony.room.energyCapacityAvailable < StandardMinerSetupCost) { this.mode = 'early'; this.setup = Setups.drones.miners.default; - } else if (doubleMine && this.colony.room.energyCapacityAvailable > DoubleMinerSetupCost) { + } else if (this.checkIfDoubleMine() && this.colony.room.energyCapacityAvailable > DoubleMinerSetupCost) { this.mode = 'double'; this.setup = Setups.drones.miners.double; } else if (this.link) { @@ -115,10 +113,13 @@ export class MiningOverlord extends Overlord { let nearby = this.source.pos.findInRange(FIND_SOURCES, 2).filter(source => this.source != source); if (nearby.length > 0) { this.secondSource = nearby[0]; - let miningPos = this.source.pos.getPositionAtDirection(this.source.pos.getDirectionTo(this.secondSource.pos)); - if (!miningPos.isWalkable()) { + // If its over 1 spot away, is there spot in between to mine? + if (!this.source.pos.isNearTo(this.secondSource)) { + let miningPos = this.source.pos.getPositionAtDirection(this.source.pos.getDirectionTo(this.secondSource.pos)); + if (!miningPos.isWalkable()) { console.log(`Double mining found but there is no spot between ${this.secondSource} ${this.secondSource.pos.print} isWalkable ${miningPos}`); - return false; + return false; + } } console.log(`Double mining found ${this.secondSource} ${this.secondSource.pos.print}`); if (this.source.id > this.secondSource.id) { From 7d4132488258d897d4803dd820fc6c0e13c0cd05 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 14 May 2019 11:12:14 -0700 Subject: [PATCH 048/137] Fixing some movement bugs, still need to properly flee --- src/overlords/offense/harass.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/overlords/offense/harass.ts b/src/overlords/offense/harass.ts index a20124a2a..bc59ccad6 100644 --- a/src/overlords/offense/harass.ts +++ b/src/overlords/offense/harass.ts @@ -38,19 +38,30 @@ export class HarassOverlord extends CombatOverlord { } private handleHarass(hydralisk: CombatZerg): void { - if (this.nextTarget && this.room.name != this.nextTarget) { + console.log(`Matt: hydralisk harassment in ${hydralisk.print}`); + //this.moveToNearbyRoom(hydralisk, hydralisk.room.name); + if (!this.nextTarget) { + this.moveToNearbyRoom(hydralisk, hydralisk.room.name); + } + if (this.nextTarget && this.directive.pos.roomName != this.nextTarget) { hydralisk.goToRoom(this.nextTarget); } else if (hydralisk.room.dangerousPlayerHostiles.length > 2) { // Time to move on this.moveToNearbyRoom(hydralisk, hydralisk.room.name); } - hydralisk.autoCombat(this.room.name); - // Clean up infra then move on to another room + hydralisk.autoCombat(this.nextTarget || hydralisk.room.name); + // Clean up construction sites then move on to another room } moveToNearbyRoom(hydralisk: CombatZerg, currentRoom: string) { this.nextTarget = _.sample(this.directive.memory.roomsToHarass); - hydralisk.goToRoom(this.nextTarget); + if (this.nextTarget) { + console.log(`Selecting new target of ${this.nextTarget} for ${hydralisk.print}`); + hydralisk.say(`Tgt ${this.nextTarget}`); + hydralisk.goToRoom(this.nextTarget); + } else { + console.log(`Tried to select new harass target from ${currentRoom} but failed for ${this.directive.print} with list ${this.directive.memory.roomsToHarass}`); + } } init() { @@ -60,7 +71,7 @@ export class HarassOverlord extends CombatOverlord { } run() { - console.log(`Matt: Running directive harass in ${this.room.print}`); + console.log(`Matt: Running directive harass in ${this.directive.print}`); this.autoRun(this.hydralisks, hydralisk => this.handleHarass(hydralisk)); } } From e7d5dc59f8e44803608ff51addc2ec7170ff68f9 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 14 May 2019 12:54:26 -0700 Subject: [PATCH 049/137] More stuff --- src/overlords/offense/harass.ts | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/overlords/offense/harass.ts b/src/overlords/offense/harass.ts index bc59ccad6..13a7250c2 100644 --- a/src/overlords/offense/harass.ts +++ b/src/overlords/offense/harass.ts @@ -17,7 +17,7 @@ export class HarassOverlord extends CombatOverlord { hydralisks: CombatZerg[]; room: Room; - nextTarget: string; + targetRemoteToHarass: string; directive: DirectiveHarass; @@ -39,26 +39,27 @@ export class HarassOverlord extends CombatOverlord { private handleHarass(hydralisk: CombatZerg): void { console.log(`Matt: hydralisk harassment in ${hydralisk.print}`); - //this.moveToNearbyRoom(hydralisk, hydralisk.room.name); - if (!this.nextTarget) { - this.moveToNearbyRoom(hydralisk, hydralisk.room.name); + hydralisk.autoCombat(this.targetRemoteToHarass || hydralisk.room.name); + + //this.chooseRemoteToHarass(hydralisk, hydralisk.room.name); + if (!this.targetRemoteToHarass) { + this.chooseRemoteToHarass(hydralisk, hydralisk.room.name); } - if (this.nextTarget && this.directive.pos.roomName != this.nextTarget) { - hydralisk.goToRoom(this.nextTarget); + if (this.targetRemoteToHarass && hydralisk.room.name != this.targetRemoteToHarass) { + hydralisk.goToRoom(this.targetRemoteToHarass); } else if (hydralisk.room.dangerousPlayerHostiles.length > 2) { // Time to move on - this.moveToNearbyRoom(hydralisk, hydralisk.room.name); + this.chooseRemoteToHarass(hydralisk, hydralisk.room.name); } - hydralisk.autoCombat(this.nextTarget || hydralisk.room.name); // Clean up construction sites then move on to another room } - moveToNearbyRoom(hydralisk: CombatZerg, currentRoom: string) { - this.nextTarget = _.sample(this.directive.memory.roomsToHarass); - if (this.nextTarget) { - console.log(`Selecting new target of ${this.nextTarget} for ${hydralisk.print}`); - hydralisk.say(`Tgt ${this.nextTarget}`); - hydralisk.goToRoom(this.nextTarget); + chooseRemoteToHarass(hydralisk: CombatZerg, currentRoom: string) { + this.targetRemoteToHarass = _.sample(this.directive.memory.roomsToHarass); + if (this.targetRemoteToHarass) { + console.log(`Selecting new target of ${this.targetRemoteToHarass} for ${hydralisk.print}`); + hydralisk.say(`Tgt ${this.targetRemoteToHarass}`); + hydralisk.goToRoom(this.targetRemoteToHarass); } else { console.log(`Tried to select new harass target from ${currentRoom} but failed for ${this.directive.print} with list ${this.directive.memory.roomsToHarass}`); } From cb3ddf5924515a54b8b06005d540645019cdb7cf Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 14 May 2019 17:27:11 -0700 Subject: [PATCH 050/137] Maybe fixing a "could not access room" error? --- src/directives/resource/powerMine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index f3fa5bee2..0b49f664f 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -60,7 +60,7 @@ export class DirectivePowerMine extends Directive { } get powerBank(): StructurePowerBank | undefined { - this._powerBank = this._powerBank || this.room != undefined ? this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank : undefined; + this._powerBank = this._powerBank || this.room ? this.flag.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank : undefined; return this._powerBank; } From 0eba255dc6bc016d6de22a23d7ee7a5c1db1a464 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 11:11:41 -0700 Subject: [PATCH 051/137] Fixing up logging --- src/directives/resource/powerMine.ts | 9 +++------ src/overlords/powerMining/PowerDrill.ts | 9 ++++----- src/overlords/powerMining/PowerHauler.ts | 6 +++--- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 0b49f664f..0c8fca34e 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -36,7 +36,7 @@ export class DirectivePowerMine extends Directive { } spawnMoarOverlords() { - if (!this.miningDone) { + if (!this.miningDone && this.powerBank) { this.overlords.powerMine = new PowerDrillOverlord(this); } if (!this.pickupDone) { @@ -74,7 +74,6 @@ export class DirectivePowerMine extends Directive { if (this.pos.isVisible) { this.memory.totalResources = this.powerBank ? this.powerBank.power : this.memory.totalResources; // update total amount remaining } - console.log("Directive total resources = " + this.totalResources); return this.memory.totalResources; } @@ -92,7 +91,6 @@ export class DirectivePowerMine extends Directive { let attackStrength: number = tally.attack * ATTACK_POWER || 0; // PB have 50% hitback, avg damage is attack strength if its enough healing, otherwise healing let avgDamagePerTick = Math.min(attackStrength, healStrength*2); - console.log("Calculating PB remaining lifespan: " + this.powerBank.hits / avgDamagePerTick); return this.powerBank.hits / avgDamagePerTick; } } @@ -101,14 +99,14 @@ export class DirectivePowerMine extends Directive { log.info("Checking spawning haulers"); // Begin checking for spawn haulers at 666 estimated ticks before PB destruction if (this.haulDirectiveCreated || this.room && (!this.powerBank || this.powerBank.hits < 500000)) { - Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); + log.debug('Activating spawning haulers for power mining in room ' + this.pos.roomName); this.haulDirectiveCreated = true; this.overlords.powerHaul = new PowerHaulingOverlord(this); } } setMiningDone(name: string) { - Game.notify("Setting mining done and removing overlord for power mine in room " + this.room + " at time " + Game.time); + log.debug("Setting mining done and removing overlord for power mine in room " + this.room + " at time " + Game.time); delete this.overlords[name]; this.miningDone = true; this._powerBank = undefined; @@ -120,7 +118,6 @@ export class DirectivePowerMine extends Directive { isPickupDone(): boolean { if (!this.pickupDone && this.miningDone && this.room && this.pos.isVisible && !this.hasDrops) { this.pickupDone = true; - Game.notify(`Hauling is done for ${this.room.print} at time ${Game.time}`); } return this.pickupDone; } diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 6731fc6b4..5249ca4ff 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -78,19 +78,18 @@ export class PowerDrillOverlord extends CombatOverlord { if (this.directive.powerBank == undefined && !this.directive.haulDirectiveCreated) { if (this.pos.lookFor(LOOK_RESOURCES).length == 0) { // Well shit, we didn't finish mining - Game.notify(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING CREEP at time: ${Game.time}`); + log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING CREEP at time: ${Game.time}`); this.directive.remove(); return; } - Game.notify("Power bank in " + this.room + " is dead."); + Game.notify(`Power bank in ${this.room.print} is dead.`); drill.say('💀 RIP 💀'); this.directive.setMiningDone(this.name); let result = drill.retire(); if (result == ERR_BUSY) { drill.spawning } - //this.directive.remove(); - Game.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString() + " result: " + result); + log.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString() + " result: " + result); return; } } @@ -99,7 +98,7 @@ export class PowerDrillOverlord extends CombatOverlord { // Go to power room if (!this.room || drill.room != this.room || drill.pos.isEdge || !this.directive.powerBank) { // log.debugCreep(drill, `Going to room!`); - Game.notify("Drill is moving to power site in " + this.pos.roomName + "."); + log.notify("Drill is moving to power site in " + this.pos.roomName + "."); drill.goTo(this.pos); return; } diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index d056193df..14c554d28 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -127,13 +127,13 @@ export class PowerHaulingOverlord extends Overlord { } // Check hauling is done !this.directive.pickupDone && this.directive.isPickupDone(); - if (this.directive.pickupDone) { + if (this.directive.pickupDone && Game.time % 16 == 0) { let stillCarryingPower = _.find(this.haulers, hauler => hauler.carry.power != undefined && hauler.carry.power > 0); if (!stillCarryingPower) { - Game.notify('Deleting Power Mining Directive as no haulers are left carrying ' + this.pos.print); + log.alert(`Deleting Power Mining Directive ${this.directive.print} as no haulers are left carrying power.`); this.directive.remove(); } else { - console.log(`Still carrying power back with ${stillCarryingPower.print}`); + log.debug(`Still carrying power back with ${stillCarryingPower.print} for ${this.directive.print}`); } } for (let hauler of this.haulers) { From 63d7a974c61381bfdad20b7e313a1323bac19106 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 11:34:17 -0700 Subject: [PATCH 052/137] Small hauler improvement --- src/directives/resource/haul.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 3caab481b..12fbd9980 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -21,7 +21,7 @@ export class DirectiveHaul extends Directive { private _store: StoreDefinition; private _drops: { [resourceType: string]: Resource[] }; - private finishAtTime: number; + private _finishAtTime: number; memory: DirectiveHaulMemory; @@ -109,9 +109,9 @@ export class DirectiveHaul extends Directive { run(): void { if (_.sum(this.store) == 0 && this.pos.isVisible) { // If everything is picked up, crudely give enough time to bring it back - this.finishAtTime = Game.time + 800; + this._finishAtTime = this._finishAtTime || (Game.time + 300); } - if (Game.time >= this.finishAtTime) { + if (Game.time >= this._finishAtTime) { this.remove(); } } From 6a361ea0f63a0971e5e5f9c32086abab1b0bff74 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 14:37:15 -0700 Subject: [PATCH 053/137] Invasion improvements --- src/directives/defense/invasionDefense.ts | 11 +++++++---- src/overlords/defense/rangedDefense.ts | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 1d6cab663..218e91dee 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -25,6 +25,9 @@ export class DirectiveInvasionDefense extends Directive { memory: DirectiveInvasionDefenseMemory; room: Room | undefined; + safeEndTime: 300; + safeSpawnHaltTime: 100; + private relocateFrequency: number; constructor(flag: Flag) { @@ -32,12 +35,12 @@ export class DirectiveInvasionDefense extends Directive { } spawnMoarOverlords() { - if (!this.room) { return; } - const expectedDamage = CombatIntel.maxDamageByCreeps(this.room.dangerousHostiles); - const useBoosts = (expectedDamage > ATTACK_POWER * 75) + const expectedDamage = CombatIntel.maxDamageByCreeps(this.room.dangerousPlayerHostiles); + const expectedHealing = CombatIntel.maxHealingByCreeps(this.room.dangerousPlayerHostiles); + const useBoosts = (expectedDamage > ATTACK_POWER * 13) || (expectedHealing > RANGED_ATTACK_POWER * 50) && !!this.colony.terminal && !!this.colony.evolutionChamber; const percentWalls = _.filter(this.room.barriers, s => s.structureType == STRUCTURE_WALL).length / @@ -64,7 +67,7 @@ export class DirectiveInvasionDefense extends Directive { } // If there are no hostiles left in the room and everyone's healed, then remove the flag if (this.room && this.room.hostiles.length == 0 && - Game.time - this.memory.safeSince > 100 && this.room.hostileStructures.length == 0) { + (Game.time - this.memory.safeSince) > this.safeEndTime && this.room.hostileStructures.length == 0) { if (_.filter(this.room.creeps, creep => creep.hits < creep.hitsMax).length == 0) { this.remove(); } diff --git a/src/overlords/defense/rangedDefense.ts b/src/overlords/defense/rangedDefense.ts index c3f2f2e5f..1b6e2ac92 100644 --- a/src/overlords/defense/rangedDefense.ts +++ b/src/overlords/defense/rangedDefense.ts @@ -16,6 +16,7 @@ export class RangedDefenseOverlord extends CombatOverlord { hydralisks: CombatZerg[]; room: Room; + directive: DirectiveInvasionDefense; static settings = { retreatHitsPercent : 0.85, @@ -27,7 +28,7 @@ export class RangedDefenseOverlord extends CombatOverlord { priority = OverlordPriority.defense.rangedDefense) { super(directive, 'rangedDefense', priority, 1); this.hydralisks = this.combatZerg(Roles.ranged, { - boostWishlist: boosted ? [boostResources.ranged_attack[3], boostResources.heal[3], boostResources.move[3]] + boostWishlist: boosted ? [boostResources.tough[3], boostResources.ranged_attack[3], boostResources.heal[3], boostResources.move[3]] : undefined }); } From 219a0f8f00519d8e9699852c9daa51577ddf0aff Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 14:42:15 -0700 Subject: [PATCH 054/137] Small diff --- src/directives/defense/invasionDefense.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 218e91dee..9c07b40c6 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -53,7 +53,6 @@ export class DirectiveInvasionDefense extends Directive { } else { this.overlords.meleeDefense = new MeleeDefenseOverlord(this, useBoosts); } - } init(): void { From 5c0d68b11111d83e1e9efe02944fa24fc8d0b71e Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 15:01:19 -0700 Subject: [PATCH 055/137] fixing logging --- src/overlords/mining/miner.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index b3c4e4154..f8cf83afa 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -68,6 +68,8 @@ export class MiningOverlord extends Overlord { } this.miningPowerNeeded = Math.ceil(this.energyPerTick / HARVEST_POWER) + 1; + //this.checkForNearbyMines(); + // Decide operating mode if (Cartographer.roomType(this.pos.roomName) == ROOMTYPE_SOURCEKEEPER) { this.mode = 'SK'; @@ -132,6 +134,21 @@ export class MiningOverlord extends Overlord { return false; } + checkForNearbyMines() { + if (Game.rooms[this.pos.roomName]) { + //console.log('Double mining check'); + this.source = _.first(this.pos.lookFor(LOOK_SOURCES)); + let nearbySources = this.source.pos.findInRange(FIND_SOURCES, 8) + .filter(source => { + //if (source != this.source) {console.log(`Source path length is ${source.pos.findPathTo(this.source!).length} in ${source.room.print}`)} + return this.source != source && source.pos.findPathTo(this.source!).length < 8; + }); + if (nearbySources.length > 0) { + //console.log(`Nearby sources are ${nearbySources} in ${this.room!.print}`); + } + } + } + /** * TODO Note to self - make directive to make a double miner */ @@ -377,11 +394,9 @@ export class MiningOverlord extends Overlord { * Actions for handling double mining */ private doubleMiningActions(miner: Zerg) { - // Approach mining site - if (this.goToMiningSite(miner)) return; - console.log(`Double mining with ${miner.print} here ${this.source!.pos.print}, ${this.source}, ${this.secondSource} is disabled ${this.isDisabled}`); + //console.log(`Double mining with ${miner.print} here ${this.source!.pos.print}, ${this.source}, ${this.secondSource} is disabled ${this.isDisabled}`); // Link mining if (this.link) { @@ -407,7 +422,6 @@ export class MiningOverlord extends Overlord { } else if (this.source && this.source.energy > 0) { return miner.harvest(this.source!); } else { - console.log('mining second source'); return miner.harvest(this.secondSource!); } } From fbdc03af3614d9db2cd0ad40007ac53aa38a81bf Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 15:03:14 -0700 Subject: [PATCH 056/137] Adjusting hydro boosted body to output more damage and have more healing --- src/creepSetups/setups.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 1c57dcbee..5199e486d 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -235,8 +235,8 @@ export const CombatSetups = { }), boosted_T3: new CreepSetup(Roles.ranged, { - pattern : [TOUGH, TOUGH, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, - MOVE, MOVE, HEAL], + pattern : [TOUGH, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, + MOVE, MOVE, HEAL, HEAL], sizeLimit: Infinity, }), From e26c52bfba8f25fa8e1d0162e6af98dea7dcb608 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 15:07:42 -0700 Subject: [PATCH 057/137] Swapping to the obsc version --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 46224df62..569ab0250 100644 --- a/src/main.ts +++ b/src/main.ts @@ -36,7 +36,7 @@ import {Mem} from './memory/Memory'; import {OvermindConsole} from './console/Console'; import {Stats} from './stats/stats'; import profiler from './profiler/screeps-profiler'; -import _Overmind from './Overmind_ts'; // this should be './Overmind_obfuscated' unless you are me +import _Overmind from './Overmind_obfuscated'; // this should be './Overmind_obfuscated' unless you are me import {VersionMigration} from './versionMigration/migrator'; import {RemoteDebugger} from './debug/remoteDebugger'; import {ActionParser} from './reinforcementLearning/actionParser'; From 11ab43eb82f95530feee8833185fc6f8a5ed3a15 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 15:08:11 -0700 Subject: [PATCH 058/137] Improving harass logging --- src/overlords/offense/harass.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/overlords/offense/harass.ts b/src/overlords/offense/harass.ts index 13a7250c2..448f5990e 100644 --- a/src/overlords/offense/harass.ts +++ b/src/overlords/offense/harass.ts @@ -8,6 +8,7 @@ import {boostResources} from '../../resources/map_resources'; import {CombatZerg} from '../../zerg/CombatZerg'; import {CombatOverlord} from '../CombatOverlord'; import {DirectiveHarass} from "../../directives/offense/harass"; +import {log} from "../../console/log"; /** * Spawns ranged harassers to stop mining for an enemy room @@ -38,7 +39,6 @@ export class HarassOverlord extends CombatOverlord { } private handleHarass(hydralisk: CombatZerg): void { - console.log(`Matt: hydralisk harassment in ${hydralisk.print}`); hydralisk.autoCombat(this.targetRemoteToHarass || hydralisk.room.name); //this.chooseRemoteToHarass(hydralisk, hydralisk.room.name); @@ -55,13 +55,13 @@ export class HarassOverlord extends CombatOverlord { } chooseRemoteToHarass(hydralisk: CombatZerg, currentRoom: string) { - this.targetRemoteToHarass = _.sample(this.directive.memory.roomsToHarass); - if (this.targetRemoteToHarass) { - console.log(`Selecting new target of ${this.targetRemoteToHarass} for ${hydralisk.print}`); + let nextRoom = this.directive.memory.roomsToHarass.shift(); + if (nextRoom) { + this.directive.memory.roomsToHarass.push(nextRoom); + this.targetRemoteToHarass = nextRoom; + log.debug(`Selecting new target of ${this.targetRemoteToHarass} for ${hydralisk.print} from ${this.directive.memory.roomsToHarass}`); hydralisk.say(`Tgt ${this.targetRemoteToHarass}`); hydralisk.goToRoom(this.targetRemoteToHarass); - } else { - console.log(`Tried to select new harass target from ${currentRoom} but failed for ${this.directive.print} with list ${this.directive.memory.roomsToHarass}`); } } @@ -72,7 +72,6 @@ export class HarassOverlord extends CombatOverlord { } run() { - console.log(`Matt: Running directive harass in ${this.directive.print}`); this.autoRun(this.hydralisks, hydralisk => this.handleHarass(hydralisk)); } } From 9d17bbc5cef99b330af8d76930683a9aa88bc60c Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 15 May 2019 15:18:48 -0700 Subject: [PATCH 059/137] Fixing harass NPE --- src/directives/offense/harass.ts | 7 +++++++ src/overlords/offense/harass.ts | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/directives/offense/harass.ts b/src/directives/offense/harass.ts index 44366079e..4fc6732b2 100644 --- a/src/directives/offense/harass.ts +++ b/src/directives/offense/harass.ts @@ -48,6 +48,13 @@ export class DirectiveHarass extends Directive { } + findNearbyReservedRoomsForHarassment() { + if (this.memory.targetPlayer) { + return this.findNearbyReservedRooms(this.flag.pos.roomName, this.memory.targetPlayer); + } + return []; + } + /** * Finds the rooms to harass * diff --git a/src/overlords/offense/harass.ts b/src/overlords/offense/harass.ts index 448f5990e..8fb8fc87d 100644 --- a/src/overlords/offense/harass.ts +++ b/src/overlords/offense/harass.ts @@ -55,6 +55,9 @@ export class HarassOverlord extends CombatOverlord { } chooseRemoteToHarass(hydralisk: CombatZerg, currentRoom: string) { + if (!this.directive.memory.roomsToHarass || this.directive.memory.roomsToHarass.length == 0) { + this.directive.memory.roomsToHarass = this.directive.findNearbyReservedRoomsForHarassment(); + } let nextRoom = this.directive.memory.roomsToHarass.shift(); if (nextRoom) { this.directive.memory.roomsToHarass.push(nextRoom); From a1cd462373190539b5482410cbaff8df6e2c396b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 20 May 2019 11:05:30 -0700 Subject: [PATCH 060/137] Removing logging message --- src/overlords/mining/miner.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index f8cf83afa..048da4fff 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -403,7 +403,6 @@ export class MiningOverlord extends Overlord { if (this.source && this.source.energy > 0) { miner.harvest(this.source!); } else { - console.log('mining second source'); miner.harvest(this.secondSource!); } if (miner.carry.energy > 0.9 * miner.carryCapacity) { From 712e61551284e0463aa92191ce4fb4b9219e4efa Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 20 May 2019 15:46:52 -0700 Subject: [PATCH 061/137] Currently directive removal isn't occurring properly, seeing if this helps --- src/overlords/powerMining/PowerHauler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 14c554d28..1a04d4131 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -126,8 +126,7 @@ export class PowerHaulingOverlord extends Overlord { this.wishlist(this.numHaulers, Setups.transporters.default); } // Check hauling is done - !this.directive.pickupDone && this.directive.isPickupDone(); - if (this.directive.pickupDone && Game.time % 16 == 0) { + if (this.directive.isPickupDone() && Game.time % 16 == 0) { let stillCarryingPower = _.find(this.haulers, hauler => hauler.carry.power != undefined && hauler.carry.power > 0); if (!stillCarryingPower) { log.alert(`Deleting Power Mining Directive ${this.directive.print} as no haulers are left carrying power.`); From 9f6274c7b628ed1808ce9cd4c20cbb5c7528e0e3 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 20 May 2019 16:06:16 -0700 Subject: [PATCH 062/137] Prevent workers from trying to upgrade blocked controllers and added notes --- src/overlords/core/worker.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index 95f85fff3..a8924d692 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -334,8 +334,12 @@ export class WorkerOverlord extends Overlord { private handleWorker(worker: Zerg) { if (worker.carry.energy > 0) { - // Upgrade controller if close to downgrade - if (this.colony.controller.ticksToDowngrade <= (this.colony.level >= 4 ? 10000 : 2000)) { + + // TODO Add high priority to block controller with ramparts/walls in case of downgrade attack + // FIXME workers get stalled at controller in case of downgrade attack + // Upgrade controller if close to downgrade or prepare to upgrade if getting controller attacked + const downgradeLevel = CONTROLLER_DOWNGRADE[this.colony.controller.level] * .7; + if (this.colony.controller.ticksToDowngrade <= downgradeLevel && !this.colony.controller.upgradeBlocked || this.colony.controller.upgradeBlocked < 30) { if (this.upgradeActions(worker)) return; } // Repair damaged non-road non-barrier structures From 816b1a38e40a4f57c9386a03be55efb64cd8aee2 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 20 May 2019 17:58:50 -0700 Subject: [PATCH 063/137] Adding controller saving to pioneer --- src/overlords/colonization/pioneer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overlords/colonization/pioneer.ts b/src/overlords/colonization/pioneer.ts index 90c746ecd..37ab764e2 100644 --- a/src/overlords/colonization/pioneer.ts +++ b/src/overlords/colonization/pioneer.ts @@ -62,7 +62,7 @@ export class PioneerOverlord extends Overlord { if (pioneer.carry.energy == 0) { pioneer.task = Tasks.recharge(); } else if (this.room && this.room.controller && - (this.room.controller.ticksToDowngrade < 2500 || !this.spawnSite) && + (this.room.controller.ticksToDowngrade < (0.1 * CONTROLLER_DOWNGRADE[this.colony.controller.level]) || !this.spawnSite) && !(this.room.controller.upgradeBlocked > 0)) { // Save controller if it's about to downgrade or if you have nothing else to do pioneer.task = Tasks.upgrade(this.room.controller); From 5812be31a419153e9d44f843b6fad444d9672603 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 21 May 2019 09:35:39 -0700 Subject: [PATCH 064/137] Removing mineral mining in rooms without terminal --- src/directives/resource/extract.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/directives/resource/extract.ts b/src/directives/resource/extract.ts index 7a75ba02a..989c3e58a 100644 --- a/src/directives/resource/extract.ts +++ b/src/directives/resource/extract.ts @@ -47,6 +47,9 @@ export class DirectiveExtract extends Directive { if (this.colony.level < 6) { log.notify(`Removing extraction directive in ${this.pos.roomName}: room RCL insufficient.`); this.remove(); + } else if (!this.colony.terminal) { + log.notify(`Removing extraction directive in ${this.pos.roomName}: room is missing terminal.`); + this.remove(); } } From f3a5ac8a8f8f982b55f6c7448792b18ae2cfb198 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 21 May 2019 11:34:23 -0700 Subject: [PATCH 065/137] Removing bad imports --- src/directives/colony/clearRoom.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 2e7de2e52..318dad32a 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -4,10 +4,8 @@ import {ClaimingOverlord} from '../../overlords/colonization/claimer'; import {MY_USERNAME} from '../../~settings'; import {log} from '../../console/log'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; -import {hasContents, printRoomName} from '../../utilities/utils'; +import {printRoomName} from '../../utilities/utils'; import {DirectiveHaul} from "../resource/haul"; -import {WorkerOverlord} from "../../overlords/core/worker"; -import {Zerg} from "../../zerg/Zerg"; import {Pathing} from "../../movement/Pathing"; import {DirectiveDismantle} from "../targeting/dismantle"; From d4dbb74e2518fb75960ea00d99b2bd0e6660be9b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 21 May 2019 11:43:48 -0700 Subject: [PATCH 066/137] Removed noisy logging --- src/overlords/mining/miner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index 048da4fff..2af7336a3 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -119,13 +119,13 @@ export class MiningOverlord extends Overlord { if (!this.source.pos.isNearTo(this.secondSource)) { let miningPos = this.source.pos.getPositionAtDirection(this.source.pos.getDirectionTo(this.secondSource.pos)); if (!miningPos.isWalkable()) { - console.log(`Double mining found but there is no spot between ${this.secondSource} ${this.secondSource.pos.print} isWalkable ${miningPos}`); + //console.log(`Double mining found but there is no spot between ${this.secondSource} ${this.secondSource.pos.print} isWalkable ${miningPos}`); return false; } } console.log(`Double mining found ${this.secondSource} ${this.secondSource.pos.print}`); if (this.source.id > this.secondSource.id) { - console.log(`This is a disabled mining ${this.directive.name} via source id`); + //console.log(`This is a disabled mining ${this.directive.name} via source id`); this.isDisabled = true; } return true; From 9a1a3347f951a8ceef6d1d89a509e64cf4770c1c Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 21 May 2019 18:26:12 -0700 Subject: [PATCH 067/137] Updating power mining to use a state instead of the gibberish from before --- src/directives/resource/powerMine.ts | 80 ++++++++++++++---------- src/overlords/powerMining/PowerDrill.ts | 18 ++---- src/overlords/powerMining/PowerHauler.ts | 29 ++++----- 3 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 0c8fca34e..cae523620 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -9,6 +9,14 @@ import {log} from "../../console/log"; interface DirectivePowerMineMemory extends FlagMemory { totalResources?: number; + /* + 0: init + 1: mining started + 2: mining near done, hauling started + 3: mining done + 4: hauling picking is complete + */ + state: number; } @@ -22,9 +30,6 @@ export class DirectivePowerMine extends Directive { static color = COLOR_YELLOW; static secondaryColor = COLOR_RED; - miningDone: boolean; - pickupDone: boolean; - haulDirectiveCreated: boolean; private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; @@ -33,14 +38,15 @@ export class DirectivePowerMine extends Directive { constructor(flag: Flag) { super(flag); this._powerBank = this.room != undefined ? this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank : undefined; + this.memory.state = 0; } spawnMoarOverlords() { - if (!this.miningDone && this.powerBank) { + if (this.memory.state < 3 && this.powerBank) { this.overlords.powerMine = new PowerDrillOverlord(this); } - if (!this.pickupDone) { - this.spawnHaulers(); + if (this.memory.state > 1) { + this.overlords.powerHaul = new PowerHaulingOverlord(this); } } @@ -81,10 +87,7 @@ export class DirectivePowerMine extends Directive { if (!this.room) { return undefined; } else if (this.powerBank == undefined) { - if (this.miningDone) { - // Power Bank is gone - return 0; - } + return 0; } else { let tally = calculateFormationStrength(this.powerBank.pos.findInRange(FIND_MY_CREEPS, 4)); let healStrength: number = tally.heal * HEAL_POWER || 0; @@ -95,31 +98,39 @@ export class DirectivePowerMine extends Directive { } } - spawnHaulers() { - log.info("Checking spawning haulers"); - // Begin checking for spawn haulers at 666 estimated ticks before PB destruction - if (this.haulDirectiveCreated || this.room && (!this.powerBank || this.powerBank.hits < 500000)) { - log.debug('Activating spawning haulers for power mining in room ' + this.pos.roomName); - this.haulDirectiveCreated = true; - this.overlords.powerHaul = new PowerHaulingOverlord(this); - } - } - - setMiningDone(name: string) { - log.debug("Setting mining done and removing overlord for power mine in room " + this.room + " at time " + Game.time); - delete this.overlords[name]; - this.miningDone = true; - this._powerBank = undefined; - } - /** - * This states when all the power has been picked up. Once all power has been picked up and delivered remove the directive - */ - isPickupDone(): boolean { - if (!this.pickupDone && this.miningDone && this.room && this.pos.isVisible && !this.hasDrops) { - this.pickupDone = true; + manageState() { + let currentState = this.memory.state; + if (currentState == 0 && this.powerBank && this.powerBank.hits < this.powerBank.hitsMax) { + if (this.powerBank.pos.findInRange(FIND_MY_CREEPS, 3).length == 0) { + // Power bank is damage but we didn't mine it + log.alert(`Power bank mining ${this.print} failed as someone else is mining this location`); + this.remove(); + } else { + // Set to mining started + this.memory.state = 1; + } + } else if (currentState == 1 && this.room && (!this.powerBank || this.powerBank.hits < 500000)) { + log.info('Activating spawning haulers for power mining in room ' + this.pos.roomName); + this.memory.state = 2; + } else if (currentState == 1 || currentState == 2 && this.room && !this.powerBank && !this.hasDrops) { + log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING CREEP at time: ${Game.time}`); + this.remove(); + } else if (currentState == 2 && this.room && (!this.powerBank)) { + log.alert(`Mining is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); + this.memory.state = 3; + // TODO reassign them to guard the bank + delete this.overlords["powerMine"]; + this._powerBank = undefined; // This might be fluff + } else if (currentState == 3 && this.room && !this.hasDrops) { + // Hauler pickup is now complete + log.alert(`Hauler pickup is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); + this.memory.state = 4; + // TODO Stop spawning haulers + } else if (currentState == 4 && this.overlords.powerHaul && (this.overlords.powerHaul as PowerHaulingOverlord).checkIfStillCarryingPower() == undefined) { + log.alert(`Hauling complete for ${this.print} at time ${Game.time}`); + this.remove(); } - return this.pickupDone; } init(): void { @@ -127,6 +138,9 @@ export class DirectivePowerMine extends Directive { } run(): void { + if (Game.time % 5 == 0) { + this.manageState(); + } } } diff --git a/src/overlords/powerMining/PowerDrill.ts b/src/overlords/powerMining/PowerDrill.ts index 5249ca4ff..34fc41c79 100644 --- a/src/overlords/powerMining/PowerDrill.ts +++ b/src/overlords/powerMining/PowerDrill.ts @@ -31,7 +31,6 @@ export class PowerDrillOverlord extends CombatOverlord { directive: DirectivePowerMine; memory: PowerDrillOverlordMemory; partnerMap: Map; - isDone: boolean; drills: CombatZerg[]; coolant: CombatZerg[]; @@ -75,19 +74,12 @@ export class PowerDrillOverlord extends CombatOverlord { // We are not there yet } else { // If power bank is dead - if (this.directive.powerBank == undefined && !this.directive.haulDirectiveCreated) { - if (this.pos.lookFor(LOOK_RESOURCES).length == 0) { - // Well shit, we didn't finish mining - log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING CREEP at time: ${Game.time}`); - this.directive.remove(); - return; - } + if (this.directive.powerBank == undefined && this.directive.memory.state < 2) { Game.notify(`Power bank in ${this.room.print} is dead.`); drill.say('💀 RIP 💀'); - this.directive.setMiningDone(this.name); let result = drill.retire(); if (result == ERR_BUSY) { - drill.spawning + // drill spawning, find something else to do with them } log.notify("FINISHED POWER MINING IN " + this.room + " DELETING CREEP at time: " + Game.time.toString() + " result: " + result); return; @@ -130,7 +122,6 @@ export class PowerDrillOverlord extends CombatOverlord { // If power bank is dead Game.notify("Power bank in " + this.room + " is dead."); coolant.say('💀 RIP 💀'); - this.isDone = true; coolant.retire(); return; } @@ -204,11 +195,10 @@ export class PowerDrillOverlord extends CombatOverlord { run() { this.autoRun(this.drills, drill => this.handleDrill(drill)); this.autoRun(this.coolant, coolant => this.handleCoolant(coolant)); - if (this.isDone && !this.directive.miningDone) { + if (this.directive.memory.state >= 3) { + Game.notify("DELETING ALL POWER MINING CREEPS BECAUSE STATE IS >= 3 in " + this.directive.print); this.drills.forEach(drill => drill.retire()); this.coolant.forEach(coolant => coolant.retire()); - this.directive.setMiningDone(this.name); - delete this.directive.overlords[this.name]; } } diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 1a04d4131..a91b30c9c 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -22,7 +22,7 @@ export class PowerHaulingOverlord extends Overlord { requiredRCL = 6; // Allow time for body to spawn - prespawnAmount = 250; + prespawnAmount = 350; constructor(directive: DirectivePowerMine, priority = OverlordPriority.collectionUrgent.haul) { super(directive, 'powerHaul', priority); @@ -33,7 +33,7 @@ export class PowerHaulingOverlord extends Overlord { // Calculate amount of hauling each hauler provides in a lifetime let haulerCarryParts = Setups.transporters.default.getBodyPotential(CARRY, this.colony); // Calculate number of haulers - this.numHaulers = Math.round(haulingPartsNeeded/haulerCarryParts); + this.numHaulers = Math.ceil(haulingPartsNeeded/haulerCarryParts); // setup time to request the haulers this.tickToSpawnOn = Game.time + (this.directive.calculateRemainingLifespan() || 0) - this.prespawnAmount; } @@ -45,16 +45,15 @@ export class PowerHaulingOverlord extends Overlord { } protected handleHauler(hauler: Zerg) { - if (_.sum(hauler.carry) == 0 && this.directive.pickupDone) { - hauler.retire(); - } else if (_.sum(hauler.carry) == 0) { - // Travel to directive and collect resources - if (this.directive.pickupDone) { + if (_.sum(hauler.carry) == 0) { + if (this.directive.memory.state >= 4) { + // FIXME: Maybe ditch this and put it as a separate on-finishing method to reassign hauler.say('💀 RIP 💀',true); log.warning(`${hauler.name} is committing suicide as directive is done!`); this.numHaulers = 0; hauler.retire(); } + // Travel to directive and collect resources if (hauler.inSameRoomAs(this.directive)) { // Pick up drops first if (this.directive.hasDrops) { @@ -120,21 +119,15 @@ export class PowerHaulingOverlord extends Overlord { } } + checkIfStillCarryingPower() { + return _.find(this.haulers, hauler => hauler.carry.power != undefined && hauler.carry.power > 0); + } + run() { - if (Game.time >= this.tickToSpawnOn && !this.directive.pickupDone) { + if (Game.time >= this.tickToSpawnOn && this.directive.memory.state < 4) { Game.notify('Time to spawn haulers ' + this.pos.roomName); this.wishlist(this.numHaulers, Setups.transporters.default); } - // Check hauling is done - if (this.directive.isPickupDone() && Game.time % 16 == 0) { - let stillCarryingPower = _.find(this.haulers, hauler => hauler.carry.power != undefined && hauler.carry.power > 0); - if (!stillCarryingPower) { - log.alert(`Deleting Power Mining Directive ${this.directive.print} as no haulers are left carrying power.`); - this.directive.remove(); - } else { - log.debug(`Still carrying power back with ${stillCarryingPower.print} for ${this.directive.print}`); - } - } for (let hauler of this.haulers) { if (hauler.isIdle) { this.handleHauler(hauler); From 4a146cce03f57827afd5e7b34498cb6710595518 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Tue, 21 May 2019 23:59:28 -0700 Subject: [PATCH 068/137] Fixing if statement --- src/directives/resource/powerMine.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index cae523620..a041736f3 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -113,7 +113,7 @@ export class DirectivePowerMine extends Directive { } else if (currentState == 1 && this.room && (!this.powerBank || this.powerBank.hits < 500000)) { log.info('Activating spawning haulers for power mining in room ' + this.pos.roomName); this.memory.state = 2; - } else if (currentState == 1 || currentState == 2 && this.room && !this.powerBank && !this.hasDrops) { + } else if ((currentState == 1 || currentState == 2) && this.room && !this.powerBank && !this.hasDrops) { log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING CREEP at time: ${Game.time}`); this.remove(); } else if (currentState == 2 && this.room && (!this.powerBank)) { @@ -130,6 +130,8 @@ export class DirectivePowerMine extends Directive { } else if (currentState == 4 && this.overlords.powerHaul && (this.overlords.powerHaul as PowerHaulingOverlord).checkIfStillCarryingPower() == undefined) { log.alert(`Hauling complete for ${this.print} at time ${Game.time}`); this.remove(); + } else { + log.debug(`Power mining ${this.print} is in state ${currentState}`); } } From 1f34e3293e45e23a9d89ac30645a37da4fa490a2 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 22 May 2019 10:28:59 -0700 Subject: [PATCH 069/137] Adding partial power directive, needs the powers first --- src/directives/initializer.ts | 8 + src/directives/powerCreeps/baseOperator.ts | 244 ++++++++++++++++++ .../powerCreeps/powers/generateOps.ts | 13 + .../powerCreeps/powers/genericPower.ts | 17 ++ 4 files changed, 282 insertions(+) create mode 100644 src/directives/powerCreeps/baseOperator.ts create mode 100644 src/directives/powerCreeps/powers/generateOps.ts create mode 100644 src/directives/powerCreeps/powers/genericPower.ts diff --git a/src/directives/initializer.ts b/src/directives/initializer.ts index 081a081bd..a63af3941 100644 --- a/src/directives/initializer.ts +++ b/src/directives/initializer.ts @@ -25,6 +25,7 @@ import {DirectiveTargetSiege} from './targeting/siegeTarget'; import {DirectiveTerminalEmergencyState} from './terminalState/terminalState_emergency'; import {DirectiveTerminalEvacuateState} from './terminalState/terminalState_evacuate'; import {DirectiveTerminalRebuildState} from './terminalState/terminalState_rebuild'; +import {DirectiveBaseOperator} from "./powerCreeps/baseOperator"; /** * This is the initializer for directives, which maps flags by their color code to the corresponding directive @@ -128,6 +129,13 @@ export function DirectiveWrapper(flag: Flag): Directive | undefined { return new DirectiveRPBunker(flag); } break; + // Power directives ==================================================================================== + case COLOR_CYAN: + switch (flag.secondaryColor) { + case COLOR_PURPLE: + return new DirectiveBaseOperator(flag); + } + break; } } diff --git a/src/directives/powerCreeps/baseOperator.ts b/src/directives/powerCreeps/baseOperator.ts new file mode 100644 index 000000000..63fb97bf4 --- /dev/null +++ b/src/directives/powerCreeps/baseOperator.ts @@ -0,0 +1,244 @@ +import {CombatPlanner, SiegeAnalysis} from "../../strategy/CombatPlanner"; +import {profile} from "../../profiler/decorator"; +import {Directive} from "../Directive"; +import {log} from "../../console/log"; +import {Visualizer} from "../../visuals/Visualizer"; + + +interface DirectiveBaseOperatorMemory extends FlagMemory { + powerPriorities: PowerConstant[] +} + +/** + * Simple directive to run a power creep where the flag name is the power creep name + */ +@profile +export class DirectiveBaseOperator extends Directive { + + static directiveName = 'BaseOperator'; + static color = COLOR_CYAN; + static secondaryColor = COLOR_PURPLE; + + memory: DirectiveBaseOperatorMemory; + + // Power Creep Hack + powerCreep: PowerCreep; + + defaultPowerPriorities: [ + PWR_GENERATE_OPS, + PWR_REGEN_SOURCE, + PWR_OPERATE_TOWER, + PWR_OPERATE_LAB, + PWR_OPERATE_SPAWN, + PWR_OPERATE_EXTENSION, + PWR_REGEN_MINERAL]; + + // overlords: { + // scout?: StationaryScoutOverlord; + // destroy?: SwarmDestroyerOverlord | PairDestroyerOverlord; + // guard?: OutpostDefenseOverlord; + // controllerAttack?: ControllerAttackerOverlord; + // }; + + constructor(flag: Flag) { + super(flag); + this.powerCreep = Game.powerCreeps[flag.name]; + if (!this.powerCreep) { + log.error(`Power Creep not found for ${this.print}, deleting directive`); + this.remove(); + } + this.memory.powerPriorities = this.defaultPowerPriorities; + } + + spawnMoarOverlords() { + } + + init(): void { + + } + + + // Wrapped powerCreep methods =========================================================================================== + + renew(powerSource: StructurePowerBank | StructurePowerSpawn) { + if (this.pos.inRangeToPos(powerSource.pos, 1)) { + return this.powerCreep.renew(powerSource); + } else { + return this.powerCreep.moveTo(powerSource); + } + } + + enablePower(controller: StructureController) { + if (this.pos.inRangeToPos(controller.pos, 1)) { + return this.powerCreep.enableRoom(controller); + } else { + return this.powerCreep.moveTo(controller); + } + } + + usePower(power: PowerConstant) { + switch(power) { + case PWR_GENERATE_OPS: this.generateOps(); + case PWR_OPERATE_SPAWN: this.operateSpawn(); + } + + } + + /** + * Generate 1/2/4/6/8 ops resource units. Cooldown 50 ticks. Required creep level: 0/2/7/14/22. + */ + generateOps() { + if (this.powerCreep.powers[PWR_GENERATE_OPS].cooldown !> 0) { + return this.powerCreep.usePower(PWR_GENERATE_OPS); + } + return ERR_TIRED; + } + + operateSpawn(spawn?: StructureSpawn) { + // if (this.powerCreep.powers[PWR_oper]) + // if (!spawn) { + // spawn = _.first(this.room!.spawns.filter(spawn => spawn.effects.length == 0)); + // if (!spawn) { + // return ERR; + // } + // } + if (this.pos.inRangeToPos(spawn.pos, 1)) { + return this.powerCreep.usePower(PWR_OPERATE_SPAWN, spawn); + } else { + return this.powerCreep.moveTo(spawn); + } + } + + operateTower(tower: StructureTower) { + if (this.pos.inRangeToPos(tower.pos, POWER_INFO[PWR_OPERATE_TOWER].range)) { + return this.powerCreep.usePower(PWR_OPERATE_TOWER, tower); + } else { + return this.powerCreep.moveTo(tower); + } + } + + operateStorage(storage: StructureStorage) { + if (this.pos.inRangeToPos(storage.pos, POWER_INFO[PWR_OPERATE_STORAGE].range)) { + return this.powerCreep.usePower(PWR_OPERATE_STORAGE, storage); + } else { + return this.powerCreep.moveTo(storage); + } + } + + operateExtensions(container: StructureStorage | StructureTerminal | StructureContainer) { + if (this.pos.inRangeToPos(container.pos, POWER_INFO[PWR_OPERATE_EXTENSION].range)) { + return this.powerCreep.usePower(PWR_OPERATE_EXTENSION, container); + } else { + return this.powerCreep.moveTo(container); + } + } + + operateObserver(observer: StructureObserver) { + if (this.pos.inRangeToPos(observer.pos, POWER_INFO[PWR_OPERATE_OBSERVER].range)) { + return this.powerCreep.usePower(PWR_OPERATE_OBSERVER, observer); + } else { + return this.powerCreep.moveTo(observer); + } + } + + operateTerminal(terminal: StructureTerminal) { + if (this.pos.inRangeToPos(terminal.pos, POWER_INFO[PWR_OPERATE_TERMINAL].range)) { + return this.powerCreep.usePower(PWR_OPERATE_TERMINAL, terminal); + } else { + return this.powerCreep.moveTo(terminal); + } + } + + operatePower(power: StructurePowerSpawn) { + if (this.pos.inRangeToPos(power.pos, POWER_INFO[PWR_OPERATE_POWER].range)) { + return this.powerCreep.usePower(PWR_OPERATE_POWER, power); + } else { + return this.powerCreep.moveTo(power); + } + } + + operateController(controller: StructureController) { + if (this.pos.inRangeToPos(controller.pos, POWER_INFO[PWR_OPERATE_CONTROLLER].range)) { + return this.powerCreep.usePower(PWR_OPERATE_CONTROLLER, controller); + } else { + return this.powerCreep.moveTo(controller); + } + } + + // operateFactory(factory: StructureFactory) { + // if (this.pos.inRangeToPos(factory.pos, POWER_INFO[PWR_OPERATE_FACTORY].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_FACTORY, factory); + // } else { + // return this.moveTo(factory); + // } + // } + + shield() { + if (this.powerCreep.powers[PWR_SHIELD].cooldown !> 0) { + return this.powerCreep.usePower(PWR_SHIELD); + } + return ERR_TIRED; + } + + regenSource(source : Source) { + if (this.pos.inRangeToPos(source.pos, POWER_INFO[PWR_REGEN_SOURCE].range)) { + return this.powerCreep.usePower(PWR_REGEN_SOURCE, source); + } else { + return this.powerCreep.moveTo(source); + } + } + + regenMineral(mineral: Mineral) { + if (this.pos.inRangeToPos(mineral.pos, POWER_INFO[PWR_REGEN_MINERAL].range)) { + return this.powerCreep.usePower(PWR_REGEN_MINERAL, mineral); + } else { + return this.powerCreep.moveTo(mineral); + } + } + + fortify(rampart: StructureRampart) { + if (this.pos.inRangeToPos(rampart.pos, POWER_INFO[PWR_FORTIFY].range)) { + return this.powerCreep.usePower(PWR_FORTIFY, rampart); + } else { + return this.powerCreep.moveTo(rampart); + } + } + + operateLab(lab: StructureLab) { + if (this.pos.inRangeToPos(lab.pos, POWER_INFO[PWR_OPERATE_LAB].range)) { + return this.powerCreep.usePower(PWR_OPERATE_LAB, lab); + } else { + return this.powerCreep.moveTo(lab); + } + } + + + runPowers() { + + } + + + + run(): void { + if (!this.room) { + return; + } else if (this.powerCreep.ticksToLive == undefined && this.powerCreep.spawnCooldownTime !> 0 && this.room && this.room.powerSpawn) { + // Spawn creep + this.powerCreep.spawn(this.room.powerSpawn); + } else if (this.room.controller && !this.room.controller.isPowerEnabled) { + // Enable power + this.enablePower(this.room.controller); + } else if (this.powerCreep && this.powerCreep.ticksToLive && this.powerCreep.ticksToLive < 300 && this.room.powerSpawn) { + this.renew(this.room.powerSpawn); + } else { + this.runPowers(); + } + + + } + + visuals(): void { + Visualizer.marker(this.pos, {color: 'red'}); + } + +} \ No newline at end of file diff --git a/src/directives/powerCreeps/powers/generateOps.ts b/src/directives/powerCreeps/powers/generateOps.ts new file mode 100644 index 000000000..f2af8df29 --- /dev/null +++ b/src/directives/powerCreeps/powers/generateOps.ts @@ -0,0 +1,13 @@ +import {profile} from "../../../profiler/decorator"; +import {Power} from "./genericPower"; + +export const powerId = PWR_GENERATE_OPS; + +/** + * An abstract class for encapsulating power creep power usage. + */ +@profile +export abstract class GenerateOps extends Power{ + + run() {} +} \ No newline at end of file diff --git a/src/directives/powerCreeps/powers/genericPower.ts b/src/directives/powerCreeps/powers/genericPower.ts new file mode 100644 index 000000000..10b7787a9 --- /dev/null +++ b/src/directives/powerCreeps/powers/genericPower.ts @@ -0,0 +1,17 @@ +import {profile} from "../../../profiler/decorator"; +import {powerId} from "./generateOps"; + +/** + * An abstract class for encapsulating power creep power usage. + */ +@profile +export abstract class Power { + static powerId: PowerConstant; + + canRunPower(pc: PowerCreep) { + const power = pc.powers[powerId]; + return power && power.level > 0 && power.cooldown == 0; + } + + run() {} +} \ No newline at end of file From 2b207b60a77bf9083de4f25eb8a0eadb470692ff Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 23 May 2019 13:17:53 -0700 Subject: [PATCH 070/137] More work done --- .../powerCreeps/powers/generateOps.ts | 19 +++++++- .../powerCreeps/powers/genericPower.ts | 46 +++++++++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/directives/powerCreeps/powers/generateOps.ts b/src/directives/powerCreeps/powers/generateOps.ts index f2af8df29..7879ab578 100644 --- a/src/directives/powerCreeps/powers/generateOps.ts +++ b/src/directives/powerCreeps/powers/generateOps.ts @@ -1,5 +1,6 @@ import {profile} from "../../../profiler/decorator"; import {Power} from "./genericPower"; +import {log} from "../../../console/log"; export const powerId = PWR_GENERATE_OPS; @@ -7,7 +8,21 @@ export const powerId = PWR_GENERATE_OPS; * An abstract class for encapsulating power creep power usage. */ @profile -export abstract class GenerateOps extends Power{ +export abstract class GenerateOps extends Power { - run() {} + operatePower() { + if (this.powerCreep.carry.ops && this.powerCreep.carry.ops > (this.powerCreep.carryCapacity * 0.9)) { + const storage = this.powerCreep.room!.storage; + if (!storage) { + log.error(`Ops power creep with no storage`); + } else { + this.powerCreep.moveTo(this.powerCreep.room!.storage!.pos); + this.powerCreep.transfer(storage, RESOURCE_OPS, this.powerCreep.carry.ops); + } + } else { + return this.powerCreep.usePower(powerId); + } + return ERR_TIRED; + + } } \ No newline at end of file diff --git a/src/directives/powerCreeps/powers/genericPower.ts b/src/directives/powerCreeps/powers/genericPower.ts index 10b7787a9..29ef4b76d 100644 --- a/src/directives/powerCreeps/powers/genericPower.ts +++ b/src/directives/powerCreeps/powers/genericPower.ts @@ -8,10 +8,50 @@ import {powerId} from "./generateOps"; export abstract class Power { static powerId: PowerConstant; - canRunPower(pc: PowerCreep) { - const power = pc.powers[powerId]; + _target: { // Data for the target the task is directed to: + ref: string; // Target id or name + _pos: ProtoPos; // Target position's coordinates in case vision is lost + }; + + + _powerCreep: { + name: string; + }; + + /** + * Dereferences the Task's target + */ + get target(): RoomObject | null { + return deref(this._target.ref); + } + + canRunPower() { + const power = this.powerCreep.powers[powerId]; return power && power.level > 0 && power.cooldown == 0; } - run() {} + /** + * Return the wrapped creep which is executing this task + */ + get powerCreep(): PowerCreep { // Get task's own creep by its name + // Returns zerg wrapper instead of creep to use monkey-patched functions + return Game.powerCreeps[this._powerCreep.name]; + } + + /** + * Set the creep which is executing this task + */ + set powerCreep(pc: PowerCreep) { + this._powerCreep.name = pc.name; + } + + run() { + if (this.canRunPower()) { + this.operatePower() + } + } + + operatePower() { + + } } \ No newline at end of file From 0269749fba71de9d0c703c52a5bcdd6dfac69223 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 07:43:34 -0700 Subject: [PATCH 071/137] Adding required rcl --- src/directives/resource/powerMine.ts | 29 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index a041736f3..22ececa4f 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -1,7 +1,6 @@ import {Directive} from '../Directive'; import {profile} from '../../profiler/decorator'; import {PowerDrillOverlord} from '../../overlords/powerMining/PowerDrill'; -import {Pathing} from "../../movement/Pathing"; import {calculateFormationStrength} from "../../utilities/creepUtils"; import {PowerHaulingOverlord} from "../../overlords/powerMining/PowerHauler"; import {log} from "../../console/log"; @@ -32,17 +31,18 @@ export class DirectivePowerMine extends Directive { private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; + requiredRCL: 7; memory: DirectivePowerMineMemory; constructor(flag: Flag) { - super(flag); - this._powerBank = this.room != undefined ? this.pos.lookForStructure(STRUCTURE_POWER_BANK) as StructurePowerBank : undefined; - this.memory.state = 0; + super(flag, colony => colony.level >= this.requiredRCL); + this._powerBank = this.powerBank; + this.memory.state = this.memory.state || 0; } spawnMoarOverlords() { - if (this.memory.state < 3 && this.powerBank) { + if (this.memory.state < 3) { this.overlords.powerMine = new PowerDrillOverlord(this); } if (this.memory.state > 1) { @@ -101,37 +101,46 @@ export class DirectivePowerMine extends Directive { manageState() { let currentState = this.memory.state; + log.alert(`Managing state ${currentState} of ${this.print} and ${this.powerBank}`); if (currentState == 0 && this.powerBank && this.powerBank.hits < this.powerBank.hitsMax) { if (this.powerBank.pos.findInRange(FIND_MY_CREEPS, 3).length == 0) { // Power bank is damage but we didn't mine it + Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); log.alert(`Power bank mining ${this.print} failed as someone else is mining this location`); this.remove(); } else { // Set to mining started this.memory.state = 1; } - } else if (currentState == 1 && this.room && (!this.powerBank || this.powerBank.hits < 500000)) { + } else if ((currentState == 0 || currentState == 1) && this.room && (!this.powerBank || this.powerBank.hits < 500000)) { + Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); log.info('Activating spawning haulers for power mining in room ' + this.pos.roomName); this.memory.state = 2; - } else if ((currentState == 1 || currentState == 2) && this.room && !this.powerBank && !this.hasDrops) { - log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING CREEP at time: ${Game.time}`); + } else if ((currentState == 0 || currentState == 1 || currentState == 2) && this.room && !this.powerBank && !this.hasDrops) { + // TODO this had an error where it triggered incorrectly + Game.notify(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.print} DELETING Directive at time ${Game.time}`); + log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING Directive at time: ${Game.time}`); this.remove(); - } else if (currentState == 2 && this.room && (!this.powerBank)) { + } else if (currentState == 2 && this.room && (!this.powerBank) && this.hasDrops) { + Game.notify(`Mining is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); log.alert(`Mining is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); this.memory.state = 3; // TODO reassign them to guard the bank delete this.overlords["powerMine"]; this._powerBank = undefined; // This might be fluff } else if (currentState == 3 && this.room && !this.hasDrops) { + Game.notify(`Hauler pickup is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); // Hauler pickup is now complete log.alert(`Hauler pickup is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); this.memory.state = 4; // TODO Stop spawning haulers } else if (currentState == 4 && this.overlords.powerHaul && (this.overlords.powerHaul as PowerHaulingOverlord).checkIfStillCarryingPower() == undefined) { + Game.notify(`Hauling complete for ${this.print} at time ${Game.time}`); log.alert(`Hauling complete for ${this.print} at time ${Game.time}`); this.remove(); } else { log.debug(`Power mining ${this.print} is in state ${currentState}`); + // Todo this isn't error but needs other stuff } } @@ -140,7 +149,7 @@ export class DirectivePowerMine extends Directive { } run(): void { - if (Game.time % 5 == 0) { + if (Game.time % 1 == 0) { this.manageState(); } } From bc6cf9d9c72ede3566e9a9d23d6165c5ea84d567 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 07:49:15 -0700 Subject: [PATCH 072/137] Adjusting rcl and exit conditions for states --- src/directives/resource/powerMine.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 22ececa4f..e1efba482 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -28,15 +28,15 @@ export class DirectivePowerMine extends Directive { static directiveName = 'powerMine'; static color = COLOR_YELLOW; static secondaryColor = COLOR_RED; + static requiredRCL: 7; private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; - requiredRCL: 7; memory: DirectivePowerMineMemory; constructor(flag: Flag) { - super(flag, colony => colony.level >= this.requiredRCL); + super(flag, colony => colony.level >= DirectivePowerMine.requiredRCL); this._powerBank = this.powerBank; this.memory.state = this.memory.state || 0; } @@ -116,11 +116,17 @@ export class DirectivePowerMine extends Directive { Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); log.info('Activating spawning haulers for power mining in room ' + this.pos.roomName); this.memory.state = 2; - } else if ((currentState == 0 || currentState == 1 || currentState == 2) && this.room && !this.powerBank && !this.hasDrops) { - // TODO this had an error where it triggered incorrectly - Game.notify(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.print} DELETING Directive at time ${Game.time}`); - log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING Directive at time: ${Game.time}`); - this.remove(); + } else if ((currentState == 0 || currentState == 1 || currentState == 2) && this.room && !this.powerBank) { + if (!this.hasDrops) { + // TODO this had an error where it triggered incorrectly + Game.notify(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.print} DELETING Directive at time ${Game.time}`); + log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING Directive at time: ${Game.time}`); + this.remove(); + } else { + // If somehow there is no bank but there is drops where bank was + this.memory.state = 3; + } + } else if (currentState == 2 && this.room && (!this.powerBank) && this.hasDrops) { Game.notify(`Mining is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); log.alert(`Mining is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); From 1d54bf0abd47ed474ad6dc73b6f4c30286c85676 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 07:56:30 -0700 Subject: [PATCH 073/137] Fixing RCL --- src/directives/resource/powerMine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index e1efba482..c621de5dd 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -28,7 +28,7 @@ export class DirectivePowerMine extends Directive { static directiveName = 'powerMine'; static color = COLOR_YELLOW; static secondaryColor = COLOR_RED; - static requiredRCL: 7; + static requiredRCL = 7; private _powerBank: StructurePowerBank | undefined; private _drops: { [resourceType: string]: Resource[] }; From b06b8ba2eb8702dd2e553a5bb9b281836e582c7f Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 10:59:29 -0700 Subject: [PATCH 074/137] Fixing logging --- src/directives/resource/powerMine.ts | 8 ++++---- src/overlords/powerMining/PowerHauler.ts | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index c621de5dd..40261b41c 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -101,7 +101,7 @@ export class DirectivePowerMine extends Directive { manageState() { let currentState = this.memory.state; - log.alert(`Managing state ${currentState} of ${this.print} and ${this.powerBank}`); + log.debug(`Managing state ${currentState} of directive ${this.print} with PB ${this.powerBank}`); if (currentState == 0 && this.powerBank && this.powerBank.hits < this.powerBank.hitsMax) { if (this.powerBank.pos.findInRange(FIND_MY_CREEPS, 3).length == 0) { // Power bank is damage but we didn't mine it @@ -141,8 +141,8 @@ export class DirectivePowerMine extends Directive { this.memory.state = 4; // TODO Stop spawning haulers } else if (currentState == 4 && this.overlords.powerHaul && (this.overlords.powerHaul as PowerHaulingOverlord).checkIfStillCarryingPower() == undefined) { - Game.notify(`Hauling complete for ${this.print} at time ${Game.time}`); - log.alert(`Hauling complete for ${this.print} at time ${Game.time}`); + Game.notify(`Hauling complete for ${this.print} at time ${Game.time}. Final power collected was ${(this.overlords.powerHaul as PowerHaulingOverlord).totalCollected} out of ${this.memory.totalResources}`); + log.alert(`Hauling complete for ${this.print} at time ${Game.time}. Final power collected was ${(this.overlords.powerHaul as PowerHaulingOverlord).totalCollected} out of ${this.memory.totalResources}`); this.remove(); } else { log.debug(`Power mining ${this.print} is in state ${currentState}`); @@ -155,7 +155,7 @@ export class DirectivePowerMine extends Directive { } run(): void { - if (Game.time % 1 == 0) { + if (Game.time % 9 == 0) { this.manageState(); } } diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index a91b30c9c..0a3e733da 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -19,7 +19,9 @@ export class PowerHaulingOverlord extends Overlord { directive: DirectivePowerMine; tickToSpawnOn: number; numHaulers: number; + totalCollected: number; + // TODO bug where haulers can come from tiny rooms not ready yet requiredRCL = 6; // Allow time for body to spawn prespawnAmount = 350; @@ -28,6 +30,7 @@ export class PowerHaulingOverlord extends Overlord { super(directive, 'powerHaul', priority); this.directive = directive; this.haulers = this.zerg(Roles.transport); + this.totalCollected = this.totalCollected || 0; // Spawn haulers to collect ALL the power at the same time. let haulingPartsNeeded = this.directive.totalResources/CARRY_CAPACITY; // Calculate amount of hauling each hauler provides in a lifetime @@ -102,6 +105,7 @@ export class PowerHaulingOverlord extends Overlord { return; } } else { // prefer to put minerals in terminal + this.totalCollected += hauler.carry.power || 0; if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { hauler.task = Tasks.transfer(this.colony.terminal, resourceType); return; @@ -125,7 +129,6 @@ export class PowerHaulingOverlord extends Overlord { run() { if (Game.time >= this.tickToSpawnOn && this.directive.memory.state < 4) { - Game.notify('Time to spawn haulers ' + this.pos.roomName); this.wishlist(this.numHaulers, Setups.transporters.default); } for (let hauler of this.haulers) { From 10b783df3df48d2df0fcdd9416772de53dfaaf1d Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 12:25:13 -0700 Subject: [PATCH 075/137] Minor tweaks to states --- src/directives/resource/powerMine.ts | 16 ++++++++-------- src/overlords/powerMining/PowerHauler.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/directives/resource/powerMine.ts b/src/directives/resource/powerMine.ts index 40261b41c..c48cf3eb7 100644 --- a/src/directives/resource/powerMine.ts +++ b/src/directives/resource/powerMine.ts @@ -103,10 +103,10 @@ export class DirectivePowerMine extends Directive { let currentState = this.memory.state; log.debug(`Managing state ${currentState} of directive ${this.print} with PB ${this.powerBank}`); if (currentState == 0 && this.powerBank && this.powerBank.hits < this.powerBank.hitsMax) { - if (this.powerBank.pos.findInRange(FIND_MY_CREEPS, 3).length == 0) { + if (this.powerBank.pos.findInRange(FIND_MY_CREEPS, 3).length == 0 && this.powerBank.pos.findInRange(FIND_HOSTILE_CREEPS, 3).length > 0) { // Power bank is damage but we didn't mine it - Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); - log.alert(`Power bank mining ${this.print} failed as someone else is mining this location`); + Game.notify(`Power bank mining ${this.print} failed as someone else is mining this location.`); + log.alert(`Power bank mining ${this.print} failed as someone else is mining this location.`); this.remove(); } else { // Set to mining started @@ -116,17 +116,16 @@ export class DirectivePowerMine extends Directive { Game.notify('Activating spawning haulers for power mining in room ' + this.pos.roomName); log.info('Activating spawning haulers for power mining in room ' + this.pos.roomName); this.memory.state = 2; - } else if ((currentState == 0 || currentState == 1 || currentState == 2) && this.room && !this.powerBank) { + } else if ((currentState == 0 || currentState == 1 || currentState == 2) && this.room && this.pos.isVisible && !this.powerBank) { if (!this.hasDrops) { // TODO this had an error where it triggered incorrectly - Game.notify(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.print} DELETING Directive at time ${Game.time}`); - log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISHED POWER MINING IN ${this.room} DELETING Directive at time: ${Game.time}`); + Game.notify(`WE FAILED. SORRY CHIEF, COULDN'T FINISH POWER MINING IN ${this.print} DELETING Directive at time ${Game.time}`); + log.error(`WE FAILED. SORRY CHIEF, COULDN'T FINISH POWER MINING IN ${this.room} DELETING Directive at time: ${Game.time}`); this.remove(); } else { // If somehow there is no bank but there is drops where bank was this.memory.state = 3; } - } else if (currentState == 2 && this.room && (!this.powerBank) && this.hasDrops) { Game.notify(`Mining is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); log.alert(`Mining is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); @@ -134,13 +133,14 @@ export class DirectivePowerMine extends Directive { // TODO reassign them to guard the bank delete this.overlords["powerMine"]; this._powerBank = undefined; // This might be fluff - } else if (currentState == 3 && this.room && !this.hasDrops) { + } else if (currentState == 3 && this.room && this.pos.isVisible && !this.hasDrops) { Game.notify(`Hauler pickup is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); // Hauler pickup is now complete log.alert(`Hauler pickup is complete for ${this.print} in ${this.room.print} at time ${Game.time}`); this.memory.state = 4; // TODO Stop spawning haulers } else if (currentState == 4 && this.overlords.powerHaul && (this.overlords.powerHaul as PowerHaulingOverlord).checkIfStillCarryingPower() == undefined) { + // TODO Doesn't give enough time to pick up power Game.notify(`Hauling complete for ${this.print} at time ${Game.time}. Final power collected was ${(this.overlords.powerHaul as PowerHaulingOverlord).totalCollected} out of ${this.memory.totalResources}`); log.alert(`Hauling complete for ${this.print} at time ${Game.time}. Final power collected was ${(this.overlords.powerHaul as PowerHaulingOverlord).totalCollected} out of ${this.memory.totalResources}`); this.remove(); diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index 0a3e733da..ddb27523e 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -73,7 +73,7 @@ export class PowerHaulingOverlord extends Overlord { hauler.say('🚬', true); } return; - } else if (this.room && this.room.drops) { + } else if (this.room && this.room.drops) { let allDrops: Resource[] = _.flatten(_.values(this.room.drops)); let drop = allDrops[0]; if (drop) { From 835fed9038ed20e4374871cce1d3b5eb8df212b0 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 15:55:58 -0700 Subject: [PATCH 076/137] Opgen part done --- src/directives/powerCreeps/baseOperator.ts | 267 +++++++++--------- .../powerCreeps/powers/generateOps.ts | 12 +- 2 files changed, 142 insertions(+), 137 deletions(-) diff --git a/src/directives/powerCreeps/baseOperator.ts b/src/directives/powerCreeps/baseOperator.ts index 63fb97bf4..93adbd7fe 100644 --- a/src/directives/powerCreeps/baseOperator.ts +++ b/src/directives/powerCreeps/baseOperator.ts @@ -3,6 +3,8 @@ import {profile} from "../../profiler/decorator"; import {Directive} from "../Directive"; import {log} from "../../console/log"; import {Visualizer} from "../../visuals/Visualizer"; +import {Power} from "./powers/genericPower"; +import {GenerateOps} from "./powers/generateOps"; interface DirectiveBaseOperatorMemory extends FlagMemory { @@ -47,7 +49,7 @@ export class DirectiveBaseOperator extends Directive { log.error(`Power Creep not found for ${this.print}, deleting directive`); this.remove(); } - this.memory.powerPriorities = this.defaultPowerPriorities; + this.memory.powerPriorities = this.memory.powerPriorities || this.defaultPowerPriorities; } spawnMoarOverlords() { @@ -78,147 +80,152 @@ export class DirectiveBaseOperator extends Directive { usePower(power: PowerConstant) { switch(power) { - case PWR_GENERATE_OPS: this.generateOps(); - case PWR_OPERATE_SPAWN: this.operateSpawn(); + case PWR_GENERATE_OPS: return new GenerateOps(); +// case PWR_OPERATE_SPAWN: return this.operateSpawn(); } } - - /** - * Generate 1/2/4/6/8 ops resource units. Cooldown 50 ticks. Required creep level: 0/2/7/14/22. - */ - generateOps() { - if (this.powerCreep.powers[PWR_GENERATE_OPS].cooldown !> 0) { - return this.powerCreep.usePower(PWR_GENERATE_OPS); - } - return ERR_TIRED; - } - - operateSpawn(spawn?: StructureSpawn) { - // if (this.powerCreep.powers[PWR_oper]) - // if (!spawn) { - // spawn = _.first(this.room!.spawns.filter(spawn => spawn.effects.length == 0)); - // if (!spawn) { - // return ERR; - // } - // } - if (this.pos.inRangeToPos(spawn.pos, 1)) { - return this.powerCreep.usePower(PWR_OPERATE_SPAWN, spawn); - } else { - return this.powerCreep.moveTo(spawn); - } - } - - operateTower(tower: StructureTower) { - if (this.pos.inRangeToPos(tower.pos, POWER_INFO[PWR_OPERATE_TOWER].range)) { - return this.powerCreep.usePower(PWR_OPERATE_TOWER, tower); - } else { - return this.powerCreep.moveTo(tower); - } - } - - operateStorage(storage: StructureStorage) { - if (this.pos.inRangeToPos(storage.pos, POWER_INFO[PWR_OPERATE_STORAGE].range)) { - return this.powerCreep.usePower(PWR_OPERATE_STORAGE, storage); - } else { - return this.powerCreep.moveTo(storage); - } - } - - operateExtensions(container: StructureStorage | StructureTerminal | StructureContainer) { - if (this.pos.inRangeToPos(container.pos, POWER_INFO[PWR_OPERATE_EXTENSION].range)) { - return this.powerCreep.usePower(PWR_OPERATE_EXTENSION, container); - } else { - return this.powerCreep.moveTo(container); - } - } - - operateObserver(observer: StructureObserver) { - if (this.pos.inRangeToPos(observer.pos, POWER_INFO[PWR_OPERATE_OBSERVER].range)) { - return this.powerCreep.usePower(PWR_OPERATE_OBSERVER, observer); - } else { - return this.powerCreep.moveTo(observer); - } - } - - operateTerminal(terminal: StructureTerminal) { - if (this.pos.inRangeToPos(terminal.pos, POWER_INFO[PWR_OPERATE_TERMINAL].range)) { - return this.powerCreep.usePower(PWR_OPERATE_TERMINAL, terminal); - } else { - return this.powerCreep.moveTo(terminal); - } - } - - operatePower(power: StructurePowerSpawn) { - if (this.pos.inRangeToPos(power.pos, POWER_INFO[PWR_OPERATE_POWER].range)) { - return this.powerCreep.usePower(PWR_OPERATE_POWER, power); - } else { - return this.powerCreep.moveTo(power); - } - } - - operateController(controller: StructureController) { - if (this.pos.inRangeToPos(controller.pos, POWER_INFO[PWR_OPERATE_CONTROLLER].range)) { - return this.powerCreep.usePower(PWR_OPERATE_CONTROLLER, controller); - } else { - return this.powerCreep.moveTo(controller); - } - } - - // operateFactory(factory: StructureFactory) { - // if (this.pos.inRangeToPos(factory.pos, POWER_INFO[PWR_OPERATE_FACTORY].range)) { - // return this.powerCreep.usePower(PWR_OPERATE_FACTORY, factory); + // + // /** + // * Generate 1/2/4/6/8 ops resource units. Cooldown 50 ticks. Required creep level: 0/2/7/14/22. + // */ + // generateOps() { + // if (this.powerCreep.powers[PWR_GENERATE_OPS].cooldown !> 0) { + // return this.powerCreep.usePower(PWR_GENERATE_OPS); + // } + // return ERR_TIRED; + // } + // + // operateSpawn(spawn?: StructureSpawn) { + // // if (this.powerCreep.powers[PWR_oper]) + // // if (!spawn) { + // // spawn = _.first(this.room!.spawns.filter(spawn => spawn.effects.length == 0)); + // // if (!spawn) { + // // return ERR; + // // } + // // } + // if (this.pos.inRangeToPos(spawn.pos, 1)) { + // return this.powerCreep.usePower(PWR_OPERATE_SPAWN, spawn); // } else { - // return this.moveTo(factory); + // return this.powerCreep.moveTo(spawn); + // } + // } + // + // operateTower(tower: StructureTower) { + // if (this.pos.inRangeToPos(tower.pos, POWER_INFO[PWR_OPERATE_TOWER].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_TOWER, tower); + // } else { + // return this.powerCreep.moveTo(tower); + // } + // } + // + // operateStorage(storage: StructureStorage) { + // if (this.pos.inRangeToPos(storage.pos, POWER_INFO[PWR_OPERATE_STORAGE].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_STORAGE, storage); + // } else { + // return this.powerCreep.moveTo(storage); + // } + // } + // + // operateExtensions(container: StructureStorage | StructureTerminal | StructureContainer) { + // if (this.pos.inRangeToPos(container.pos, POWER_INFO[PWR_OPERATE_EXTENSION].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_EXTENSION, container); + // } else { + // return this.powerCreep.moveTo(container); + // } + // } + // + // operateObserver(observer: StructureObserver) { + // if (this.pos.inRangeToPos(observer.pos, POWER_INFO[PWR_OPERATE_OBSERVER].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_OBSERVER, observer); + // } else { + // return this.powerCreep.moveTo(observer); + // } + // } + // + // operateTerminal(terminal: StructureTerminal) { + // if (this.pos.inRangeToPos(terminal.pos, POWER_INFO[PWR_OPERATE_TERMINAL].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_TERMINAL, terminal); + // } else { + // return this.powerCreep.moveTo(terminal); + // } + // } + // + // operatePower(power: StructurePowerSpawn) { + // if (this.pos.inRangeToPos(power.pos, POWER_INFO[PWR_OPERATE_POWER].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_POWER, power); + // } else { + // return this.powerCreep.moveTo(power); + // } + // } + // + // operateController(controller: StructureController) { + // if (this.pos.inRangeToPos(controller.pos, POWER_INFO[PWR_OPERATE_CONTROLLER].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_CONTROLLER, controller); + // } else { + // return this.powerCreep.moveTo(controller); + // } + // } + // + // // operateFactory(factory: StructureFactory) { + // // if (this.pos.inRangeToPos(factory.pos, POWER_INFO[PWR_OPERATE_FACTORY].range)) { + // // return this.powerCreep.usePower(PWR_OPERATE_FACTORY, factory); + // // } else { + // // return this.moveTo(factory); + // // } + // // } + // + // shield() { + // if (this.powerCreep.powers[PWR_SHIELD].cooldown !> 0) { + // return this.powerCreep.usePower(PWR_SHIELD); + // } + // return ERR_TIRED; + // } + // + // regenSource(source : Source) { + // if (this.pos.inRangeToPos(source.pos, POWER_INFO[PWR_REGEN_SOURCE].range)) { + // return this.powerCreep.usePower(PWR_REGEN_SOURCE, source); + // } else { + // return this.powerCreep.moveTo(source); + // } + // } + // + // regenMineral(mineral: Mineral) { + // if (this.pos.inRangeToPos(mineral.pos, POWER_INFO[PWR_REGEN_MINERAL].range)) { + // return this.powerCreep.usePower(PWR_REGEN_MINERAL, mineral); + // } else { + // return this.powerCreep.moveTo(mineral); + // } + // } + // + // fortify(rampart: StructureRampart) { + // if (this.pos.inRangeToPos(rampart.pos, POWER_INFO[PWR_FORTIFY].range)) { + // return this.powerCreep.usePower(PWR_FORTIFY, rampart); + // } else { + // return this.powerCreep.moveTo(rampart); + // } + // } + // + // operateLab(lab: StructureLab) { + // if (this.pos.inRangeToPos(lab.pos, POWER_INFO[PWR_OPERATE_LAB].range)) { + // return this.powerCreep.usePower(PWR_OPERATE_LAB, lab); + // } else { + // return this.powerCreep.moveTo(lab); // } // } - - shield() { - if (this.powerCreep.powers[PWR_SHIELD].cooldown !> 0) { - return this.powerCreep.usePower(PWR_SHIELD); - } - return ERR_TIRED; - } - - regenSource(source : Source) { - if (this.pos.inRangeToPos(source.pos, POWER_INFO[PWR_REGEN_SOURCE].range)) { - return this.powerCreep.usePower(PWR_REGEN_SOURCE, source); - } else { - return this.powerCreep.moveTo(source); - } - } - - regenMineral(mineral: Mineral) { - if (this.pos.inRangeToPos(mineral.pos, POWER_INFO[PWR_REGEN_MINERAL].range)) { - return this.powerCreep.usePower(PWR_REGEN_MINERAL, mineral); - } else { - return this.powerCreep.moveTo(mineral); - } - } - - fortify(rampart: StructureRampart) { - if (this.pos.inRangeToPos(rampart.pos, POWER_INFO[PWR_FORTIFY].range)) { - return this.powerCreep.usePower(PWR_FORTIFY, rampart); - } else { - return this.powerCreep.moveTo(rampart); - } - } - - operateLab(lab: StructureLab) { - if (this.pos.inRangeToPos(lab.pos, POWER_INFO[PWR_OPERATE_LAB].range)) { - return this.powerCreep.usePower(PWR_OPERATE_LAB, lab); - } else { - return this.powerCreep.moveTo(lab); - } - } runPowers() { - + const priorities = this.memory.powerPriorities; + for (let powerId of priorities) { + let powerToUse = this.usePower(powerId); + if (powerToUse && powerToUse.operatePower()) { + break; + } + } } - run(): void { if (!this.room) { return; diff --git a/src/directives/powerCreeps/powers/generateOps.ts b/src/directives/powerCreeps/powers/generateOps.ts index 7879ab578..e7b5f90ee 100644 --- a/src/directives/powerCreeps/powers/generateOps.ts +++ b/src/directives/powerCreeps/powers/generateOps.ts @@ -8,21 +8,19 @@ export const powerId = PWR_GENERATE_OPS; * An abstract class for encapsulating power creep power usage. */ @profile -export abstract class GenerateOps extends Power { - +export class GenerateOps extends Power { operatePower() { if (this.powerCreep.carry.ops && this.powerCreep.carry.ops > (this.powerCreep.carryCapacity * 0.9)) { - const storage = this.powerCreep.room!.storage; - if (!storage) { + const terminal = this.powerCreep.room!.terminal; + if (!terminal) { log.error(`Ops power creep with no storage`); } else { - this.powerCreep.moveTo(this.powerCreep.room!.storage!.pos); - this.powerCreep.transfer(storage, RESOURCE_OPS, this.powerCreep.carry.ops); + this.powerCreep.moveTo(terminal); + this.powerCreep.transfer(terminal, RESOURCE_OPS, this.powerCreep.carry.ops); } } else { return this.powerCreep.usePower(powerId); } return ERR_TIRED; - } } \ No newline at end of file From 2062d72d2c93c5b3d0ff0c8fe8b977fbc590ff71 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 17:21:26 -0700 Subject: [PATCH 077/137] Making the tasks work --- src/directives/powerCreeps/baseOperator.ts | 42 ++++++++++++------- .../powerCreeps/powers/generateOps.ts | 5 +++ .../powerCreeps/powers/genericPower.ts | 15 +++++++ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/directives/powerCreeps/baseOperator.ts b/src/directives/powerCreeps/baseOperator.ts index 93adbd7fe..f094a09fc 100644 --- a/src/directives/powerCreeps/baseOperator.ts +++ b/src/directives/powerCreeps/baseOperator.ts @@ -8,7 +8,7 @@ import {GenerateOps} from "./powers/generateOps"; interface DirectiveBaseOperatorMemory extends FlagMemory { - powerPriorities: PowerConstant[] + powerPriorities: PowerConstant[]; } /** @@ -26,7 +26,7 @@ export class DirectiveBaseOperator extends Directive { // Power Creep Hack powerCreep: PowerCreep; - defaultPowerPriorities: [ + defaultPowerPriorities: PowerConstant[] = [ PWR_GENERATE_OPS, PWR_REGEN_SOURCE, PWR_OPERATE_TOWER, @@ -63,24 +63,29 @@ export class DirectiveBaseOperator extends Directive { // Wrapped powerCreep methods =========================================================================================== renew(powerSource: StructurePowerBank | StructurePowerSpawn) { - if (this.pos.inRangeToPos(powerSource.pos, 1)) { + if (this.powerCreep.pos.inRangeToPos(powerSource.pos, 1)) { return this.powerCreep.renew(powerSource); } else { - return this.powerCreep.moveTo(powerSource); + return this.powerCreep.moveTo(powerSource, {ignoreRoads: true, range: 1, swampCost: 1, reusePath: 0, visualizePathStyle: {lineStyle: "dashed", fill: 'yellow'}}); } } enablePower(controller: StructureController) { - if (this.pos.inRangeToPos(controller.pos, 1)) { + log.alert(`Trying to enable power for ${controller} with `); + if (this.powerCreep.pos.inRangeToPos(controller.pos, 1)) { return this.powerCreep.enableRoom(controller); } else { - return this.powerCreep.moveTo(controller); + //let path = this.powerCreep.pos.findPathTo(controller, {ignoreRoads: true, range: 1, swampCost: 1}); + //log.alert(`Trying to enable power for ${controller} with ${JSON.stringify(path)}`); + //return this.powerCreep.moveByPath(path); + return this.powerCreep.moveTo(controller.pos, {ignoreRoads: true, range: 1, swampCost: 1, reusePath: 0, visualizePathStyle: {lineStyle: "solid"}}); } } usePower(power: PowerConstant) { + console.log(`The power constant is ${power}`) switch(power) { - case PWR_GENERATE_OPS: return new GenerateOps(); + case PWR_GENERATE_OPS: return new GenerateOps(this.powerCreep); // case PWR_OPERATE_SPAWN: return this.operateSpawn(); } @@ -217,8 +222,10 @@ export class DirectiveBaseOperator extends Directive { runPowers() { const priorities = this.memory.powerPriorities; - for (let powerId of priorities) { - let powerToUse = this.usePower(powerId); + console.log(`Powerid of priority list of ${priorities}`); + for (let powerId in priorities) { + console.log(`Powerid of ${powerId} and list of ${priorities}`); + let powerToUse = this.usePower(priorities[powerId]); if (powerToUse && powerToUse.operatePower()) { break; } @@ -227,18 +234,23 @@ export class DirectiveBaseOperator extends Directive { run(): void { + console.log(`Running power creep ${JSON.stringify(this.powerCreep)} with ttl ${this.powerCreep.ticksToLive} with ${this.room!.powerSpawn}`); if (!this.room) { return; - } else if (this.powerCreep.ticksToLive == undefined && this.powerCreep.spawnCooldownTime !> 0 && this.room && this.room.powerSpawn) { + } else if (!this.powerCreep.ticksToLive && this.room && this.room.powerSpawn) { // Spawn creep - this.powerCreep.spawn(this.room.powerSpawn); + let res = this.powerCreep.spawn(this.room.powerSpawn); + log.alert(`Running ${this.powerCreep} with spawn of ${res}`); } else if (this.room.controller && !this.room.controller.isPowerEnabled) { // Enable power - this.enablePower(this.room.controller); - } else if (this.powerCreep && this.powerCreep.ticksToLive && this.powerCreep.ticksToLive < 300 && this.room.powerSpawn) { - this.renew(this.room.powerSpawn); + let res = this.enablePower(this.room.controller); + log.alert(`Running ${this.powerCreep} with enable power of ${res}`); + } else if (this.powerCreep && this.powerCreep.ticksToLive && this.powerCreep.ticksToLive < 400 && this.room.powerSpawn) { + let res = this.renew(this.room.powerSpawn); + log.alert(`Running ${this.powerCreep} with renew of ${res}`); } else { - this.runPowers(); + let res = this.runPowers(); + log.alert(`Running ${this.powerCreep} with power of ${res}`); } diff --git a/src/directives/powerCreeps/powers/generateOps.ts b/src/directives/powerCreeps/powers/generateOps.ts index e7b5f90ee..2f02070bd 100644 --- a/src/directives/powerCreeps/powers/generateOps.ts +++ b/src/directives/powerCreeps/powers/generateOps.ts @@ -9,6 +9,11 @@ export const powerId = PWR_GENERATE_OPS; */ @profile export class GenerateOps extends Power { + + constructor(powerCreep: PowerCreep, target?: RoomObject) { + super(powerCreep, target); + } + operatePower() { if (this.powerCreep.carry.ops && this.powerCreep.carry.ops > (this.powerCreep.carryCapacity * 0.9)) { const terminal = this.powerCreep.room!.terminal; diff --git a/src/directives/powerCreeps/powers/genericPower.ts b/src/directives/powerCreeps/powers/genericPower.ts index 29ef4b76d..97a07042e 100644 --- a/src/directives/powerCreeps/powers/genericPower.ts +++ b/src/directives/powerCreeps/powers/genericPower.ts @@ -1,5 +1,6 @@ import {profile} from "../../../profiler/decorator"; import {powerId} from "./generateOps"; +import {log} from "../../../console/log"; /** * An abstract class for encapsulating power creep power usage. @@ -18,6 +19,20 @@ export abstract class Power { name: string; }; + constructor(powerCreep: PowerCreep, target?: RoomObject) { + log.notify(`Creating power task for ${powerCreep}`); + this._powerCreep = { + name: powerCreep.name, + }; + if (target) { + this._target = { + ref : target.ref, + _pos: target.pos, + } + } + + } + /** * Dereferences the Task's target */ From 3fca31d7de893838f27e359fbc66ca277cf3aa28 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 24 May 2019 23:15:03 -0700 Subject: [PATCH 078/137] Temporary commit, remove later --- src/declarations/prototypes.d.ts | 11 + src/zerg/PowerZerg.ts | 51 +++ src/zerg/ZergShell.ts | 592 +++++++++++++++++++++++++++++++ 3 files changed, 654 insertions(+) create mode 100644 src/zerg/PowerZerg.ts create mode 100644 src/zerg/ZergShell.ts diff --git a/src/declarations/prototypes.d.ts b/src/declarations/prototypes.d.ts index 1d0e9f187..093f452a9 100644 --- a/src/declarations/prototypes.d.ts +++ b/src/declarations/prototypes.d.ts @@ -7,6 +7,17 @@ interface Creep { inRampart: boolean; } +interface PowerCreep { + hitsPredicted?: number; + intel?: { [property: string]: number }; + memory: CreepMemory; + fatigue: number; + body: BodyPartDefinition[]; + boosts: _ResourceConstantSansEnergy[]; + boostCounts: { [boostType: string]: number }; + inRampart: boolean; +} + interface ConstructionSite { isWalkable: boolean; } diff --git a/src/zerg/PowerZerg.ts b/src/zerg/PowerZerg.ts new file mode 100644 index 000000000..36e5bff54 --- /dev/null +++ b/src/zerg/PowerZerg.ts @@ -0,0 +1,51 @@ +import {CombatIntel} from '../intel/CombatIntel'; +import {Movement, NO_ACTION} from '../movement/Movement'; +import {profile} from '../profiler/decorator'; +import {CombatTargeting} from '../targeting/CombatTargeting'; +import {GoalFinder} from '../targeting/GoalFinder'; +import {randomHex} from '../utilities/utils'; +import {Zerg} from './Zerg'; + +interface CombatZergMemory extends CreepMemory { + recovering: boolean; + lastInDanger: number; + partner?: string; + swarm?: string; +} + +export const DEFAULT_PARTNER_TICK_DIFFERENCE = 650; +export const DEFAULT_SWARM_TICK_DIFFERENCE = 500; + +/** + * CombatZerg is an extension of the Zerg class which contains additional combat-related methods + */ +@profile +export class PowerZerg extends Zerg { + + memory: CombatZergMemory; + isPowerZerg: boolean; + + constructor(creep: Creep, notifyWhenAttacked = true) { + super(creep, notifyWhenAttacked); + this.isPowerZerg = true; + _.defaults(this.memory, { + recovering : false, + lastInDanger: 0, + targets : {} + }); + } + + static fatigue() { + return 0; + } + + static body() { + return [MOVE]; + } + + static attack(target: Creep | Structure): 0 | -1 | -4 | -7 | -9 | -12 | -11 { + return ERR_TIRED; + } + + +} diff --git a/src/zerg/ZergShell.ts b/src/zerg/ZergShell.ts new file mode 100644 index 000000000..44b11c033 --- /dev/null +++ b/src/zerg/ZergShell.ts @@ -0,0 +1,592 @@ +import {Colony} from '../Colony'; +import {log} from '../console/log'; +import {isCreep, isZerg} from '../declarations/typeGuards'; +import {CombatIntel} from '../intel/CombatIntel'; +import {Movement, MoveOptions} from '../movement/Movement'; +import {Overlord} from '../overlords/Overlord'; +import {profile} from '../profiler/decorator'; +import {initializeTask} from '../tasks/initializer'; +import {Task} from '../tasks/Task'; +import {NEW_OVERMIND_INTERVAL} from '../~settings'; +import {PowerZerg} from "./PowerZerg"; + +export function getOverlord(creep: Zerg | Creep): Overlord | null { + if (creep.memory[_MEM.OVERLORD]) { + return Overmind.overlords[creep.memory[_MEM.OVERLORD]!] || null; + } else { + return null; + } +} + +export function setOverlord(creep: Zerg | Creep, newOverlord: Overlord | null) { + // Remove cache references to old assignments + const roleName = creep.memory.role; + const ref = creep.memory[_MEM.OVERLORD]; + const oldOverlord: Overlord | null = ref ? Overmind.overlords[ref] : null; + if (ref && Overmind.cache.overlords[ref] && Overmind.cache.overlords[ref][roleName]) { + _.remove(Overmind.cache.overlords[ref][roleName], name => name == creep.name); + } + if (newOverlord) { + // Change to the new overlord's colony + creep.memory[_MEM.COLONY] = newOverlord.colony.name; + // Change assignments in memory + creep.memory[_MEM.OVERLORD] = newOverlord.ref; + // Update the cache references + if (!Overmind.cache.overlords[newOverlord.ref]) { + Overmind.cache.overlords[newOverlord.ref] = {}; + } + if (!Overmind.cache.overlords[newOverlord.ref][roleName]) { + Overmind.cache.overlords[newOverlord.ref][roleName] = []; + } + Overmind.cache.overlords[newOverlord.ref][roleName].push(creep.name); + } else { + creep.memory[_MEM.OVERLORD] = null; + } + if (oldOverlord) oldOverlord.recalculateCreeps(); + if (newOverlord) newOverlord.recalculateCreeps(); +} + +export function normalizeZerg(creep: Zerg | Creep): Zerg | Creep { + return Overmind.zerg[creep.name] || creep; +} + +export function toCreep(creep: Zerg | Creep): Creep { + return isZerg(creep) ? creep.creep : creep; +} + +// Last pipeline is more complex because it depends on the energy a creep has; sidelining this for now +const actionPipelines: string[][] = [ + ['harvest', 'attack', 'build', 'repair', 'dismantle', 'attackController', 'rangedHeal', 'heal'], + ['rangedAttack', 'rangedMassAttack', 'build', 'repair', 'rangedHeal'], + // ['upgradeController', 'build', 'repair', 'withdraw', 'transfer', 'drop'], +]; + +interface ParkingOptions { + range: number; + exactRange: boolean; + offroad: boolean; +} + +interface FleeOptions { + dropEnergy?: boolean; + invalidateTask?: boolean; +} + +const RANGES = { + BUILD : 3, + REPAIR : 3, + TRANSFER: 1, + WITHDRAW: 1, + HARVEST : 1, + DROP : 0, +}; + +/** + * The Zerg class is a wrapper for owned creeps and contains all wrapped creep methods and many additional methods for + * direct control of a creep. + */ +@profile +export class Zerg { + + creep: Creep | PowerZerg; // The creep that this wrapper class will control + body: BodyPartDefinition[]; // These properties are all wrapped from this.creep.* to this.* + carry: StoreDefinition; // | + carryCapacity: number; // | + fatigue: number; // | + hits: number; // | + hitsMax: number; // | + id: string; // | + memory: CreepMemory; // | See the ICreepMemory interface for structure + name: string; // | + pos: RoomPosition; // | + nextPos: RoomPosition; // | The next position the creep will be in after registering a move intent + ref: string; // | + roleName: string; // | + room: Room; // | + saying: string; // | + spawning: boolean; // | + ticksToLive: number | undefined; // | + lifetime: number; + actionLog: { [actionName: string]: boolean }; // Tracks the actions that a creep has completed this tick + blockMovement: boolean; // Whether the zerg is allowed to move or not + private _task: Task | null; // Cached Task object that is instantiated once per tick and on change + + constructor(creep: Creep, notifyWhenAttacked = true) { + // Copy over creep references + this.creep = creep; + this.body = creep.body; + this.carry = creep.carry; + this.carryCapacity = creep.carryCapacity; + this.fatigue = creep.fatigue; + this.hits = creep.hits; + this.hitsMax = creep.hitsMax; + this.id = creep.id; + this.memory = creep.memory; + this.name = creep.name; + this.pos = creep.pos; + this.nextPos = creep.pos; + this.ref = creep.ref; + this.roleName = creep.memory.role; + this.room = creep.room; + this.saying = creep.saying; + this.spawning = creep.spawning; + this.ticksToLive = creep.ticksToLive; + // Extra properties + this.lifetime = this.getBodyparts(CLAIM) > 0 ? CREEP_CLAIM_LIFE_TIME : CREEP_LIFE_TIME; + this.actionLog = {}; + this.blockMovement = false; + // Register global references + Overmind.zerg[this.name] = this; + global[this.name] = this; + // Handle attack notification when at lifetime - 1 + if (!notifyWhenAttacked && (this.ticksToLive || 0) >= this.lifetime - (NEW_OVERMIND_INTERVAL + 1)) { + // creep.notifyWhenAttacked only uses the 0.2CPU intent cost if it changes the intent value + this.notifyWhenAttacked(notifyWhenAttacked); + } + } + + /** + * Refresh all changeable properties of the creep or delete from Overmind and global when dead + */ + refresh(): void { + const creep = Game.creeps[this.name]; + if (creep) { + this.creep = creep; + this.pos = creep.pos; + this.nextPos = creep.pos; + this.body = creep.body; + this.carry = creep.carry; + this.carryCapacity = creep.carryCapacity; + this.fatigue = creep.fatigue; + this.hits = creep.hits; + this.memory = creep.memory; + this.roleName = creep.memory.role; + this.room = creep.room; + this.saying = creep.saying; + this.spawning = creep.spawning; + this.ticksToLive = creep.ticksToLive; + this.actionLog = {}; + this.blockMovement = false; + this._task = null; // todo + } else { + log.debug(`Deleting from global`); + delete Overmind.zerg[this.name]; + delete global[this.name]; + } + } + + debug(...args: any[]) { + if (this.memory.debug) { + log.debug(this.print, args); + } + } + + get ticksUntilSpawned(): number | undefined { + if (this.spawning) { + const spawner = this.pos.lookForStructure(STRUCTURE_SPAWN) as StructureSpawn; + if (spawner && spawner.spawning) { + return spawner.spawning.remainingTime; + } else { + // Shouldn't ever get here + console.log(`Error determining ticks to spawn for ${this.name} @ ${this.pos.print}!`); + } + } + } + + get print(): string { + return '[' + this.name + ']'; + } + + cancelOrder(methodName: string): OK | ERR_NOT_FOUND { + const result = this.creep.cancelOrder(methodName); + if (result == OK) this.actionLog[methodName] = false; + return result; + } + + drop(resourceType: ResourceConstant, amount?: number) { + const result = this.creep.drop(resourceType, amount); + if (!this.actionLog.drop) this.actionLog.drop = (result == OK); + return result; + } + + goDrop(pos: RoomPosition, resourceType: ResourceConstant, amount?: number) { + if (this.pos.inRangeToPos(pos, RANGES.DROP)) { + return this.drop(resourceType, amount); + } else { + return this.goTo(pos); + } + } + + generateSafeMode(target: StructureController) { + return this.creep.generateSafeMode(target); + } + + harvest(source: Source | Mineral) { + const result = this.creep.harvest(source); + if (!this.actionLog.harvest) this.actionLog.harvest = (result == OK); + return result; + } + + goHarvest(source: Source | Mineral) { + if (this.pos.inRangeToPos(source.pos, RANGES.HARVEST)) { + return this.harvest(source); + } else { + return this.goTo(source); + } + } + + move(direction: DirectionConstant, force = false) { + if (!this.blockMovement && !force) { + const result = this.creep.move(direction); + if (result == OK) { + if (!this.actionLog.move) this.actionLog.move = true; + this.nextPos = this.pos.getPositionAtDirection(direction); + } + return result; + } else { + return ERR_BUSY; + } + } + + notifyWhenAttacked(enabled: boolean) { + return this.creep.notifyWhenAttacked(enabled); + } + + pickup(resource: Resource) { + const result = this.creep.pickup(resource); + if (!this.actionLog.pickup) this.actionLog.pickup = (result == OK); + return result; + } + + /* Say a message; maximum message length is 10 characters */ + say(message: string, pub?: boolean) { + return this.creep.say(message, pub); + } + + signController(target: StructureController, text: string) { + const result = this.creep.signController(target, text); + if (!this.actionLog.signController) this.actionLog.signController = (result == OK); + return result; + } + + suicide() { + return this.creep.suicide(); + } + + transfer(target: Creep | Zerg | Structure, resourceType: ResourceConstant = RESOURCE_ENERGY, amount?: number) { + let result: ScreepsReturnCode; + if (target instanceof Zerg) { + result = this.creep.transfer(target.creep, resourceType, amount); + } else { + result = this.creep.transfer(target, resourceType, amount); + } + if (!this.actionLog.transfer) this.actionLog.transfer = (result == OK); + return result; + } + + goTransfer(target: Creep | Zerg | Structure, resourceType: ResourceConstant = RESOURCE_ENERGY, amount?: number) { + if (this.pos.inRangeToPos(target.pos, RANGES.TRANSFER)) { + return this.transfer(target, resourceType, amount); + } else { + return this.goTo(target); + } + } + + withdraw(target: Structure | Tombstone, resourceType: ResourceConstant = RESOURCE_ENERGY, amount?: number) { + const result = this.creep.withdraw(target, resourceType, amount); + if (!this.actionLog.withdraw) this.actionLog.withdraw = (result == OK); + return result; + } + + goWithdraw(target: Structure | Tombstone, resourceType: ResourceConstant = RESOURCE_ENERGY, amount?: number) { + if (this.pos.inRangeToPos(target.pos, RANGES.WITHDRAW)) { + return this.withdraw(target, resourceType, amount); + } else { + return this.goTo(target); + } + } + + // Simultaneous creep actions -------------------------------------------------------------------------------------- + + /** + * Determine whether the given action will conflict with an action the creep has already taken. + * See http://docs.screeps.com/simultaneous-actions.html for more details. + */ + canExecute(actionName: string): boolean { + // Only one action can be executed from within a single pipeline + let conflictingActions: string[] = [actionName]; + for (const pipeline of actionPipelines) { + if (pipeline.includes(actionName)) conflictingActions = conflictingActions.concat(pipeline); + } + for (const action of conflictingActions) { + if (this.actionLog[action]) { + return false; + } + } + return true; + } + + // Body configuration and related data ----------------------------------------------------------------------------- + + getActiveBodyparts(type: BodyPartConstant): number { + return this.creep.getActiveBodyparts(type); + } + + /* The same as creep.getActiveBodyparts, but just counts bodyparts regardless of condition. */ + getBodyparts(partType: BodyPartConstant): number { + return _.filter(this.body, (part: BodyPartDefinition) => part.type == partType).length; + } + + // Custom creep methods ============================================================================================ + + // Carry methods + + get hasMineralsInCarry(): boolean { + for (const resourceType in this.carry) { + if (resourceType != RESOURCE_ENERGY && (this.carry[resourceType] || 0) > 0) { + return true; + } + } + return false; + } + + // Boosting logic -------------------------------------------------------------------------------------------------- + + get boosts(): _ResourceConstantSansEnergy[] { + return this.creep.boosts; + } + + get boostCounts(): { [boostType: string]: number } { + return this.creep.boostCounts; + } + + get needsBoosts(): boolean { + if (this.overlord) { + return this.overlord.shouldBoost(this); + } + return false; + } + + // Overlord logic -------------------------------------------------------------------------------------------------- + + get overlord(): Overlord | null { + return getOverlord(this); + } + + set overlord(newOverlord: Overlord | null) { + setOverlord(this, newOverlord); + } + + /* Reassigns the creep to work under a new overlord and as a new role. */ + reassign(newOverlord: Overlord | null, newRole: string, invalidateTask = true) { + this.overlord = newOverlord; + this.roleName = newRole; + this.memory.role = newRole; + if (invalidateTask) { + this.task = null; + } + } + + // Task logic ------------------------------------------------------------------------------------------------------ + + /** + * Wrapper for _task + */ + get task(): Task | null { + if (!this._task) { + this._task = this.memory.task ? initializeTask(this.memory.task) : null; + } + return this._task; + } + + /** + * Assign the creep a task with the setter, replacing creep.assign(Task) + */ + set task(task: Task | null) { + // Unregister target from old task if applicable + const oldProtoTask = this.memory.task; + if (oldProtoTask) { + const oldRef = oldProtoTask._target.ref; + if (Overmind.cache.targets[oldRef]) { + _.remove(Overmind.cache.targets[oldRef], name => name == this.name); + } + } + // Set the new task + this.memory.task = task ? task.proto : null; + if (task) { + if (task.target) { + // Register task target in cache if it is actively targeting something (excludes goTo and similar) + if (!Overmind.cache.targets[task.target.ref]) { + Overmind.cache.targets[task.target.ref] = []; + } + Overmind.cache.targets[task.target.ref].push(this.name); + } + // Register references to creep + task.creep = this; + } + // Clear cache + this._task = null; + } + + /** + * Does the creep have a valid task at the moment? + */ + get hasValidTask(): boolean { + return !!this.task && this.task.isValid(); + } + + /** + * Creeps are idle if they don't have a task. + */ + get isIdle(): boolean { + return !this.task || !this.task.isValid(); + } + + /** + * Execute the task you currently have. + */ + run(): number | undefined { + if (this.task) { + return this.task.run(); + } + } + + // Colony association ---------------------------------------------------------------------------------------------- + + /** + * Colony that the creep belongs to. + */ + get colony(): Colony { + return Overmind.colonies[this.memory[_MEM.COLONY]]; + } + + set colony(newColony: Colony) { + this.memory[_MEM.COLONY] = newColony.name; + } + + /** + * If the creep is in a colony room or outpost + */ + get inColonyRoom(): boolean { + return Overmind.colonyMap[this.room.name] == this.memory[_MEM.COLONY]; + } + + // Movement and location ------------------------------------------------------------------------------------------- + + goTo(destination: RoomPosition | HasPos, options: MoveOptions = {}) { + return Movement.goTo(this, destination, options); + } + + goToRoom(roomName: string, options: MoveOptions = {}) { + return Movement.goToRoom(this, roomName, options); + } + + inSameRoomAs(target: HasPos): boolean { + return this.pos.roomName == target.pos.roomName; + } + + safelyInRoom(roomName: string): boolean { + return this.room.name == roomName && !this.pos.isEdge; + } + + get inRampart(): boolean { + return this.creep.inRampart; + } + + get isMoving(): boolean { + const moveData = this.memory._go as MoveData | undefined; + return !!moveData && !!moveData.path && moveData.path.length > 1; + } + + /** + * Kite around hostiles in the room + */ + kite(avoidGoals: (RoomPosition | HasPos)[] = this.room.hostiles, options: MoveOptions = {}): number | undefined { + _.defaults(options, { + fleeRange: 5 + }); + return Movement.kite(this, avoidGoals, options); + } + + private defaultFleeGoals() { + let fleeGoals: (RoomPosition | HasPos)[] = []; + fleeGoals = fleeGoals.concat(this.room.hostiles) + .concat(_.filter(this.room.keeperLairs, lair => (lair.ticksToSpawn || Infinity) < 10)); + return fleeGoals; + } + + /** + * Flee from hostiles in the room, while not repathing every tick + */ + flee(avoidGoals: (RoomPosition | HasPos)[] = this.room.fleeDefaults, + fleeOptions: FleeOptions = {}, + moveOptions: MoveOptions = {}): boolean { + if (avoidGoals.length == 0) { + return false; + } else if (this.room.controller && this.room.controller.my && this.room.controller.safeMode) { + return false; + } else { + const fleeing = Movement.flee(this, avoidGoals, fleeOptions.dropEnergy, moveOptions) != undefined; + if (fleeing) { + // Drop energy if needed + if (fleeOptions.dropEnergy && this.carry.energy > 0) { + const nearbyContainers = this.pos.findInRange(this.room.storageUnits, 1); + if (nearbyContainers.length > 0) { + this.transfer(_.first(nearbyContainers), RESOURCE_ENERGY); + } else { + this.drop(RESOURCE_ENERGY); + } + } + // Invalidate task + if (fleeOptions.invalidateTask) { + this.task = null; + } + } + return fleeing; + } + } + + /** + * Park the creep off-roads + */ + park(pos: RoomPosition = this.pos, maintainDistance = false): number { + return Movement.park(this, pos, maintainDistance); + } + + /** + * Moves a creep off of the current tile to the first available neighbor + */ + moveOffCurrentPos(): number | undefined { + return Movement.moveOffCurrentPos(this); + } + + /** + * Moves onto an exit tile + */ + moveOnExit(): ScreepsReturnCode | undefined { + return Movement.moveOnExit(this); + } + + /** + * Moves off of an exit tile + */ + moveOffExit(avoidSwamp = true): ScreepsReturnCode { + return Movement.moveOffExit(this, avoidSwamp); + } + + moveOffExitToward(pos: RoomPosition, detour = true): number | undefined { + return Movement.moveOffExitToward(this, pos, detour); + } + + // Miscellaneous fun stuff ----------------------------------------------------------------------------------------- + + sayLoop(messageList: string[], pub?: boolean) { + return this.say(messageList[Game.time % messageList.length], pub); + } + + sayRandom(phrases: string[], pub?: boolean) { + return this.say(phrases[Math.floor(Math.random() * phrases.length)], pub); + } + +} + From 41bb9000c045b0f40ad1f6d215fbe93567d19e1f Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Sun, 26 May 2019 11:56:27 +0300 Subject: [PATCH 079/137] New DirectivePoisonRoom --- src/directives/initializer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/directives/initializer.ts b/src/directives/initializer.ts index 081a081bd..a13ec5521 100644 --- a/src/directives/initializer.ts +++ b/src/directives/initializer.ts @@ -12,6 +12,7 @@ import {Directive} from './Directive'; import {DirectiveControllerAttack} from './offense/controllerAttack'; import {DirectivePairDestroy} from './offense/pairDestroy'; import {DirectiveSwarmDestroy} from './offense/swarmDestroy'; +import {DirectivePoisonRoom} from './offense/poisonRoom'; import {DirectiveExtract} from './resource/extract'; import {DirectiveHarvest} from './resource/harvest'; import {DirectiveHaul} from './resource/haul'; @@ -58,6 +59,8 @@ export function DirectiveWrapper(flag: Flag): Directive | undefined { return new DirectivePairDestroy(flag); case COLOR_PURPLE: return new DirectiveControllerAttack(flag); + case COLOR_ORANGE: + return new DirectivePoisonRoom(flag); } break; From e440d208166cfd0581a089572f69470224f1eebc Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Sun, 26 May 2019 11:56:44 +0300 Subject: [PATCH 080/137] New DirectivePoisonRoom --- src/directives/offense/poisonRoom.ts | 91 ++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/directives/offense/poisonRoom.ts diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts new file mode 100644 index 000000000..4d22f79c1 --- /dev/null +++ b/src/directives/offense/poisonRoom.ts @@ -0,0 +1,91 @@ +import {ClaimingOverlord} from '../../overlords/colonization/claimer'; +import {WorkerOverlord} from '../../overlords/core/worker'; +import {log} from '../../console/log'; +import {profile} from '../../profiler/decorator'; +import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; +import {printRoomName} from '../../utilities/utils'; +import {MY_USERNAME} from '../../~settings'; +import {Directive} from '../Directive'; + + +/** + * Poison sources in remote rooms by claiming controller, walling its sources and controller then unclaiming it. + */ +@profile +export class DirectivePoisonRoom extends Directive { + + static directiveName = 'poisonRoom'; + static color = COLOR_RED; + static secondaryColor = COLOR_BROWN; + + overlords: { + claim: ClaimingOverlord; + work: WorkerOverlord; + }; + + constructor(flag: Flag) { + super(flag, colony => colony.level >= 4); + // Remove if misplaced + if (Cartographer.roomType(this.pos.roomName) != ROOMTYPE_CONTROLLER) { + log.warning(`${this.print}: ${printRoomName(this.pos.roomName)} is not a controller room; ` + + `removing directive!`); + this.remove(true); + } + } + + spawnMoarOverlords() { + this.overlords.claim = new ClaimingOverlord(this); + this.overlords.work = new WorkerOverlord(this.colony); + } + + init() { + this.alert(`Poisining Room ${printRoomName(this.pos.roomName)} `); + } + + private isPoisoned(): boolean { + if (this.room) { + const AllSources = this.room.find(FIND_SOURCES); + let result = true; + //Check for walkable source.pos.neighbors and place wall constuction site + for (const s of AllSources) { + let walkableSourcePosisions = _.filter(s.pos.neighbors, pos => pos.isWalkable()); + if(walkableSourcePosisions.length){ + _.forEach(walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); + } + result = result && !walkableSourcePosisions.length; + } + //Check for walkable source.pos.neighbors and place wall constuction site + let walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); + if(walkableControllerPosisions.length){ + _.forEach(walkableControllerPosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); + } + result = result && !walkableControllerPosisions.length; + return result; + } else { + return false; + } + } + + run() { + // Remove if structures are done + if (this.room && this.room.my) { + const isPoisoned = this.isPoisoned(); + if (isPoisoned) { + this.room.controller!.unclaim(); + log.notify(`Removing roomPoising directive in ${this.pos.roomName}: operation completed.`); + this.remove(); + } + } + + // Remove if owned by other player + if (Game.time % 10 == 2 && this.room && !!this.room.owner && this.room.owner != MY_USERNAME) { + log.notify(`Removing roomPoising directive in ${this.pos.roomName}: room already owned by another player.`); + this.remove(); + } + } +} + + + + + From 47cf1d559f74610eddf4d3b9861438931c55bdb4 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Sun, 26 May 2019 12:04:42 +0300 Subject: [PATCH 081/137] avoid colonization --- src/Overseer.ts | 4 +++- src/directives/offense/poisonRoom.ts | 36 ++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/Overseer.ts b/src/Overseer.ts index 4f221128d..fab0a78c2 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -8,6 +8,7 @@ import {DirectiveOutpost} from './directives/colony/outpost'; import {DirectiveGuard} from './directives/defense/guard'; import {DirectiveInvasionDefense} from './directives/defense/invasionDefense'; import {DirectiveOutpostDefense} from './directives/defense/outpostDefense'; +import {DirectivePoisonRoom} from './directives/offense/poisonRoom'; import {Directive} from './directives/Directive'; import {Notifier} from './directives/Notifier'; import {DirectiveBootstrap} from './directives/situational/bootstrap'; @@ -297,7 +298,8 @@ export class Overseer implements IOverseer { } // Place pioneer directives in case the colony doesn't have a spawn for some reason if (Game.time % 25 == 0 && colony.spawns.length == 0 && - !DirectiveClearRoom.isPresent(colony.pos, 'room')) { + !DirectiveClearRoom.isPresent(colony.pos, 'room') && + !DirectivePoisonRoom.isPresent(colony.pos, 'room')) { // verify that there are no spawns (not just a caching glitch) const spawns = Game.rooms[colony.name]!.find(FIND_MY_SPAWNS); if (spawns.length == 0) { diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 4d22f79c1..f598d6025 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -9,7 +9,7 @@ import {Directive} from '../Directive'; /** - * Poison sources in remote rooms by claiming controller, walling its sources and controller then unclaiming it. + * Poison sources in remote rooms by claiming controller, walling in its sources and controller then unclaiming it. */ @profile export class DirectivePoisonRoom extends Directive { @@ -44,17 +44,17 @@ export class DirectivePoisonRoom extends Directive { private isPoisoned(): boolean { if (this.room) { - const AllSources = this.room.find(FIND_SOURCES); + const allSources = this.room.find(FIND_SOURCES); let result = true; //Check for walkable source.pos.neighbors and place wall constuction site - for (const s of AllSources) { + for (const s of allSources) { let walkableSourcePosisions = _.filter(s.pos.neighbors, pos => pos.isWalkable()); if(walkableSourcePosisions.length){ _.forEach(walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); } result = result && !walkableSourcePosisions.length; } - //Check for walkable source.pos.neighbors and place wall constuction site + //Check for walkable controller.pos.neighbors and place wall constuction site let walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); if(walkableControllerPosisions.length){ _.forEach(walkableControllerPosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); @@ -66,20 +66,36 @@ export class DirectivePoisonRoom extends Directive { } } + private getWallsConstructionSites() { + const room = Game.rooms[this.pos.roomName]; + const constructionSites = room.find(FIND_CONSTRUCTION_SITES); + const wallsConstructionSites = _.filter(constructionSites, s => s.structureType == STRUCTURE_WALL); + return wallsConstructionSites; + } + run() { - // Remove if structures are done + if (this.room && this.room.my) { - const isPoisoned = this.isPoisoned(); - if (isPoisoned) { + // Remove if poisoned + if (this.isPoisoned()) { this.room.controller!.unclaim(); - log.notify(`Removing roomPoising directive in ${this.pos.roomName}: operation completed.`); + log.notify(`Removing poisonRoom directive in ${this.pos.roomName}: operation completed.`); this.remove(); - } + } else { + //Assign workers to wall sources and controller + const wallsConstructionSites = this.getWallsConstructionSites(); + _.forEach(wallsConstructionSites,csite => { + if (!this.colony.overlords.work.constructionSites.includes(csite)) { + this.colony.overlords.work.constructionSites.push(csite); + return; + } + }) + } } // Remove if owned by other player if (Game.time % 10 == 2 && this.room && !!this.room.owner && this.room.owner != MY_USERNAME) { - log.notify(`Removing roomPoising directive in ${this.pos.roomName}: room already owned by another player.`); + log.notify(`Removing poisonRoom directive in ${this.pos.roomName}: room already owned by another player.`); this.remove(); } } From 97f44dca9b332a6eb8d87f76ca36386093883236 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 20:06:28 -0700 Subject: [PATCH 082/137] Adding nuker to hauling --- src/directives/colony/clearRoom.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 9fea647d1..f237db05b 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -63,6 +63,9 @@ export class DirectiveClearRoom extends Directive { DirectiveHaul.create(s.pos); continue; } + if (s.structureType == STRUCTURE_NUKER && s.energy > 50000) { + DirectiveHaul.create(s.pos); + } if (keepRoads && s.structureType == STRUCTURE_ROAD) { continue; } From c1d87981ac5ae7b775ee6d2d12a8773e973b97a7 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 20:08:37 -0700 Subject: [PATCH 083/137] Fixing ! --- src/directives/colony/clearRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index f237db05b..0d6ec1848 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -58,7 +58,7 @@ export class DirectiveClearRoom extends Directive { for (const s of allStructures) { if (s.structureType == STRUCTURE_CONTROLLER) continue; if (keepStorageStructures && - (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && ~s.isEmpty) { + (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && !s.isEmpty) { // Create a collection flag DirectiveHaul.create(s.pos); continue; From c3f0e33a1639b26546062f33578d5246ab0cf639 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 20:22:47 -0700 Subject: [PATCH 084/137] Preventing removing directive if room not unclaimed and createIfPresent for hauling --- src/directives/colony/clearRoom.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 0d6ec1848..3db78368a 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -29,7 +29,7 @@ export class DirectiveClearRoom extends Directive { // Remove if misplaced if (Cartographer.roomType(this.pos.roomName) != ROOMTYPE_CONTROLLER) { log.warning(`${this.print}: ${printRoomName(this.pos.roomName)} is not a controller room; ` + - `removing directive!`); + `removing directive!`); this.remove(true); } if (Memory.settings.resourceCollectionMode && Memory.settings.resourceCollectionMode >= 1) { @@ -48,7 +48,7 @@ export class DirectiveClearRoom extends Directive { private removeAllStructures(): boolean { const keepStorageStructures = this.memory.keepStorageStructures !== undefined - ? this.memory.keepStorageStructures : true; + ? this.memory.keepStorageStructures : true; const keepRoads = this.memory.keepRoads !== undefined ? this.memory.keepRoads : true; const keepContainers = this.memory.keepContainers !== undefined ? this.memory.keepContainers : true; @@ -60,11 +60,11 @@ export class DirectiveClearRoom extends Directive { if (keepStorageStructures && (s.structureType == STRUCTURE_STORAGE || s.structureType == STRUCTURE_TERMINAL) && !s.isEmpty) { // Create a collection flag - DirectiveHaul.create(s.pos); + DirectiveHaul.createIfNotPresent(s.pos, 'pos'); continue; } if (s.structureType == STRUCTURE_NUKER && s.energy > 50000) { - DirectiveHaul.create(s.pos); + DirectiveHaul.createIfNotPresent(s.pos, 'pos'); } if (keepRoads && s.structureType == STRUCTURE_ROAD) { continue; @@ -90,11 +90,13 @@ export class DirectiveClearRoom extends Directive { if (this.room && this.room.my) { const done = this.removeAllStructures(); if (done) { - this.room.controller!.unclaim(); + let res = this.room.controller!.unclaim(); log.notify(`Removing clearRoom directive in ${this.pos.roomName}: operation completed.`); - this.remove(); + if (res == OK) { + this.remove(); + } } - // Clear path if controller is not reachable + // Clear path if controller is not reachable } else if (this.room && this.room.creeps.length > 1) { let currentlyDismantlingLocations = DirectiveDismantle.find(this.room.flags); From 7a5454338ddf537abfee2d3a68ba7c820c1cfa2b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 21:12:08 -0700 Subject: [PATCH 085/137] Adding fill nuker only when off cd --- src/hiveClusters/commandCenter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hiveClusters/commandCenter.ts b/src/hiveClusters/commandCenter.ts index 51e0c07ef..f593f8f5d 100644 --- a/src/hiveClusters/commandCenter.ts +++ b/src/hiveClusters/commandCenter.ts @@ -140,7 +140,7 @@ export class CommandCenter extends HiveCluster { } // Refill nuker with low priority if (this.nuker) { - if (this.nuker.energy < this.nuker.energyCapacity && this.storage.energy > 200000) { + if (this.nuker.energy < this.nuker.energyCapacity && this.storage.energy > 200000 && this.nuker.cooldown <= 1000) { this.transportRequests.requestInput(this.nuker, Priority.Low); } if (this.nuker.ghodium < this.nuker.ghodiumCapacity From b21fb41dc418300a89c6dcb3539c4fefd76aa7e3 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 21:39:33 -0700 Subject: [PATCH 086/137] Reducing number of nuke protected locations, won't protect roads etc but also by default will now not reinforce extensions --- src/directives/situational/nukeResponse.ts | 19 +++++++++++++++---- src/overlords/core/worker.ts | 3 ++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/directives/situational/nukeResponse.ts b/src/directives/situational/nukeResponse.ts index ccdbe6494..bd592d849 100644 --- a/src/directives/situational/nukeResponse.ts +++ b/src/directives/situational/nukeResponse.ts @@ -41,16 +41,27 @@ export class DirectiveNukeResponse extends Directive { } } + /** + * Returns whether a position should be reinforced or not + * @param pos + * @param ignoreExtensions + */ + static shouldReinforceLocation(pos: RoomPosition, ignoreExtensions = true) { + let dontReinforce: StructureConstant[] = [STRUCTURE_ROAD, STRUCTURE_RAMPART, STRUCTURE_WALL]; + if (ignoreExtensions) { dontReinforce.push(STRUCTURE_EXTENSION);} + return _.filter(pos.lookFor(LOOK_STRUCTURES), + s => !_.contains(dontReinforce, s.structureType)).length > 0; + } + run(): void { // Build ramparts at all positions affected by nukes with structures on them if (Game.time % 50 == 0) { if (this.nuke) { const rampartPositions = _.filter(this.nuke.pos.getPositionsInRange(2), function(pos) { // Rampart should be built to protect all non-road, non-barrier structures in nuke range - return _.filter(pos.lookFor(LOOK_STRUCTURES), - s => s.structureType != STRUCTURE_ROAD && - s.structureType != STRUCTURE_RAMPART && - s.structureType != STRUCTURE_WALL).length > 0; + return DirectiveNukeResponse.shouldReinforceLocation(pos, + // only reinforce extensions if room has spare energy + pos.room && pos.room.storage && pos.room.storage.energy > 500000); }); for (const pos of rampartPositions) { // Build a rampart if there isn't one already diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index 95f85fff3..7c4d95f69 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -12,6 +12,7 @@ import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; import {minBy} from '../../utilities/utils'; import {Zerg} from '../../zerg/Zerg'; import {Overlord, ZergOptions} from '../Overlord'; +import {DirectiveNukeResponse} from "../../directives/situational/nukeResponse"; /** * Spawns general-purpose workers, which maintain a colony, performing actions such as building, repairing, fortifying, @@ -108,7 +109,7 @@ export class WorkerOverlord extends Overlord { if (this.room.find(FIND_NUKES).length > 0) { for (const rampart of this.colony.room.ramparts) { const neededHits = this.neededRampartHits(rampart); - if (rampart.hits < neededHits) { + if (rampart.hits < neededHits && DirectiveNukeResponse.shouldReinforceLocation(rampart.pos)) { this.nukeDefenseRamparts.push(rampart); this.nukeDefenseHitsRemaining[rampart.id] = neededHits - rampart.hits; } From 9937da7ca0747807e8b2b3a5e96ab61405acdc90 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 21:47:08 -0700 Subject: [PATCH 087/137] Reducing to in range2 based on nukes having a 5x5 area --- src/overlords/core/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index 7c4d95f69..2de0382ba 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -133,7 +133,7 @@ export class WorkerOverlord extends Overlord { for (const nuke of rampart.pos.lookFor(LOOK_NUKES)) { neededHits += 10e6; } - for (const nuke of rampart.pos.findInRange(FIND_NUKES, 3)) { + for (const nuke of rampart.pos.findInRange(FIND_NUKES, 2)) { if (nuke.pos != rampart.pos) { neededHits += 5e6; } From 312eceeac93bdb9853ef25d9c57f2c79f3afb496 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 22:08:20 -0700 Subject: [PATCH 088/137] This doesn't work, but should only be megareinforcing the nuked ramparts. Right now it reinforces everything --- src/overlords/core/worker.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index 2de0382ba..81c5d516e 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -13,6 +13,7 @@ import {minBy} from '../../utilities/utils'; import {Zerg} from '../../zerg/Zerg'; import {Overlord, ZergOptions} from '../Overlord'; import {DirectiveNukeResponse} from "../../directives/situational/nukeResponse"; +import {Visualizer} from "../../visuals/Visualizer"; /** * Spawns general-purpose workers, which maintain a colony, performing actions such as building, repairing, fortifying, @@ -109,8 +110,10 @@ export class WorkerOverlord extends Overlord { if (this.room.find(FIND_NUKES).length > 0) { for (const rampart of this.colony.room.ramparts) { const neededHits = this.neededRampartHits(rampart); - if (rampart.hits < neededHits && DirectiveNukeResponse.shouldReinforceLocation(rampart.pos)) { + if (rampart.hits < neededHits && rampart.pos.findInRange(FIND_NUKES, 3) + && DirectiveNukeResponse.shouldReinforceLocation(rampart.pos)) { this.nukeDefenseRamparts.push(rampart); + Visualizer.marker(rampart.pos, {color: 'gold'}); this.nukeDefenseHitsRemaining[rampart.id] = neededHits - rampart.hits; } } From 1861bae966c290b2495174630b027f41ab4f9266 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Sun, 26 May 2019 22:16:23 -0700 Subject: [PATCH 089/137] Forgot the length param... --- src/overlords/core/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index 81c5d516e..c7bd80955 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -110,7 +110,7 @@ export class WorkerOverlord extends Overlord { if (this.room.find(FIND_NUKES).length > 0) { for (const rampart of this.colony.room.ramparts) { const neededHits = this.neededRampartHits(rampart); - if (rampart.hits < neededHits && rampart.pos.findInRange(FIND_NUKES, 3) + if (rampart.hits < neededHits && rampart.pos.findInRange(FIND_NUKES, 3).length > 0 && DirectiveNukeResponse.shouldReinforceLocation(rampart.pos)) { this.nukeDefenseRamparts.push(rampart); Visualizer.marker(rampart.pos, {color: 'gold'}); From 15797902ef03c678a091fc85fe204b883a225a33 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 27 May 2019 14:58:53 -0700 Subject: [PATCH 090/137] Just for fun, a distraction Overlord for making creeps that just run away laughing --- src/creepSetups/setups.ts | 5 +++ src/overlords/defense/distraction.ts | 55 ++++++++++++++++++++++++++++ src/zerg/CombatZerg.ts | 8 ++-- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/overlords/defense/distraction.ts diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 1c57dcbee..98638a9c4 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -229,6 +229,11 @@ export const CombatSetups = { sizeLimit: Infinity, }), + distraction: new CreepSetup(Roles.ranged, { + pattern : [MOVE, MOVE, MOVE, RANGED_ATTACK, MOVE], + sizeLimit: 1, + }), + default: new CreepSetup(Roles.ranged, { pattern : [RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, MOVE, MOVE, MOVE, MOVE, HEAL], sizeLimit: Infinity, diff --git a/src/overlords/defense/distraction.ts b/src/overlords/defense/distraction.ts new file mode 100644 index 000000000..171646d42 --- /dev/null +++ b/src/overlords/defense/distraction.ts @@ -0,0 +1,55 @@ +import {CreepSetup} from '../../creepSetups/CreepSetup'; +import {CombatSetups, Roles} from '../../creepSetups/setups'; +import {DirectiveInvasionDefense} from '../../directives/defense/invasionDefense'; +import {CombatIntel} from '../../intel/CombatIntel'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {profile} from '../../profiler/decorator'; +import {boostResources} from '../../resources/map_resources'; +import {CombatZerg} from '../../zerg/CombatZerg'; +import {CombatOverlord} from '../CombatOverlord'; + +/** + * 5 Move 1 RA creep that avoids all enemies and distracts attackers. + * Just for fun + * TODO: Make them prefer swamps when at max hp + */ +@profile +export class DistractionOverlord extends CombatOverlord { + + distraction: CombatZerg[]; + room: Room; + + static settings = { + retreatHitsPercent : 0.85, + reengageHitsPercent: 0.95, + }; + + constructor(directive: DirectiveInvasionDefense, + boosted = false, + priority = OverlordPriority.defense.rangedDefense) { + super(directive, 'distraction', priority, 1); + this.distraction = this.combatZerg(Roles.ranged); + } + + private handleDistraction(distraction: CombatZerg): void { + if (this.room.hostiles.length > 0) { + distraction.autoCombat(this.room.name, false, 5); + this.taunt(distraction, this.room.hostiles[0].owner.username); + } + } + + taunt(distraction: CombatZerg, name?: string) { + const taunts: string[] = ['Heylisten!', 'Pssssst', 'Catch Me!', `Hi ${name || ''}`, '🍑🍑🍑', '🏎️ VROOM']; + distraction.sayRandom(taunts, true); + } + + init() { + this.reassignIdleCreeps(Roles.ranged); + const setup = CombatSetups.hydralisks.distraction; + this.wishlist(1, setup); + } + + run() { + this.autoRun(this.distraction, distraction => this.handleDistraction(distraction)); + } +} diff --git a/src/zerg/CombatZerg.ts b/src/zerg/CombatZerg.ts index 567219dfc..92f3b4786 100644 --- a/src/zerg/CombatZerg.ts +++ b/src/zerg/CombatZerg.ts @@ -227,7 +227,7 @@ export class CombatZerg extends Zerg { /** * Navigate to a room, then engage hostile creeps there, perform medic actions, etc. */ - autoCombat(roomName: string, verbose = false) { + autoCombat(roomName: string, verbose = false, preferredRange?: number) { // Do standard melee, ranged, and heal actions if (this.getActiveBodyparts(ATTACK) > 0) { @@ -255,7 +255,7 @@ export class CombatZerg extends Zerg { // Fight within the room const target = CombatTargeting.findTarget(this); const preferRanged = this.getActiveBodyparts(RANGED_ATTACK) > this.getActiveBodyparts(ATTACK); - const targetRange = preferRanged ? 3 : 1; + const targetRange = preferredRange || preferRanged ? 3 : 1; this.debug(`${target}, ${targetRange}`); if (target) { const avoid = []; @@ -263,10 +263,10 @@ export class CombatZerg extends Zerg { if (preferRanged) { const meleeHostiles = _.filter(this.room.hostiles, h => CombatIntel.getAttackDamage(h) > 0); for (const hostile of meleeHostiles) { - avoid.push({pos: hostile.pos, range: 2}); + avoid.push({pos: hostile.pos, range: targetRange - 1}); } } - return Movement.combatMove(this, [{pos: target.pos, range: targetRange}], []); + return Movement.combatMove(this, [{pos: target.pos, range: targetRange}], avoid); } } From 3b0ce1826a3919edff19bbe026c563ed0fccc1c8 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Sat, 1 Jun 2019 17:02:15 +0300 Subject: [PATCH 091/137] fixed alert notification --- src/directives/offense/poisonRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index f598d6025..86367f177 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -39,7 +39,7 @@ export class DirectivePoisonRoom extends Directive { } init() { - this.alert(`Poisining Room ${printRoomName(this.pos.roomName)} `); + this.alert(`Poisining Room ${this.pos.roomName}`); } private isPoisoned(): boolean { From 72f9986392397442b90c9e4c5b14917fbfffb3df Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Sat, 1 Jun 2019 18:36:52 +0300 Subject: [PATCH 092/137] denied creation of any other diretives in the room --- src/directives/Directive.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/directives/Directive.ts b/src/directives/Directive.ts index 534e6bb0a..76eca9c63 100644 --- a/src/directives/Directive.ts +++ b/src/directives/Directive.ts @@ -5,6 +5,7 @@ import {Overlord} from '../overlords/Overlord'; import {profile} from '../profiler/decorator'; import {equalXYR, randomHex} from '../utilities/utils'; import {NotifierPriority} from './Notifier'; +import {DirectivePoisonRoom} from '../directives/offense/poisonRoom'; interface DirectiveCreationOptions { memory?: FlagMemory; @@ -310,9 +311,10 @@ export abstract class Directive { * Calling this method on positions in invisible rooms can be expensive and should be used sparingly. */ static createIfNotPresent(pos: RoomPosition, scope: 'room' | 'pos', opts: DirectiveCreationOptions = {}): number | string | undefined { - if (this.isPresent(pos, scope)) { + if (this.isPresent(pos, scope) || !DirectivePoisonRoom.isPresent(pos, scope)) { return; // do nothing if flag is already here } + const room = Game.rooms[pos.roomName] as Room | undefined; if (!room) { if (!opts.memory) { From f679b47f7ddc117b2b7bb00817450218b311e1d7 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Sun, 2 Jun 2019 23:47:30 +0300 Subject: [PATCH 093/137] added RoomPoisonerOverlord --- src/creepSetups/setups.ts | 8 ++- src/directives/Directive.ts | 2 +- src/directives/offense/poisonRoom.ts | 24 ++------- src/overlords/offense/roomPoisoner.ts | 72 ++++++++++++++++++++++++++ src/priorities/priorities_overlords.ts | 3 +- 5 files changed, 86 insertions(+), 23 deletions(-) create mode 100644 src/overlords/offense/roomPoisoner.ts diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 1c57dcbee..f596e8ecc 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -22,6 +22,7 @@ export const Roles = { ranged : 'hydralisk', healer : 'transfuser', dismantler: 'lurker', + roomPoisoner: 'roomPoisoner', }; /** @@ -177,7 +178,12 @@ export const Setups = { sizeLimit: 5, }), - } + }, + + roomPoisoner: new CreepSetup(Roles.roomPoisoner, { + pattern : [WORK, CARRY, MOVE, MOVE], + sizeLimit: 4, + }), }; diff --git a/src/directives/Directive.ts b/src/directives/Directive.ts index 76eca9c63..8b99b76c6 100644 --- a/src/directives/Directive.ts +++ b/src/directives/Directive.ts @@ -311,7 +311,7 @@ export abstract class Directive { * Calling this method on positions in invisible rooms can be expensive and should be used sparingly. */ static createIfNotPresent(pos: RoomPosition, scope: 'room' | 'pos', opts: DirectiveCreationOptions = {}): number | string | undefined { - if (this.isPresent(pos, scope) || !DirectivePoisonRoom.isPresent(pos, scope)) { + if (this.isPresent(pos, scope) || DirectivePoisonRoom.isPresent(pos, 'room')) { return; // do nothing if flag is already here } diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 86367f177..b0860b3a5 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -1,5 +1,5 @@ import {ClaimingOverlord} from '../../overlords/colonization/claimer'; -import {WorkerOverlord} from '../../overlords/core/worker'; +import {RoomPoisonerOverlord} from '../../overlords/offense/roomPoisoner'; import {log} from '../../console/log'; import {profile} from '../../profiler/decorator'; import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; @@ -20,7 +20,7 @@ export class DirectivePoisonRoom extends Directive { overlords: { claim: ClaimingOverlord; - work: WorkerOverlord; + roomPoisoner: RoomPoisonerOverlord; }; constructor(flag: Flag) { @@ -35,7 +35,7 @@ export class DirectivePoisonRoom extends Directive { spawnMoarOverlords() { this.overlords.claim = new ClaimingOverlord(this); - this.overlords.work = new WorkerOverlord(this.colony); + this.overlords.roomPoisoner = new RoomPoisonerOverlord(this); } init() { @@ -66,13 +66,6 @@ export class DirectivePoisonRoom extends Directive { } } - private getWallsConstructionSites() { - const room = Game.rooms[this.pos.roomName]; - const constructionSites = room.find(FIND_CONSTRUCTION_SITES); - const wallsConstructionSites = _.filter(constructionSites, s => s.structureType == STRUCTURE_WALL); - return wallsConstructionSites; - } - run() { if (this.room && this.room.my) { @@ -81,16 +74,7 @@ export class DirectivePoisonRoom extends Directive { this.room.controller!.unclaim(); log.notify(`Removing poisonRoom directive in ${this.pos.roomName}: operation completed.`); this.remove(); - } else { - //Assign workers to wall sources and controller - const wallsConstructionSites = this.getWallsConstructionSites(); - _.forEach(wallsConstructionSites,csite => { - if (!this.colony.overlords.work.constructionSites.includes(csite)) { - this.colony.overlords.work.constructionSites.push(csite); - return; - } - }) - } + } } // Remove if owned by other player diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts new file mode 100644 index 000000000..6c01a1f51 --- /dev/null +++ b/src/overlords/offense/roomPoisoner.ts @@ -0,0 +1,72 @@ +import {log} from '../../console/log'; +import {Roles, Setups} from '../../creepSetups/setups'; +import {DirectivePoisonRoom} from '../../directives/offense/poisonRoom'; +import {Pathing} from '../../movement/Pathing'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {profile} from '../../profiler/decorator'; +import {Tasks} from '../../tasks/Tasks'; +import {Zerg} from '../../zerg/Zerg'; +import {Overlord} from '../Overlord'; + +/** + * Spawn roomPoisoner - upgrqde controller to lvl2, wall in controller then sources. + */ +@profile +export class RoomPoisonerOverlord extends Overlord { + + roomPoisoners: Zerg[]; + controllerWallSites: ConstructionSite[] | undefined; + sourcesWallSites: ConstructionSite[] | undefined; + + constructor(directive: DirectivePoisonRoom, priority = OverlordPriority.offense.roomPoisoner) { + super(directive, 'roomPoisoner', priority); + this.roomPoisoners = this.zerg(Roles.roomPoisoner); + this.controllerWallSites = (this.room && this.room.controller) ? _.filter(this.room.constructionSites, + s => s.structureType == STRUCTURE_WALL && + s.pos.isNearTo(this.room!.controller!.pos)) : undefined; + this.sourcesWallSites = (this.room && this.room.controller) ? _.filter(this.room.constructionSites, + s => s.structureType == STRUCTURE_WALL && + !s.pos.isNearTo(this.room!.controller!.pos)) : undefined; + } + + refresh() { + super.refresh(); + this.controllerWallSites = (this.room && this.room.controller) ? _.filter(this.room.constructionSites, + s => s.structureType == STRUCTURE_WALL && + s.pos.isNearTo(this.room!.controller!.pos)) : undefined; + this.sourcesWallSites = (this.room && this.room.controller) ? _.filter(this.room.constructionSites, + s => s.structureType == STRUCTURE_WALL && + !s.pos.isNearTo(this.room!.controller!.pos)) : undefined; + } + + init() { + this.wishlist(1, Setups.roomPoisoner); + } + + private handleRoomPoisoner(roomPoisoner: Zerg): void { + // Ensure you are in the assigned room + if (roomPoisoner.room == this.room && !roomPoisoner.pos.isEdge) { + // Build and recharge + if (roomPoisoner.carry.energy == 0) { + roomPoisoner.task = Tasks.recharge(); + } else if (this.room && this.room.controller && + (this.room.controller.level < 2) && + !(this.room.controller.upgradeBlocked > 0)) { + // upgrade controller to level 2 to unlock walls + roomPoisoner.task = Tasks.upgrade(this.room.controller); + } else if (this.controllerWallSites) { + roomPoisoner.task = Tasks.build(this.controllerWallSites[0]); + } else if (this.sourcesWallSites) { + roomPoisoner.task = Tasks.build(this.sourcesWallSites[0]); + } + } else { + // pioneer.task = Tasks.goTo(this.pos); + roomPoisoner.goTo(this.pos, {ensurePath: true, avoidSK: true}); + } + } + + run() { + this.autoRun(this.roomPoisoners, roomPoisoner => this.handleRoomPoisoner(roomPoisoner)); + } +} + diff --git a/src/priorities/priorities_overlords.ts b/src/priorities/priorities_overlords.ts index bef3ab436..5d70cdd3d 100644 --- a/src/priorities/priorities_overlords.ts +++ b/src/priorities/priorities_overlords.ts @@ -22,7 +22,8 @@ export let OverlordPriority = { destroy : 300, healPoint : 301, siege : 302, - controllerAttack: 399 + controllerAttack: 388, + roomPoisoner : 399, }, colonization: { // Colonizing new rooms From 9c1218827364824ba864376cb3acb73331b6c025 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 02:09:43 +0300 Subject: [PATCH 094/137] fixed wall cite not walkable issue --- src/directives/offense/poisonRoom.ts | 2 +- src/overlords/offense/roomPoisoner.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index b0860b3a5..56c60b883 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -43,7 +43,7 @@ export class DirectivePoisonRoom extends Directive { } private isPoisoned(): boolean { - if (this.room) { + if (Game.time % 25 && this.room && this.room.controller!.level > 1) { const allSources = this.room.find(FIND_SOURCES); let result = true; //Check for walkable source.pos.neighbors and place wall constuction site diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index 6c01a1f51..be6ca6fd0 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -48,6 +48,11 @@ export class RoomPoisonerOverlord extends Overlord { if (roomPoisoner.room == this.room && !roomPoisoner.pos.isEdge) { // Build and recharge if (roomPoisoner.carry.energy == 0) { + //Csites are not walkable, remove all sources wall csites to avoid being blocked from harvesting sources + //the direcrive will try recreate them every 25 ticks + if(this.sourcesWallSites){ + _.forEach(this.sourcesWallSites, csite => {csite.remove();} ); + } roomPoisoner.task = Tasks.recharge(); } else if (this.room && this.room.controller && (this.room.controller.level < 2) && From a4f355644f0468ec258d8b6b657b307e2dad47a5 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 02:40:09 +0300 Subject: [PATCH 095/137] force claimer suicide --- src/directives/offense/poisonRoom.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 56c60b883..b42d4eb6e 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -69,6 +69,10 @@ export class DirectivePoisonRoom extends Directive { run() { if (this.room && this.room.my) { + //kill claimer if room claimed, it is can be blocking wall csite creation + if (this.overlords.claim.claimers.length){ + this.overlords.claim.claimers[0].suicide(); + } // Remove if poisoned if (this.isPoisoned()) { this.room.controller!.unclaim(); From dbb0acdb4659b1b9ba0d639953c08f8d09d73d55 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 02:59:36 +0300 Subject: [PATCH 096/137] fix target for tasks.build --- src/overlords/offense/roomPoisoner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index be6ca6fd0..4fd55867f 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -59,9 +59,9 @@ export class RoomPoisonerOverlord extends Overlord { !(this.room.controller.upgradeBlocked > 0)) { // upgrade controller to level 2 to unlock walls roomPoisoner.task = Tasks.upgrade(this.room.controller); - } else if (this.controllerWallSites) { + } else if (this.controllerWallSites && this.controllerWallSites[0]) { roomPoisoner.task = Tasks.build(this.controllerWallSites[0]); - } else if (this.sourcesWallSites) { + } else if (this.sourcesWallSites && this.sourcesWallSites[0]) { roomPoisoner.task = Tasks.build(this.sourcesWallSites[0]); } } else { From 475a00ce90e25aaca1d5611d3f2165d32965a8a2 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 04:19:00 +0300 Subject: [PATCH 097/137] directive remove csite instead of creep --- src/directives/offense/poisonRoom.ts | 47 +++++++++++++++++---------- src/overlords/offense/roomPoisoner.ts | 9 ++--- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index b42d4eb6e..30fe570e1 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -17,6 +17,8 @@ export class DirectivePoisonRoom extends Directive { static directiveName = 'poisonRoom'; static color = COLOR_RED; static secondaryColor = COLOR_BROWN; + static requiredRCL = 4; + static poisonSourcesOnly = false; overlords: { claim: ClaimingOverlord; @@ -24,7 +26,7 @@ export class DirectivePoisonRoom extends Directive { }; constructor(flag: Flag) { - super(flag, colony => colony.level >= 4); + super(flag, colony => colony.level >= DirectivePoisonRoom.requiredRCL); // Remove if misplaced if (Cartographer.roomType(this.pos.roomName) != ROOMTYPE_CONTROLLER) { log.warning(`${this.print}: ${printRoomName(this.pos.roomName)} is not a controller room; ` + @@ -43,23 +45,34 @@ export class DirectivePoisonRoom extends Directive { } private isPoisoned(): boolean { + let result = true; if (Game.time % 25 && this.room && this.room.controller!.level > 1) { - const allSources = this.room.find(FIND_SOURCES); - let result = true; - //Check for walkable source.pos.neighbors and place wall constuction site - for (const s of allSources) { - let walkableSourcePosisions = _.filter(s.pos.neighbors, pos => pos.isWalkable()); - if(walkableSourcePosisions.length){ - _.forEach(walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); - } - result = result && !walkableSourcePosisions.length; - } - //Check for walkable controller.pos.neighbors and place wall constuction site - let walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); - if(walkableControllerPosisions.length){ - _.forEach(walkableControllerPosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); - } - result = result && !walkableControllerPosisions.length; + //wall csites are not walkable, block sources only if roomPoisoner.carry.energy > 0 + if (this.overlords.roomPoisoner.roomPoisoners.length && + this.overlords.roomPoisoner.roomPoisoners[0].carry.energy > 0){ + const allSources = this.room.find(FIND_SOURCES); + //Check for walkable source.pos.neighbors and place wall constuction site + for (const s of allSources) { + let walkableSourcePosisions = _.filter(s.pos.neighbors, pos => pos.isWalkable()); + if(!!walkableSourcePosisions.length){ + _.forEach(walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); + } + result = result && !walkableSourcePosisions.length; + } + } else { + //remove all csites if roomPoisoner.carry.energy == 0, wall csites are not walkable + if(this.room){ + _.forEach(this.room.constructionSites, csite => {csite.remove();} ); + } + } + if(!DirectivePoisonRoom.poisonSourcesOnly){ + //Check for walkable controller.pos.neighbors and place wall constuction site + let walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); + if(!!walkableControllerPosisions.length){ + _.forEach(walkableControllerPosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); + } + result = result && !walkableControllerPosisions.length; + } return result; } else { return false; diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index 4fd55867f..41317a6ac 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -48,20 +48,15 @@ export class RoomPoisonerOverlord extends Overlord { if (roomPoisoner.room == this.room && !roomPoisoner.pos.isEdge) { // Build and recharge if (roomPoisoner.carry.energy == 0) { - //Csites are not walkable, remove all sources wall csites to avoid being blocked from harvesting sources - //the direcrive will try recreate them every 25 ticks - if(this.sourcesWallSites){ - _.forEach(this.sourcesWallSites, csite => {csite.remove();} ); - } roomPoisoner.task = Tasks.recharge(); } else if (this.room && this.room.controller && (this.room.controller.level < 2) && !(this.room.controller.upgradeBlocked > 0)) { // upgrade controller to level 2 to unlock walls roomPoisoner.task = Tasks.upgrade(this.room.controller); - } else if (this.controllerWallSites && this.controllerWallSites[0]) { + } else if (this.controllerWallSites && this.controllerWallSites.length) { roomPoisoner.task = Tasks.build(this.controllerWallSites[0]); - } else if (this.sourcesWallSites && this.sourcesWallSites[0]) { + } else if (this.sourcesWallSites && this.sourcesWallSites.length) { roomPoisoner.task = Tasks.build(this.sourcesWallSites[0]); } } else { From fe3521d0640a0fb924e5c3e35da8c6bce73b6e91 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 05:30:40 +0300 Subject: [PATCH 098/137] remove containers --- src/directives/offense/poisonRoom.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 30fe570e1..7909de7e9 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -86,6 +86,10 @@ export class DirectivePoisonRoom extends Directive { if (this.overlords.claim.claimers.length){ this.overlords.claim.claimers[0].suicide(); } + //remove any containers that can be next to sources + if(this.room.containers.length){ + _.forEach(this.room.containers, container => {container.destroy();}); + } // Remove if poisoned if (this.isPoisoned()) { this.room.controller!.unclaim(); From 1633e5d168cd6ae72478191dd3eebdfbd146674b Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 06:20:52 +0300 Subject: [PATCH 099/137] Fixed COLOR mismatch --- src/Colony.ts | 3 ++- src/directives/Directive.ts | 3 +-- src/directives/initializer.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Colony.ts b/src/Colony.ts index c23be5588..f6697e9ab 100644 --- a/src/Colony.ts +++ b/src/Colony.ts @@ -3,6 +3,7 @@ import {$} from './caching/GlobalCache'; import {log} from './console/log'; import {StoreStructure} from './declarations/typeGuards'; import {DirectiveExtract} from './directives/resource/extract'; +import {DirectivePoisonRoom} from './directives/offense/poisonRoom'; import {_HARVEST_MEM_DOWNTIME, _HARVEST_MEM_USAGE, DirectiveHarvest} from './directives/resource/harvest'; import {HiveCluster} from './hiveClusters/_HiveCluster'; import {CommandCenter} from './hiveClusters/commandCenter'; @@ -311,7 +312,7 @@ export class Colony { $.set(this, 'sources', () => _.sortBy(_.flatten(_.map(this.rooms, room => room.sources)), source => source.pos.getMultiRoomRangeTo(this.pos))); for (const source of this.sources) { - DirectiveHarvest.createIfNotPresent(source.pos, 'pos'); + DirectivePoisonRoom.isPresent(source.pos, 'room') && DirectiveHarvest.createIfNotPresent(source.pos, 'pos'); } $.set(this, 'extractors', () => _(this.rooms) diff --git a/src/directives/Directive.ts b/src/directives/Directive.ts index 8b99b76c6..894d0079d 100644 --- a/src/directives/Directive.ts +++ b/src/directives/Directive.ts @@ -5,7 +5,6 @@ import {Overlord} from '../overlords/Overlord'; import {profile} from '../profiler/decorator'; import {equalXYR, randomHex} from '../utilities/utils'; import {NotifierPriority} from './Notifier'; -import {DirectivePoisonRoom} from '../directives/offense/poisonRoom'; interface DirectiveCreationOptions { memory?: FlagMemory; @@ -311,7 +310,7 @@ export abstract class Directive { * Calling this method on positions in invisible rooms can be expensive and should be used sparingly. */ static createIfNotPresent(pos: RoomPosition, scope: 'room' | 'pos', opts: DirectiveCreationOptions = {}): number | string | undefined { - if (this.isPresent(pos, scope) || DirectivePoisonRoom.isPresent(pos, 'room')) { + if (this.isPresent(pos, scope)) { return; // do nothing if flag is already here } diff --git a/src/directives/initializer.ts b/src/directives/initializer.ts index a13ec5521..120fd2432 100644 --- a/src/directives/initializer.ts +++ b/src/directives/initializer.ts @@ -59,7 +59,7 @@ export function DirectiveWrapper(flag: Flag): Directive | undefined { return new DirectivePairDestroy(flag); case COLOR_PURPLE: return new DirectiveControllerAttack(flag); - case COLOR_ORANGE: + case COLOR_BROWN: return new DirectivePoisonRoom(flag); } break; From cf9393ea5f0d3443517c7337d067822ae2454dbe Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 07:20:09 +0300 Subject: [PATCH 100/137] patch corner cases --- src/overlords/offense/roomPoisoner.ts | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index 41317a6ac..0f153a9db 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -42,10 +42,40 @@ export class RoomPoisonerOverlord extends Overlord { init() { this.wishlist(1, Setups.roomPoisoner); } + private findStructureBlockingController(pioneer: Zerg): Structure | undefined { + const blockingPos = Pathing.findBlockingPos(pioneer.pos, pioneer.room.controller!.pos, + _.filter(pioneer.room.structures, s => !s.isWalkable)); + if (blockingPos) { + const structure = blockingPos.lookFor(LOOK_STRUCTURES)[0]; + if (structure) { + return structure; + } else { + log.error(`${this.print}: no structure at blocking pos ${blockingPos.print}! (Why?)`); + } + } + } private handleRoomPoisoner(roomPoisoner: Zerg): void { // Ensure you are in the assigned room if (roomPoisoner.room == this.room && !roomPoisoner.pos.isEdge) { + + //stomp enemy csites (not sure if I can just .remove() them after owning the room) + const enemyConstructionSites = roomPoisoner.room.find(FIND_HOSTILE_CONSTRUCTION_SITES); + if (enemyConstructionSites.length > 0 && enemyConstructionSites[0].pos.isWalkable(true)) { + roomPoisoner.goTo(enemyConstructionSites[0].pos); + return; + } + + // Remove any blocking structures preventing claimer from reaching controller + // assuming it will not be unlocked after successfull room poisoning (directive will be auto remvoed) + if (!this.room.my && this.room.structures.length > 0) { + const dismantleTarget = this.findStructureBlockingController(roomPoisoner); + if (dismantleTarget) { + roomPoisoner.task = Tasks.dismantle(dismantleTarget); + return; + } + } + // Build and recharge if (roomPoisoner.carry.energy == 0) { roomPoisoner.task = Tasks.recharge(); From b0ce31b13a4ccb2bcbd80b0e827ab0153cb0d332 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 07:24:38 +0300 Subject: [PATCH 101/137] patch corner cases2 --- src/Colony.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Colony.ts b/src/Colony.ts index f6697e9ab..7d07bc91c 100644 --- a/src/Colony.ts +++ b/src/Colony.ts @@ -312,7 +312,7 @@ export class Colony { $.set(this, 'sources', () => _.sortBy(_.flatten(_.map(this.rooms, room => room.sources)), source => source.pos.getMultiRoomRangeTo(this.pos))); for (const source of this.sources) { - DirectivePoisonRoom.isPresent(source.pos, 'room') && DirectiveHarvest.createIfNotPresent(source.pos, 'pos'); + !DirectivePoisonRoom.isPresent(source.pos, 'room') && DirectiveHarvest.createIfNotPresent(source.pos, 'pos'); } $.set(this, 'extractors', () => _(this.rooms) From a3951ee73060285ad9c7c20ee4f6b944a7a68ec8 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 08:49:33 +0300 Subject: [PATCH 102/137] optimization --- src/directives/offense/poisonRoom.ts | 48 ++++++++++++++++++---------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 7909de7e9..1908a5b8d 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -20,6 +20,9 @@ export class DirectivePoisonRoom extends Directive { static requiredRCL = 4; static poisonSourcesOnly = false; + walkableSourcePosisions: RoomPosition[]; + walkableControllerPosisions: RoomPosition[]; + overlords: { claim: ClaimingOverlord; roomPoisoner: RoomPoisonerOverlord; @@ -41,23 +44,24 @@ export class DirectivePoisonRoom extends Directive { } init() { - this.alert(`Poisining Room ${this.pos.roomName}`); + this.alert(`Poisoning Room ${this.pos.roomName}`); + if(this.room && this.room.controller){ + for (const s of this.room.sources) { + this.walkableSourcePosisions = this.walkableSourcePosisions.concat( + _.filter(s.pos.neighbors, pos => pos.isWalkable())); + } + this.walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); + } } - private isPoisoned(): boolean { - let result = true; - if (Game.time % 25 && this.room && this.room.controller!.level > 1) { + private poison() { + if (this.room && this.room.controller!.level > 1) { //wall csites are not walkable, block sources only if roomPoisoner.carry.energy > 0 if (this.overlords.roomPoisoner.roomPoisoners.length && this.overlords.roomPoisoner.roomPoisoners[0].carry.energy > 0){ - const allSources = this.room.find(FIND_SOURCES); //Check for walkable source.pos.neighbors and place wall constuction site - for (const s of allSources) { - let walkableSourcePosisions = _.filter(s.pos.neighbors, pos => pos.isWalkable()); - if(!!walkableSourcePosisions.length){ - _.forEach(walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); - } - result = result && !walkableSourcePosisions.length; + if(this.walkableSourcePosisions.length){ + _.forEach(this.walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); } } else { //remove all csites if roomPoisoner.carry.energy == 0, wall csites are not walkable @@ -67,11 +71,19 @@ export class DirectivePoisonRoom extends Directive { } if(!DirectivePoisonRoom.poisonSourcesOnly){ //Check for walkable controller.pos.neighbors and place wall constuction site - let walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); - if(!!walkableControllerPosisions.length){ - _.forEach(walkableControllerPosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); + if(this.walkableControllerPosisions.length){ + _.forEach(this.walkableControllerPosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); } - result = result && !walkableControllerPosisions.length; + } + + } + } + private isPoisoned(): boolean { + let result = false; + if (this.room && this.room.controller!.level > 1) { + result = !this.walkableSourcePosisions.length; + if(!DirectivePoisonRoom.poisonSourcesOnly){ + result = result && !this.walkableControllerPosisions.length; } return result; } else { @@ -81,7 +93,7 @@ export class DirectivePoisonRoom extends Directive { run() { - if (this.room && this.room.my) { + if (Game.time % 25 && this.room && this.room.my) { //kill claimer if room claimed, it is can be blocking wall csite creation if (this.overlords.claim.claimers.length){ this.overlords.claim.claimers[0].suicide(); @@ -95,7 +107,9 @@ export class DirectivePoisonRoom extends Directive { this.room.controller!.unclaim(); log.notify(`Removing poisonRoom directive in ${this.pos.roomName}: operation completed.`); this.remove(); - } + } else { + this.poison(); + } } // Remove if owned by other player From b165a12ca36dbc765c5eebd3aede2f28057945c9 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 09:34:28 +0300 Subject: [PATCH 103/137] optimization2 --- src/directives/offense/poisonRoom.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 1908a5b8d..9f31bb62f 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -46,10 +46,7 @@ export class DirectivePoisonRoom extends Directive { init() { this.alert(`Poisoning Room ${this.pos.roomName}`); if(this.room && this.room.controller){ - for (const s of this.room.sources) { - this.walkableSourcePosisions = this.walkableSourcePosisions.concat( - _.filter(s.pos.neighbors, pos => pos.isWalkable())); - } + this.walkableSourcePosisions = _.filter(_.map(_.compact([...this.room.sources]), obj => obj!.pos),pos => pos.isWalkable()); this.walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); } } From 1d7ae99952e50269d04769dbb11dcada91c9f3e9 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 10:35:31 +0300 Subject: [PATCH 104/137] optimization3 --- src/directives/offense/poisonRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 9f31bb62f..ac12e9821 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -46,7 +46,7 @@ export class DirectivePoisonRoom extends Directive { init() { this.alert(`Poisoning Room ${this.pos.roomName}`); if(this.room && this.room.controller){ - this.walkableSourcePosisions = _.filter(_.map(_.compact([...this.room.sources]), obj => obj!.pos),pos => pos.isWalkable()); + this.walkableSourcePosisions = _.filter(_.flatten(_.map(this.room.sources, s => s.pos.neighbors)),pos => pos.isWalkable()); this.walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); } } From ca102eab04414b64aa9ef0904f8946a4da27f278 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 11:01:41 +0300 Subject: [PATCH 105/137] optimization4 --- src/directives/offense/poisonRoom.ts | 7 +++++++ src/overlords/offense/roomPoisoner.ts | 9 +-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index ac12e9821..fbb8d5025 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -99,6 +99,13 @@ export class DirectivePoisonRoom extends Directive { if(this.room.containers.length){ _.forEach(this.room.containers, container => {container.destroy();}); } + + //remove any hostile consituction sites + if(this.room.constructionSites.length){ + _.forEach(this.room.constructionSites, csite => { + !csite.my && csite.remove(); + }) + } // Remove if poisoned if (this.isPoisoned()) { this.room.controller!.unclaim(); diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index 0f153a9db..4d4c118a3 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -58,14 +58,7 @@ export class RoomPoisonerOverlord extends Overlord { private handleRoomPoisoner(roomPoisoner: Zerg): void { // Ensure you are in the assigned room if (roomPoisoner.room == this.room && !roomPoisoner.pos.isEdge) { - - //stomp enemy csites (not sure if I can just .remove() them after owning the room) - const enemyConstructionSites = roomPoisoner.room.find(FIND_HOSTILE_CONSTRUCTION_SITES); - if (enemyConstructionSites.length > 0 && enemyConstructionSites[0].pos.isWalkable(true)) { - roomPoisoner.goTo(enemyConstructionSites[0].pos); - return; - } - + // Remove any blocking structures preventing claimer from reaching controller // assuming it will not be unlocked after successfull room poisoning (directive will be auto remvoed) if (!this.room.my && this.room.structures.length > 0) { From c2ac121450053de656120d8aeac750be87f9f459 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 12:28:55 +0300 Subject: [PATCH 106/137] isWalkable ignore creeps --- src/directives/offense/poisonRoom.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index fbb8d5025..691bd7dcf 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -46,8 +46,8 @@ export class DirectivePoisonRoom extends Directive { init() { this.alert(`Poisoning Room ${this.pos.roomName}`); if(this.room && this.room.controller){ - this.walkableSourcePosisions = _.filter(_.flatten(_.map(this.room.sources, s => s.pos.neighbors)),pos => pos.isWalkable()); - this.walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable()); + this.walkableSourcePosisions = _.filter(_.flatten(_.map(this.room.sources, s => s.pos.neighbors)),pos => pos.isWalkable(true)); + this.walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable(true)); } } From e07072ea59ad2a8d49db91b479f6b39953d0c6f8 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Mon, 3 Jun 2019 13:29:25 +0300 Subject: [PATCH 107/137] FIND_HOSTILE_CONSTRUCTION_SITES --- src/directives/offense/poisonRoom.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 691bd7dcf..cd31f2ef9 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -90,7 +90,7 @@ export class DirectivePoisonRoom extends Directive { run() { - if (Game.time % 25 && this.room && this.room.my) { + if (Game.time % 25 == 0 && this.room && this.room.my) { //kill claimer if room claimed, it is can be blocking wall csite creation if (this.overlords.claim.claimers.length){ this.overlords.claim.claimers[0].suicide(); @@ -101,11 +101,8 @@ export class DirectivePoisonRoom extends Directive { } //remove any hostile consituction sites - if(this.room.constructionSites.length){ - _.forEach(this.room.constructionSites, csite => { - !csite.my && csite.remove(); - }) - } + _.forEach(this.room.find(FIND_HOSTILE_CONSTRUCTION_SITES), csite => {csite.remove();}); + // Remove if poisoned if (this.isPoisoned()) { this.room.controller!.unclaim(); From c0034701b5a7f5381edd0da2433e78b118190ffe Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Tue, 4 Jun 2019 23:38:40 +0300 Subject: [PATCH 108/137] added outpostDefense and attackController --- src/directives/offense/poisonRoom.ts | 8 ++++++++ src/overlords/offense/roomPoisoner.ts | 11 ----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index cd31f2ef9..c39dc8476 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -6,6 +6,8 @@ import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; import {printRoomName} from '../../utilities/utils'; import {MY_USERNAME} from '../../~settings'; import {Directive} from '../Directive'; +import {DirectiveControllerAttack} from '../../directives/offense/controllerAttack'; +import {DirectiveOutpostDefense} from '../defense/outpostDefense'; /** @@ -49,6 +51,12 @@ export class DirectivePoisonRoom extends Directive { this.walkableSourcePosisions = _.filter(_.flatten(_.map(this.room.sources, s => s.pos.neighbors)),pos => pos.isWalkable(true)); this.walkableControllerPosisions = _.filter(this.room.controller!.pos.neighbors, pos => pos.isWalkable(true)); } + if(this.room && this.room.controller && this.room.controller.reservation && this.room.controller.reservation.ticksToEnd > 500){ + DirectiveControllerAttack.createIfNotPresent(this.room.controller.pos,'room'); + } + if(this.room && this.room.dangerousPlayerHostiles.length > 0){ + DirectiveOutpostDefense.createIfNotPresent(new RoomPosition(25,25,this.room.name),'room'); + } } private poison() { diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index 4d4c118a3..f15f44fb0 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -58,17 +58,6 @@ export class RoomPoisonerOverlord extends Overlord { private handleRoomPoisoner(roomPoisoner: Zerg): void { // Ensure you are in the assigned room if (roomPoisoner.room == this.room && !roomPoisoner.pos.isEdge) { - - // Remove any blocking structures preventing claimer from reaching controller - // assuming it will not be unlocked after successfull room poisoning (directive will be auto remvoed) - if (!this.room.my && this.room.structures.length > 0) { - const dismantleTarget = this.findStructureBlockingController(roomPoisoner); - if (dismantleTarget) { - roomPoisoner.task = Tasks.dismantle(dismantleTarget); - return; - } - } - // Build and recharge if (roomPoisoner.carry.energy == 0) { roomPoisoner.task = Tasks.recharge(); From 35bd4d225fedcd57655089b2f33cd85abe27dd4b Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 05:15:19 +0300 Subject: [PATCH 109/137] added davaned's ideas --- src/creepSetups/setups.ts | 2 +- src/directives/offense/poisonRoom.ts | 47 +++++++++++++++++++++------ src/overlords/offense/roomPoisoner.ts | 16 +-------- src/zerg/CombatZerg.ts | 2 +- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index f596e8ecc..a1c608634 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -22,7 +22,7 @@ export const Roles = { ranged : 'hydralisk', healer : 'transfuser', dismantler: 'lurker', - roomPoisoner: 'roomPoisoner', + roomPoisoner: 'salter', }; /** diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index c39dc8476..00ddbf233 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -1,4 +1,5 @@ import {ClaimingOverlord} from '../../overlords/colonization/claimer'; +import {StationaryScoutOverlord} from '../../overlords/scouting/stationary'; import {RoomPoisonerOverlord} from '../../overlords/offense/roomPoisoner'; import {log} from '../../console/log'; import {profile} from '../../profiler/decorator'; @@ -6,7 +7,7 @@ import {Cartographer, ROOMTYPE_CONTROLLER} from '../../utilities/Cartographer'; import {printRoomName} from '../../utilities/utils'; import {MY_USERNAME} from '../../~settings'; import {Directive} from '../Directive'; -import {DirectiveControllerAttack} from '../../directives/offense/controllerAttack'; +import {DirectiveControllerAttack} from './controllerAttack'; import {DirectiveOutpostDefense} from '../defense/outpostDefense'; @@ -15,8 +16,8 @@ import {DirectiveOutpostDefense} from '../defense/outpostDefense'; */ @profile export class DirectivePoisonRoom extends Directive { - - static directiveName = 'poisonRoom'; + + static directiveName = 'contaminate'; static color = COLOR_RED; static secondaryColor = COLOR_BROWN; static requiredRCL = 4; @@ -27,7 +28,8 @@ export class DirectivePoisonRoom extends Directive { overlords: { claim: ClaimingOverlord; - roomPoisoner: RoomPoisonerOverlord; + roomPoisoner: RoomPoisonerOverlord; + scout: StationaryScoutOverlord; }; constructor(flag: Flag) { @@ -38,11 +40,30 @@ export class DirectivePoisonRoom extends Directive { `removing directive!`); this.remove(true); } + /* commented: keep the directive running for constant checking by stationary scout + // remove if already contaminated (if visible) + if (this.isPoisoned()) { + log.warning(`${this.print}: ${printRoomName(this.pos.roomName)} is already contaminated; ` + + `removing directive!`); + this.remove(true); + } + + // remove if no spare GCL + if(_.filter(Game.rooms, room => room.my).length == Game.gcl.level){ + log.warning(`${this.print}: ${printRoomName(this.pos.roomName)} not enough GCL; ` + + `removing directive!`); + this.remove(true); + } + */ } spawnMoarOverlords() { - this.overlords.claim = new ClaimingOverlord(this); - this.overlords.roomPoisoner = new RoomPoisonerOverlord(this); + if(!this.pos.isVisible){ + this.overlords.scout = new StationaryScoutOverlord(this); + } else if(this.room!.dangerousPlayerHostiles.length == 0 && !this.isPoisoned()){ + this.overlords.claim = new ClaimingOverlord(this); + this.overlords.roomPoisoner = new RoomPoisonerOverlord(this); + } } init() { @@ -69,9 +90,10 @@ export class DirectivePoisonRoom extends Directive { _.forEach(this.walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); } } else { - //remove all csites if roomPoisoner.carry.energy == 0, wall csites are not walkable + //remove all csites if roomPoisoner.carry.energy == 0, wall csites are not walkable, also remove roads! if(this.room){ _.forEach(this.room.constructionSites, csite => {csite.remove();} ); + _.forEach(this.room.roads, road => {road.destroy();} ); } } if(!DirectivePoisonRoom.poisonSourcesOnly){ @@ -99,6 +121,12 @@ export class DirectivePoisonRoom extends Directive { run() { if (Game.time % 25 == 0 && this.room && this.room.my) { + + if(_.filter(Game.rooms, room => room.my).length == Game.gcl.level){ + log.warning(`${this.print}: ${printRoomName(this.pos.roomName)} not enough GCL; ` + + `for contamination directive!`); + } + //kill claimer if room claimed, it is can be blocking wall csite creation if (this.overlords.claim.claimers.length){ this.overlords.claim.claimers[0].suicide(); @@ -111,11 +139,10 @@ export class DirectivePoisonRoom extends Directive { //remove any hostile consituction sites _.forEach(this.room.find(FIND_HOSTILE_CONSTRUCTION_SITES), csite => {csite.remove();}); - // Remove if poisoned if (this.isPoisoned()) { this.room.controller!.unclaim(); - log.notify(`Removing poisonRoom directive in ${this.pos.roomName}: operation completed.`); - this.remove(); + //log.notify(`Removing poisonRoom directive in ${this.pos.roomName}: operation completed.`); + //this.remove(); } else { this.poison(); } diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index f15f44fb0..e3506aa44 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -1,7 +1,5 @@ -import {log} from '../../console/log'; import {Roles, Setups} from '../../creepSetups/setups'; import {DirectivePoisonRoom} from '../../directives/offense/poisonRoom'; -import {Pathing} from '../../movement/Pathing'; import {OverlordPriority} from '../../priorities/priorities_overlords'; import {profile} from '../../profiler/decorator'; import {Tasks} from '../../tasks/Tasks'; @@ -19,7 +17,7 @@ export class RoomPoisonerOverlord extends Overlord { sourcesWallSites: ConstructionSite[] | undefined; constructor(directive: DirectivePoisonRoom, priority = OverlordPriority.offense.roomPoisoner) { - super(directive, 'roomPoisoner', priority); + super(directive, 'contaminate', priority); this.roomPoisoners = this.zerg(Roles.roomPoisoner); this.controllerWallSites = (this.room && this.room.controller) ? _.filter(this.room.constructionSites, s => s.structureType == STRUCTURE_WALL && @@ -42,18 +40,6 @@ export class RoomPoisonerOverlord extends Overlord { init() { this.wishlist(1, Setups.roomPoisoner); } - private findStructureBlockingController(pioneer: Zerg): Structure | undefined { - const blockingPos = Pathing.findBlockingPos(pioneer.pos, pioneer.room.controller!.pos, - _.filter(pioneer.room.structures, s => !s.isWalkable)); - if (blockingPos) { - const structure = blockingPos.lookFor(LOOK_STRUCTURES)[0]; - if (structure) { - return structure; - } else { - log.error(`${this.print}: no structure at blocking pos ${blockingPos.print}! (Why?)`); - } - } - } private handleRoomPoisoner(roomPoisoner: Zerg): void { // Ensure you are in the assigned room diff --git a/src/zerg/CombatZerg.ts b/src/zerg/CombatZerg.ts index 567219dfc..e25e40fff 100644 --- a/src/zerg/CombatZerg.ts +++ b/src/zerg/CombatZerg.ts @@ -162,7 +162,7 @@ export class CombatZerg extends Zerg { */ autoRanged(possibleTargets = this.room.hostiles, allowMassAttack = true) { const target = CombatTargeting.findBestCreepTargetInRange(this, 3, possibleTargets) - || CombatTargeting.findBestStructureTargetInRange(this, 3); + || CombatTargeting.findBestStructureTargetInRange(this, 3,false); //disabled allowUnowned structure attack in order not to desrtory poison walls this.debug(`Ranged target: ${target}`); if (target) { if (allowMassAttack From 882a488e1e0d5294c443a169d2df0d3b76b63da8 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 06:22:07 +0300 Subject: [PATCH 110/137] added corner case of controller blocked before sources --- src/directives/offense/poisonRoom.ts | 7 +++++-- src/overlords/offense/roomPoisoner.ts | 30 +++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 00ddbf233..05350b0bb 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -90,10 +90,9 @@ export class DirectivePoisonRoom extends Directive { _.forEach(this.walkableSourcePosisions,pos=>{pos.createConstructionSite(STRUCTURE_WALL)}); } } else { - //remove all csites if roomPoisoner.carry.energy == 0, wall csites are not walkable, also remove roads! + //remove all csites if roomPoisoner.carry.energy == 0, wall csites are not walkable if(this.room){ _.forEach(this.room.constructionSites, csite => {csite.remove();} ); - _.forEach(this.room.roads, road => {road.destroy();} ); } } if(!DirectivePoisonRoom.poisonSourcesOnly){ @@ -140,6 +139,10 @@ export class DirectivePoisonRoom extends Directive { _.forEach(this.room.find(FIND_HOSTILE_CONSTRUCTION_SITES), csite => {csite.remove();}); if (this.isPoisoned()) { + //remove roads before unclaiming + if(this.room.roads.length > 0){ + _.forEach(this.room.roads, road => {road.destroy();} ); + } this.room.controller!.unclaim(); //log.notify(`Removing poisonRoom directive in ${this.pos.roomName}: operation completed.`); //this.remove(); diff --git a/src/overlords/offense/roomPoisoner.ts b/src/overlords/offense/roomPoisoner.ts index e3506aa44..3803580ba 100644 --- a/src/overlords/offense/roomPoisoner.ts +++ b/src/overlords/offense/roomPoisoner.ts @@ -1,4 +1,6 @@ +import {log} from '../../console/log'; import {Roles, Setups} from '../../creepSetups/setups'; +import {Pathing} from '../../movement/Pathing'; import {DirectivePoisonRoom} from '../../directives/offense/poisonRoom'; import {OverlordPriority} from '../../priorities/priorities_overlords'; import {profile} from '../../profiler/decorator'; @@ -41,10 +43,35 @@ export class RoomPoisonerOverlord extends Overlord { this.wishlist(1, Setups.roomPoisoner); } + private findStructureBlockingController(roomPoisoner: Zerg): Structure | undefined { + const blockingPos = Pathing.findBlockingPos(roomPoisoner.pos, roomPoisoner.room.controller!.pos, + _.filter(roomPoisoner.room.structures, s => !s.isWalkable)); + if (blockingPos) { + const structure = blockingPos.lookFor(LOOK_STRUCTURES)[0]; + if (structure) { + return structure; + } else { + log.error(`${this.print}: no structure at blocking pos ${blockingPos.print}! (Why?)`); + } + } + } + private handleRoomPoisoner(roomPoisoner: Zerg): void { // Ensure you are in the assigned room if (roomPoisoner.room == this.room && !roomPoisoner.pos.isEdge) { - // Build and recharge + //corner case: unclaimed controller blocked, while sources not 100% bloked + if(!this.room.my && this.sourcesWallSites && this.controllerWallSites && + this.controllerWallSites.length ==0 && this.sourcesWallSites.length > 0){ + + const dismantleTarget = this.findStructureBlockingController(roomPoisoner); + if (dismantleTarget) { + roomPoisoner.task = Tasks.dismantle(dismantleTarget); + return; + } + } + + + // recharge if (roomPoisoner.carry.energy == 0) { roomPoisoner.task = Tasks.recharge(); } else if (this.room && this.room.controller && @@ -58,7 +85,6 @@ export class RoomPoisonerOverlord extends Overlord { roomPoisoner.task = Tasks.build(this.sourcesWallSites[0]); } } else { - // pioneer.task = Tasks.goTo(this.pos); roomPoisoner.goTo(this.pos, {ensurePath: true, avoidSK: true}); } } From 0b1c9d59b1d1f50a4850323d3d8b42e66b0f8e67 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 07:24:46 +0300 Subject: [PATCH 111/137] extra checks --- src/directives/offense/poisonRoom.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 05350b0bb..58b130a17 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -107,9 +107,9 @@ export class DirectivePoisonRoom extends Directive { private isPoisoned(): boolean { let result = false; if (this.room && this.room.controller!.level > 1) { - result = !this.walkableSourcePosisions.length; + result = !!this.walkableSourcePosisions && !this.walkableSourcePosisions.length; if(!DirectivePoisonRoom.poisonSourcesOnly){ - result = result && !this.walkableControllerPosisions.length; + result = result && !!this.walkableControllerPosisions && !this.walkableControllerPosisions.length; } return result; } else { @@ -119,6 +119,7 @@ export class DirectivePoisonRoom extends Directive { run() { + if (Game.time % 25 == 0 && this.room && this.room.my) { if(_.filter(Game.rooms, room => room.my).length == Game.gcl.level){ From 48fc6ba8592a66daa8b7463125aed46349ce63b0 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 07:40:25 +0300 Subject: [PATCH 112/137] from dangerouHostile to hostiles --- src/directives/offense/poisonRoom.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 58b130a17..cb74324bf 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -60,7 +60,7 @@ export class DirectivePoisonRoom extends Directive { spawnMoarOverlords() { if(!this.pos.isVisible){ this.overlords.scout = new StationaryScoutOverlord(this); - } else if(this.room!.dangerousPlayerHostiles.length == 0 && !this.isPoisoned()){ + } else if(this.room!.playerHostiles.length == 0 && !this.isPoisoned()){ this.overlords.claim = new ClaimingOverlord(this); this.overlords.roomPoisoner = new RoomPoisonerOverlord(this); } @@ -75,7 +75,7 @@ export class DirectivePoisonRoom extends Directive { if(this.room && this.room.controller && this.room.controller.reservation && this.room.controller.reservation.ticksToEnd > 500){ DirectiveControllerAttack.createIfNotPresent(this.room.controller.pos,'room'); } - if(this.room && this.room.dangerousPlayerHostiles.length > 0){ + if(this.room && this.room.playerHostiles.length > 0){ DirectiveOutpostDefense.createIfNotPresent(new RoomPosition(25,25,this.room.name),'room'); } } From 5da36f57b7711e1248ede791a495a84107219efa Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 08:42:03 +0300 Subject: [PATCH 113/137] send defences if hostiles and not poisoned --- src/directives/offense/poisonRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index cb74324bf..611874b40 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -75,7 +75,7 @@ export class DirectivePoisonRoom extends Directive { if(this.room && this.room.controller && this.room.controller.reservation && this.room.controller.reservation.ticksToEnd > 500){ DirectiveControllerAttack.createIfNotPresent(this.room.controller.pos,'room'); } - if(this.room && this.room.playerHostiles.length > 0){ + if(this.room && this.room.playerHostiles.length > 0 && !this.isPoisoned()){ DirectiveOutpostDefense.createIfNotPresent(new RoomPosition(25,25,this.room.name),'room'); } } From c3a9b0c98c869346bd32454bc7a4028a881bdb2c Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 08:53:48 +0300 Subject: [PATCH 114/137] spawn poisoner if no dangrous hostile player, spawnDefense when any hostile --- src/directives/offense/poisonRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 611874b40..5a88a06d3 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -60,7 +60,7 @@ export class DirectivePoisonRoom extends Directive { spawnMoarOverlords() { if(!this.pos.isVisible){ this.overlords.scout = new StationaryScoutOverlord(this); - } else if(this.room!.playerHostiles.length == 0 && !this.isPoisoned()){ + } else if(this.room!.dangerousPlayerHostiles.length == 0 && !this.isPoisoned()){ this.overlords.claim = new ClaimingOverlord(this); this.overlords.roomPoisoner = new RoomPoisonerOverlord(this); } From a031549b8b7a91a10d3df7c17497687f49e5dadb Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 08:55:50 +0300 Subject: [PATCH 115/137] this.room --- src/directives/offense/poisonRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/directives/offense/poisonRoom.ts b/src/directives/offense/poisonRoom.ts index 5a88a06d3..8eba65d93 100644 --- a/src/directives/offense/poisonRoom.ts +++ b/src/directives/offense/poisonRoom.ts @@ -60,7 +60,7 @@ export class DirectivePoisonRoom extends Directive { spawnMoarOverlords() { if(!this.pos.isVisible){ this.overlords.scout = new StationaryScoutOverlord(this); - } else if(this.room!.dangerousPlayerHostiles.length == 0 && !this.isPoisoned()){ + } else if(this.room && this.room.dangerousPlayerHostiles.length == 0 && !this.isPoisoned()){ this.overlords.claim = new ClaimingOverlord(this); this.overlords.roomPoisoner = new RoomPoisonerOverlord(this); } From 09bc9233557de1a417b40fddff650ce6dc0a9068 Mon Sep 17 00:00:00 2001 From: hasoon hasoon Date: Wed, 5 Jun 2019 09:26:05 +0300 Subject: [PATCH 116/137] prevent DirectiveInvasionDefense --- src/Overseer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Overseer.ts b/src/Overseer.ts index fab0a78c2..1f5398977 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -216,7 +216,7 @@ export class Overseer implements IOverseer { // Place defensive directive after hostiles have been present for a long enough time const safetyData = RoomIntel.getSafetyData(colony.room.name); const invasionIsPersistent = safetyData.unsafeFor > 20; - if (invasionIsPersistent) { + if (invasionIsPersistent && !DirectivePoisonRoom.isPresent(colony.pos, 'room')) { DirectiveInvasionDefense.createIfNotPresent(colony.controller.pos, 'room'); } } From 14fb752cc3c49ff42a2517ad8a01de1cd7530e7a Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 5 Jun 2019 19:55:46 -0700 Subject: [PATCH 117/137] Tracking enemy creep expenditure --- src/declarations/memory.d.ts | 10 +++++ src/directives/defense/invasionDefense.ts | 52 +++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 22b85a301..f6376b9e8 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -30,6 +30,9 @@ interface Memory { haltTick?: number; combatPlanner: any; reinforcementLearning?: any; + playerCreepTracker: { + [playerName: string]: CreepTracker + } [otherProperty: string]: any; } @@ -123,6 +126,13 @@ interface PathingMemory { weightedDistances: { [pos1Name: string]: { [pos2Name: string]: number; } }; } +interface CreepTracker { + creeps: { [name: string]: number }; // first tick seen + types: { [type: string]: number}; // amount seen + parts: { [bodyPart: string]: number}; // quantity + boosts: { [boostType: string]: number}; // how many boosts are spent +} + interface FlagMemory { [_MEM.TICK]?: number; [_MEM.EXPIRATION]?: number; diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 1d6cab663..f1afe24ed 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -58,9 +58,61 @@ export class DirectiveInvasionDefense extends Directive { this.alert(`Invasion (hostiles: ${numHostiles})`, NotifierPriority.Critical); } + recordBaddies () { + if (!this.room) { + return; + } + let mem = Memory.playerCreepTracker; + let hostiles = this.room.hostiles; + hostiles.forEach(creep => { + if (!mem[creep.owner.username]) { + mem[creep.owner.username] = { + creeps: {}, + types: {}, + parts: {}, + boosts: {}, + }; + } + let playerMem = mem[creep.owner.username]; + if (!playerMem.creeps[creep.name]) { + playerMem.creeps[creep.name] = Game.time; + const creepType = creep.name.substr(0, creep.name.indexOf(" ")); + playerMem.types[creepType] = (playerMem.types[creepType]+1) || 1 ; + for (const boostType of _.keys(creep.boostCounts)) { + const count = creep.boostCounts[boostType]; + playerMem.boosts[boostType] = (playerMem.boosts[boostType])+count || count; + } + for (const bodyPart of creep.body) { + playerMem.parts[bodyPart.type] = (playerMem.boosts[bodyPart.type])+1 || 1; + if (bodyPart.boost) { + playerMem.boosts[bodyPart.boost] = (playerMem.boosts[bodyPart.boost])+1 || 1; + } + } + } + }); + } + + cleanUpPlayerMem() { + let mem = Memory.playerCreepTracker; + for (let player of _.keys(mem)) { + let tracker = mem[player]; + for (let creep of _.keys(tracker.creeps)) { + if (tracker.creeps[creep] + 1500 < Game.time) { + delete tracker.creeps[creep]; + } + } + } + } + run(): void { if (!this.room || this.room.hostiles.length > 0) { this.memory.safeSince = Game.time; + this.recordBaddies(); + } + + if (Game.time % 5000 == 0) { + // clean up, ya this shit + this.cleanUpPlayerMem(); } // If there are no hostiles left in the room and everyone's healed, then remove the flag if (this.room && this.room.hostiles.length == 0 && From c232b1913bed2e0bf8299494aab1d6eb40f896f0 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 5 Jun 2019 20:00:12 -0700 Subject: [PATCH 118/137] Adding default --- src/memory/Memory.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/memory/Memory.ts b/src/memory/Memory.ts index 35db27790..af5a8a41f 100644 --- a/src/memory/Memory.ts +++ b/src/memory/Memory.ts @@ -191,6 +191,9 @@ export class Mem { if (!Memory.settings) { Memory.settings = {} as any; } + if (!Memory.playerCreepTracker) { + Memory.playerCreepTracker = {}; + } if (!USE_PROFILER) { delete Memory.profiler; } From 2e397d60ff6ec186a7111a6e2004125ff7aa364c Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 5 Jun 2019 20:09:48 -0700 Subject: [PATCH 119/137] Fixing tracker --- src/directives/defense/invasionDefense.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index f1afe24ed..2f0a711f3 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -77,13 +77,13 @@ export class DirectiveInvasionDefense extends Directive { if (!playerMem.creeps[creep.name]) { playerMem.creeps[creep.name] = Game.time; const creepType = creep.name.substr(0, creep.name.indexOf(" ")); - playerMem.types[creepType] = (playerMem.types[creepType]+1) || 1 ; - for (const boostType of _.keys(creep.boostCounts)) { - const count = creep.boostCounts[boostType]; - playerMem.boosts[boostType] = (playerMem.boosts[boostType])+count || count; + if (creepType == creep.name) { + // memory protection if they don't split name + return; } + playerMem.types[creepType] = (playerMem.types[creepType]+1) || 1 ; for (const bodyPart of creep.body) { - playerMem.parts[bodyPart.type] = (playerMem.boosts[bodyPart.type])+1 || 1; + playerMem.parts[bodyPart.type] = (playerMem.parts[bodyPart.type])+1 || 1; if (bodyPart.boost) { playerMem.boosts[bodyPart.boost] = (playerMem.boosts[bodyPart.boost])+1 || 1; } From 3473b8e81e6af377d978ddd4db0604ba6bb8db29 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 7 Jun 2019 12:01:48 -0700 Subject: [PATCH 120/137] Adding zone data fetch --- src/intel/RoomIntel.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/intel/RoomIntel.ts b/src/intel/RoomIntel.ts index c9046ed45..3c4812130 100644 --- a/src/intel/RoomIntel.ts +++ b/src/intel/RoomIntel.ts @@ -7,6 +7,7 @@ import {ExpansionEvaluator} from '../strategy/ExpansionEvaluator'; import {getCacheExpiration, irregularExponentialMovingAverage} from '../utilities/utils'; import {Zerg} from '../zerg/Zerg'; import {MY_USERNAME} from '../~settings'; +import {Segmenter} from "../memory/Segmenter"; const RECACHE_TIME = 2500; const OWNED_RECACHE_TIME = 1000; @@ -351,6 +352,16 @@ export class RoomIntel { return 0; } + static requestZoneData() { + const checkOnTick = 12; + if (Game.time % 30 == checkOnTick - 2) { + Segmenter.requestForeignSegment('LOAN', 96); + } else if (Game.time % 30 == checkOnTick - 1) { + const loanData = Segmenter.getForeignSegment(); + console.log(`\n \n \n \n Loan Data is: ${loanData} \n \n \n \n `); + } + } + static run(): void { let alreadyComputedScore = false; @@ -360,6 +371,7 @@ export class RoomIntel { this.markVisible(room); this.recordSafety(room); + this.requestZoneData(); // Track invasion data, harvesting, and casualties for all colony rooms and outposts if (Overmind.colonyMap[room.name]) { // if it is an owned or outpost room From 11551359c6edc3fcb3ffccb424a39a5001191434 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 7 Jun 2019 12:28:54 -0700 Subject: [PATCH 121/137] Adding to memory and printing --- src/declarations/memory.d.ts | 1 + src/intel/RoomIntel.ts | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 22b85a301..4a603e0c3 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -31,6 +31,7 @@ interface Memory { combatPlanner: any; reinforcementLearning?: any; + zoneRooms: { [roomName: string]: { [type: string]: number} }; [otherProperty: string]: any; } diff --git a/src/intel/RoomIntel.ts b/src/intel/RoomIntel.ts index 3c4812130..efc699a66 100644 --- a/src/intel/RoomIntel.ts +++ b/src/intel/RoomIntel.ts @@ -353,25 +353,34 @@ export class RoomIntel { } static requestZoneData() { - const checkOnTick = 12; - if (Game.time % 30 == checkOnTick - 2) { - Segmenter.requestForeignSegment('LOAN', 96); - } else if (Game.time % 30 == checkOnTick - 1) { + const checkOnTick = 4; + if (Game.time % 10 == checkOnTick - 2) { + Segmenter.requestForeignSegment('LeagueOfAutomatedNations', 96); + } else if (Game.time % 10 == checkOnTick - 1 || Game.time % 10 == checkOnTick) { const loanData = Segmenter.getForeignSegment(); - console.log(`\n \n \n \n Loan Data is: ${loanData} \n \n \n \n `); + if (loanData) { + console.log(`\n \n \n \n Loan Data is: ${JSON.stringify(loanData)} \n \n \n \n `); + } else { + console.log('Empty loan data'); + } } } static run(): void { let alreadyComputedScore = false; + this.requestZoneData(); + for (const name in Game.rooms) { const room: Room = Game.rooms[name]; + if (Memory.zoneRooms && _.keys(Memory.zoneRooms).indexOf(name) != -1) { + console.log(`Room ${name} found in zone! ${Memory.zoneRooms[name]}`); + } + this.markVisible(room); this.recordSafety(room); - this.requestZoneData(); // Track invasion data, harvesting, and casualties for all colony rooms and outposts if (Overmind.colonyMap[room.name]) { // if it is an owned or outpost room From 2e9e38776c5830d13452c82493829d65eaba99c3 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 7 Jun 2019 13:23:21 -0700 Subject: [PATCH 122/137] Adding storage and utils for checking --- src/intel/RoomIntel.ts | 11 ++++++----- src/utilities/Cartographer.ts | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/intel/RoomIntel.ts b/src/intel/RoomIntel.ts index efc699a66..6161989da 100644 --- a/src/intel/RoomIntel.ts +++ b/src/intel/RoomIntel.ts @@ -8,6 +8,7 @@ import {getCacheExpiration, irregularExponentialMovingAverage} from '../utilitie import {Zerg} from '../zerg/Zerg'; import {MY_USERNAME} from '../~settings'; import {Segmenter} from "../memory/Segmenter"; +import {log} from "../console/log"; const RECACHE_TIME = 2500; const OWNED_RECACHE_TIME = 1000; @@ -353,15 +354,15 @@ export class RoomIntel { } static requestZoneData() { - const checkOnTick = 4; - if (Game.time % 10 == checkOnTick - 2) { + const checkOnTick = 123; + if (Game.time % 1000 == checkOnTick - 2) { Segmenter.requestForeignSegment('LeagueOfAutomatedNations', 96); - } else if (Game.time % 10 == checkOnTick - 1 || Game.time % 10 == checkOnTick) { + } else if (Game.time % 1000 == checkOnTick - 1 ) { const loanData = Segmenter.getForeignSegment(); if (loanData) { - console.log(`\n \n \n \n Loan Data is: ${JSON.stringify(loanData)} \n \n \n \n `); + Memory.zoneRooms = loanData; } else { - console.log('Empty loan data'); + log.error('Empty LOAN data'); } } } diff --git a/src/utilities/Cartographer.ts b/src/utilities/Cartographer.ts index 92d576a1a..c23877ba2 100644 --- a/src/utilities/Cartographer.ts +++ b/src/utilities/Cartographer.ts @@ -1,4 +1,5 @@ import {profile} from '../profiler/decorator'; +import {log} from "../console/log"; export const ROOMTYPE_SOURCEKEEPER = 'SK'; export const ROOMTYPE_CORE = 'CORE'; @@ -214,4 +215,24 @@ export class Cartographer { }; } + static isNoviceRoom(roomName: string): boolean { + if (Memory.zoneRooms) { + const roomInfo = Memory.zoneRooms[roomName]; + return roomInfo && !!roomInfo['novice']; + } else { + log.alert(`Checking novice room before segment is set in ${roomName}!`); + return false; + } + } + + static isRespawnRoom(roomName: string): boolean { + if (Memory.zoneRooms) { + const roomInfo = Memory.zoneRooms[roomName]; + return roomInfo && !!roomInfo['respawnArea']; + } else { + log.alert(`Checking respawn room before segment is set in ${roomName}!`); + return false; + } + } + } From 5a3e518e08085157d0e3f9ad121813233423cbbb Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 7 Jun 2019 13:31:19 -0700 Subject: [PATCH 123/137] !! for false --- src/utilities/Cartographer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utilities/Cartographer.ts b/src/utilities/Cartographer.ts index c23877ba2..f03b3bb8d 100644 --- a/src/utilities/Cartographer.ts +++ b/src/utilities/Cartographer.ts @@ -218,7 +218,7 @@ export class Cartographer { static isNoviceRoom(roomName: string): boolean { if (Memory.zoneRooms) { const roomInfo = Memory.zoneRooms[roomName]; - return roomInfo && !!roomInfo['novice']; + return !!roomInfo && !!roomInfo['novice']; } else { log.alert(`Checking novice room before segment is set in ${roomName}!`); return false; @@ -228,7 +228,7 @@ export class Cartographer { static isRespawnRoom(roomName: string): boolean { if (Memory.zoneRooms) { const roomInfo = Memory.zoneRooms[roomName]; - return roomInfo && !!roomInfo['respawnArea']; + return !!roomInfo && !!roomInfo['respawnArea']; } else { log.alert(`Checking respawn room before segment is set in ${roomName}!`); return false; From 96992ecb13612792b9c0f8730c0b6988af823c9c Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 7 Jun 2019 13:33:31 -0700 Subject: [PATCH 124/137] PR logging cleanup --- src/declarations/memory.d.ts | 2 +- src/intel/RoomIntel.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index 4a603e0c3..4e3b46908 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -30,8 +30,8 @@ interface Memory { haltTick?: number; combatPlanner: any; reinforcementLearning?: any; - zoneRooms: { [roomName: string]: { [type: string]: number} }; + [otherProperty: string]: any; } diff --git a/src/intel/RoomIntel.ts b/src/intel/RoomIntel.ts index 6161989da..68098d50d 100644 --- a/src/intel/RoomIntel.ts +++ b/src/intel/RoomIntel.ts @@ -376,10 +376,6 @@ export class RoomIntel { const room: Room = Game.rooms[name]; - if (Memory.zoneRooms && _.keys(Memory.zoneRooms).indexOf(name) != -1) { - console.log(`Room ${name} found in zone! ${Memory.zoneRooms[name]}`); - } - this.markVisible(room); this.recordSafety(room); From 108c55b4be036a9672cca63c9c2d467f7d61a398 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 12 Jun 2019 12:48:52 -0700 Subject: [PATCH 125/137] Storing new operator form quickly --- src/directives/powerCreeps/baseOperator.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/directives/powerCreeps/baseOperator.ts b/src/directives/powerCreeps/baseOperator.ts index f094a09fc..0bee0feb3 100644 --- a/src/directives/powerCreeps/baseOperator.ts +++ b/src/directives/powerCreeps/baseOperator.ts @@ -11,6 +11,12 @@ interface DirectiveBaseOperatorMemory extends FlagMemory { powerPriorities: PowerConstant[]; } +export enum types { + opgen, + baseoperator, + basedefender +} + /** * Simple directive to run a power creep where the flag name is the power creep name */ @@ -234,6 +240,10 @@ export class DirectiveBaseOperator extends Directive { run(): void { + + // For the power creeps that just sit on power spawn + const isStationary = this.powerCreep.name.toLowerCase().indexOf(types.basedefender.toString()); + console.log(`Running power creep ${JSON.stringify(this.powerCreep)} with ttl ${this.powerCreep.ticksToLive} with ${this.room!.powerSpawn}`); if (!this.room) { return; @@ -241,11 +251,11 @@ export class DirectiveBaseOperator extends Directive { // Spawn creep let res = this.powerCreep.spawn(this.room.powerSpawn); log.alert(`Running ${this.powerCreep} with spawn of ${res}`); - } else if (this.room.controller && !this.room.controller.isPowerEnabled) { + } else if (this.room.controller && !this.room.controller.isPowerEnabled && !isStationary) { // Enable power let res = this.enablePower(this.room.controller); log.alert(`Running ${this.powerCreep} with enable power of ${res}`); - } else if (this.powerCreep && this.powerCreep.ticksToLive && this.powerCreep.ticksToLive < 400 && this.room.powerSpawn) { + } else if (this.powerCreep && this.powerCreep.ticksToLive && this.powerCreep.ticksToLive < 900 && this.room.powerSpawn) { let res = this.renew(this.room.powerSpawn); log.alert(`Running ${this.powerCreep} with renew of ${res}`); } else { From eddc945d955414128b1b73c4707a34e287b8b8e6 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 12 Jun 2019 12:51:31 -0700 Subject: [PATCH 126/137] Prioritize re-upgrading controller if it was downgraded --- src/overlords/core/worker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index a8924d692..ae5906ed3 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -337,9 +337,10 @@ export class WorkerOverlord extends Overlord { // TODO Add high priority to block controller with ramparts/walls in case of downgrade attack // FIXME workers get stalled at controller in case of downgrade attack - // Upgrade controller if close to downgrade or prepare to upgrade if getting controller attacked + // Upgrade controller if close to downgrade or prepare to upgrade if getting controller attacked/was downgraded const downgradeLevel = CONTROLLER_DOWNGRADE[this.colony.controller.level] * .7; - if (this.colony.controller.ticksToDowngrade <= downgradeLevel && !this.colony.controller.upgradeBlocked || this.colony.controller.upgradeBlocked < 30) { + if ((!this.colony.controller.upgradeBlocked || this.colony.controller.upgradeBlocked < 30) + && (this.colony.controller.ticksToDowngrade <= downgradeLevel || this.colony.controller.progress > this.colony.controller.progressTotal)) { if (this.upgradeActions(worker)) return; } // Repair damaged non-road non-barrier structures From 0f7a554bdcdcbe79476c235abb73bd39ecac41e1 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 13 Jun 2019 15:34:38 -0700 Subject: [PATCH 127/137] Distraction updates --- src/overlords/defense/distraction.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/overlords/defense/distraction.ts b/src/overlords/defense/distraction.ts index 171646d42..702fdaefc 100644 --- a/src/overlords/defense/distraction.ts +++ b/src/overlords/defense/distraction.ts @@ -16,7 +16,7 @@ import {CombatOverlord} from '../CombatOverlord'; @profile export class DistractionOverlord extends CombatOverlord { - distraction: CombatZerg[]; + distractions: CombatZerg[]; room: Room; static settings = { @@ -28,18 +28,24 @@ export class DistractionOverlord extends CombatOverlord { boosted = false, priority = OverlordPriority.defense.rangedDefense) { super(directive, 'distraction', priority, 1); - this.distraction = this.combatZerg(Roles.ranged); + this.distractions = this.combatZerg(Roles.ranged); } private handleDistraction(distraction: CombatZerg): void { if (this.room.hostiles.length > 0) { - distraction.autoCombat(this.room.name, false, 5); + distraction.autoCombat(this.room.name, false, 5, {preferRamparts: false}); this.taunt(distraction, this.room.hostiles[0].owner.username); + const nearbyHostiles = this.room.hostiles.filter(hostile => hostile.pos.getRangeTo(distraction) <= 6); + //nearbyHostiles[0] + if (nearbyHostiles.length > 0) { + distraction.kite(nearbyHostiles); + } } } taunt(distraction: CombatZerg, name?: string) { - const taunts: string[] = ['Heylisten!', 'Pssssst', 'Catch Me!', `Hi ${name || ''}`, '🍑🍑🍑', '🏎️ VROOM']; + const taunts: string[] = ['Heylisten!', 'Pssssst', 'So close', '🎣', 'Try harder', 'Get good;)', 'Base ⬆️', '🔜', + '⚠️Swamp⚠️', 'Follow me!', 'Catch Me!', `Hi ${name || ''}`, '🍑🍑🍑', '🏎️ VROOM']; distraction.sayRandom(taunts, true); } @@ -50,6 +56,7 @@ export class DistractionOverlord extends CombatOverlord { } run() { - this.autoRun(this.distraction, distraction => this.handleDistraction(distraction)); + console.log(`Distraction overlord running in ${this.room.print} with ${this.distractions}!`); + this.autoRun(this.distractions, distraction => this.handleDistraction(distraction)); } } From b8254c8b3797035df37a05555d9cebfc0ba43aa1 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 13 Jun 2019 18:40:48 -0700 Subject: [PATCH 128/137] Updating distraction and lint --- src/overlords/defense/distraction.ts | 3 +-- src/zerg/CombatZerg.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/overlords/defense/distraction.ts b/src/overlords/defense/distraction.ts index 702fdaefc..f0b032213 100644 --- a/src/overlords/defense/distraction.ts +++ b/src/overlords/defense/distraction.ts @@ -36,14 +36,13 @@ export class DistractionOverlord extends CombatOverlord { distraction.autoCombat(this.room.name, false, 5, {preferRamparts: false}); this.taunt(distraction, this.room.hostiles[0].owner.username); const nearbyHostiles = this.room.hostiles.filter(hostile => hostile.pos.getRangeTo(distraction) <= 6); - //nearbyHostiles[0] if (nearbyHostiles.length > 0) { distraction.kite(nearbyHostiles); } } } - taunt(distraction: CombatZerg, name?: string) { + static taunt(distraction: CombatZerg, name?: string) { const taunts: string[] = ['Heylisten!', 'Pssssst', 'So close', '🎣', 'Try harder', 'Get good;)', 'Base ⬆️', '🔜', '⚠️Swamp⚠️', 'Follow me!', 'Catch Me!', `Hi ${name || ''}`, '🍑🍑🍑', '🏎️ VROOM']; distraction.sayRandom(taunts, true); diff --git a/src/zerg/CombatZerg.ts b/src/zerg/CombatZerg.ts index 92f3b4786..12067059d 100644 --- a/src/zerg/CombatZerg.ts +++ b/src/zerg/CombatZerg.ts @@ -1,5 +1,5 @@ import {CombatIntel} from '../intel/CombatIntel'; -import {Movement, NO_ACTION} from '../movement/Movement'; +import {CombatMoveOptions, Movement, MoveOptions, NO_ACTION} from '../movement/Movement'; import {profile} from '../profiler/decorator'; import {CombatTargeting} from '../targeting/CombatTargeting'; import {GoalFinder} from '../targeting/GoalFinder'; @@ -227,7 +227,7 @@ export class CombatZerg extends Zerg { /** * Navigate to a room, then engage hostile creeps there, perform medic actions, etc. */ - autoCombat(roomName: string, verbose = false, preferredRange?: number) { + autoCombat(roomName: string, verbose = false, preferredRange?: number, options?: CombatMoveOptions) { // Do standard melee, ranged, and heal actions if (this.getActiveBodyparts(ATTACK) > 0) { @@ -266,7 +266,7 @@ export class CombatZerg extends Zerg { avoid.push({pos: hostile.pos, range: targetRange - 1}); } } - return Movement.combatMove(this, [{pos: target.pos, range: targetRange}], avoid); + return Movement.combatMove(this, [{pos: target.pos, range: targetRange}], avoid, options); } } From 7504fc6fe37d8b6bf9bc08c9e5006f9c5bfcedd3 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Fri, 14 Jun 2019 12:15:01 -0700 Subject: [PATCH 129/137] Lint --- src/overlords/colonization/pioneer.ts | 6 +++--- src/overlords/core/worker.ts | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/overlords/colonization/pioneer.ts b/src/overlords/colonization/pioneer.ts index 09681cfbd..8921e33c9 100644 --- a/src/overlords/colonization/pioneer.ts +++ b/src/overlords/colonization/pioneer.ts @@ -63,9 +63,9 @@ export class PioneerOverlord extends Overlord { // Build and recharge if (pioneer.carry.energy == 0) { pioneer.task = Tasks.recharge(); - } else if (this.room && this.room.controller && - (this.room.controller.ticksToDowngrade < (0.1 * CONTROLLER_DOWNGRADE[this.colony.controller.level]) || !this.spawnSite) && - !(this.room.controller.upgradeBlocked > 0)) { + } else if (this.room && this.room.controller && (this.room.controller.ticksToDowngrade + < (0.1 * CONTROLLER_DOWNGRADE[this.colony.controller.level]) || !this.spawnSite) + && !(this.room.controller.upgradeBlocked > 0)) { // Save controller if it's about to downgrade or if you have nothing else to do pioneer.task = Tasks.upgrade(this.room.controller); } else if (this.spawnSite) { diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index ae5906ed3..547393c3a 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -337,11 +337,13 @@ export class WorkerOverlord extends Overlord { // TODO Add high priority to block controller with ramparts/walls in case of downgrade attack // FIXME workers get stalled at controller in case of downgrade attack - // Upgrade controller if close to downgrade or prepare to upgrade if getting controller attacked/was downgraded + // Upgrade controller if close to downgrade or if getting controller attacked/was downgraded const downgradeLevel = CONTROLLER_DOWNGRADE[this.colony.controller.level] * .7; if ((!this.colony.controller.upgradeBlocked || this.colony.controller.upgradeBlocked < 30) - && (this.colony.controller.ticksToDowngrade <= downgradeLevel || this.colony.controller.progress > this.colony.controller.progressTotal)) { + && (this.colony.controller.ticksToDowngrade <= downgradeLevel + || this.colony.controller.progress > this.colony.controller.progressTotal)) { if (this.upgradeActions(worker)) return; + } // Repair damaged non-road non-barrier structures if (this.repairStructures.length > 0 && this.colony.defcon == DEFCON.safe) { From 14ac9bcdf2a6b536cb915d85cd3780f08ab4dc01 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 17 Jun 2019 18:10:21 -0700 Subject: [PATCH 130/137] Deletes all room flags before cleanup --- src/directives/colony/clearRoom.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/directives/colony/clearRoom.ts b/src/directives/colony/clearRoom.ts index 3db78368a..6758b0937 100644 --- a/src/directives/colony/clearRoom.ts +++ b/src/directives/colony/clearRoom.ts @@ -91,6 +91,12 @@ export class DirectiveClearRoom extends Directive { const done = this.removeAllStructures(); if (done) { let res = this.room.controller!.unclaim(); + // Clear up flags + for (let flag of this.room.flags) { + if (!DirectiveClearRoom.filter(flag) && !DirectiveHaul.filter(flag)) { + flag.remove(); + } + } log.notify(`Removing clearRoom directive in ${this.pos.roomName}: operation completed.`); if (res == OK) { this.remove(); From 36838a8f78dc5b3b78184f0222ed8506f82ac236 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Thu, 20 Jun 2019 17:47:52 -0700 Subject: [PATCH 131/137] Only make container if the mineral is ready, as some improvements to upgrading --- src/Colony.ts | 2 +- src/overlords/core/worker.ts | 4 ++-- src/overlords/mining/extractor.ts | 9 +++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Colony.ts b/src/Colony.ts index c23be5588..cf8d4cf50 100644 --- a/src/Colony.ts +++ b/src/Colony.ts @@ -320,7 +320,7 @@ export class Colony { .filter(e => (e!.my && e!.room.my) || Cartographer.roomType(e!.room.name) != ROOMTYPE_CONTROLLER) .sortBy(e => e!.pos.getMultiRoomRangeTo(this.pos)).value() as StructureExtractor[]); - if (this.controller.level >= 6) { + if (this.controller.level >= 6 && this.terminal) { _.forEach(this.extractors, extractor => DirectiveExtract.createIfNotPresent(extractor.pos, 'pos')); } $.set(this, 'repairables', () => _.flatten(_.map(this.rooms, room => room.repairables))); diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index 547393c3a..156abc4e7 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -338,12 +338,12 @@ export class WorkerOverlord extends Overlord { // TODO Add high priority to block controller with ramparts/walls in case of downgrade attack // FIXME workers get stalled at controller in case of downgrade attack // Upgrade controller if close to downgrade or if getting controller attacked/was downgraded - const downgradeLevel = CONTROLLER_DOWNGRADE[this.colony.controller.level] * .7; + const downgradeLevel = CONTROLLER_DOWNGRADE[this.colony.controller.level] * + (this.colony.controller.level < 4 ? .3 : .7); if ((!this.colony.controller.upgradeBlocked || this.colony.controller.upgradeBlocked < 30) && (this.colony.controller.ticksToDowngrade <= downgradeLevel || this.colony.controller.progress > this.colony.controller.progressTotal)) { if (this.upgradeActions(worker)) return; - } // Repair damaged non-road non-barrier structures if (this.repairStructures.length > 0 && this.colony.defcon == DEFCON.safe) { diff --git a/src/overlords/mining/extractor.ts b/src/overlords/mining/extractor.ts index 835b11054..18f1ebebd 100644 --- a/src/overlords/mining/extractor.ts +++ b/src/overlords/mining/extractor.ts @@ -37,6 +37,11 @@ export class ExtractorOverlord extends Overlord { this.populateStructures(); } + // If mineral is ready to be mined, make a container + private shouldHaveContainer() { + return this.mineral && (this.mineral.mineralAmount > 0 || this.mineral.ticksToRegeneration < 2000); + } + private populateStructures() { if (Game.rooms[this.pos.roomName]) { this.extractor = this.pos.lookForStructure(STRUCTURE_EXTRACTOR) as StructureExtractor | undefined; @@ -82,8 +87,8 @@ export class ExtractorOverlord extends Overlord { } private buildOutputIfNeeded(): void { - // Create container if there is not already one being built and no link - if (!this.container) { + // Create container if there is not already one being built + if (!this.container && this.shouldHaveContainer()) { const containerSite = _.first(_.filter(this.pos.findInRange(FIND_MY_CONSTRUCTION_SITES, 2), site => site.structureType == STRUCTURE_CONTAINER)); if (!containerSite) { From 9b09a15811583f4dce50e65f071ecebda4c379f4 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 24 Jun 2019 15:16:37 -0700 Subject: [PATCH 132/137] Making miners be more optimized --- src/creepSetups/setups.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 1c57dcbee..f8b9e6136 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -31,8 +31,9 @@ export const Setups = { drones: { extractor: new CreepSetup(Roles.drone, { - pattern : [WORK, WORK, CARRY, MOVE], + pattern : [WORK, WORK, MOVE], sizeLimit: Infinity, + prefix: [CARRY, CARRY] }), miners: { From a9b5e17bdf0c0c634251369d93ce22b0b12ffb06 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 24 Jun 2019 16:08:22 -0700 Subject: [PATCH 133/137] Collect non-energy first --- src/overlords/situational/hauler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/overlords/situational/hauler.ts b/src/overlords/situational/hauler.ts index 8164b270d..cb7758b1e 100644 --- a/src/overlords/situational/hauler.ts +++ b/src/overlords/situational/hauler.ts @@ -55,7 +55,7 @@ export class HaulingOverlord extends Overlord { // Pick up drops first if (this.directive.hasDrops) { const allDrops: Resource[] = _.flatten(_.values(this.directive.drops)); - const drop = allDrops[0]; + const drop = _.find(allDrops, drop => drop.resourceType != "energy") || allDrops[0]; if (drop) { hauler.task = Tasks.pickup(drop); return; From ac1783d935d52d402eba8fe5cd728b58323764ce Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 24 Jun 2019 19:11:14 -0700 Subject: [PATCH 134/137] Minor body modification to prevent dropping on ground, need to work out root cause --- src/creepSetups/setups.ts | 2 +- src/overlords/mining/extractor.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index f8b9e6136..bc4e2bb57 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -33,7 +33,7 @@ export const Setups = { extractor: new CreepSetup(Roles.drone, { pattern : [WORK, WORK, MOVE], sizeLimit: Infinity, - prefix: [CARRY, CARRY] + prefix: [CARRY, CARRY, CARRY, WORK, MOVE] }), miners: { diff --git a/src/overlords/mining/extractor.ts b/src/overlords/mining/extractor.ts index 18f1ebebd..f22a8ac5b 100644 --- a/src/overlords/mining/extractor.ts +++ b/src/overlords/mining/extractor.ts @@ -115,7 +115,7 @@ export class ExtractorOverlord extends Overlord { if (_.sum(drone.carry) == 0) { drone.task = Tasks.harvest(this.mineral!); } - // Else see if there is an output to depsit to or to maintain + // Else see if there is an output to deposit to or to maintain else if (this.container) { drone.task = Tasks.transferAll(this.container); // Move onto the output container if you're the only drone From fc0bef4f39572dc3a0a8efafffa128b38a698511 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Mon, 24 Jun 2019 19:30:05 -0700 Subject: [PATCH 135/137] Modified body back and put a .95 on the harvest valid. Might want to split out the most barebones harvest from the more advanced mineral/other harvesting --- src/creepSetups/setups.ts | 2 +- src/tasks/instances/harvest.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index bc4e2bb57..f8b9e6136 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -33,7 +33,7 @@ export const Setups = { extractor: new CreepSetup(Roles.drone, { pattern : [WORK, WORK, MOVE], sizeLimit: Infinity, - prefix: [CARRY, CARRY, CARRY, WORK, MOVE] + prefix: [CARRY, CARRY] }), miners: { diff --git a/src/tasks/instances/harvest.ts b/src/tasks/instances/harvest.ts index ca94de5af..8efbea396 100644 --- a/src/tasks/instances/harvest.ts +++ b/src/tasks/instances/harvest.ts @@ -14,7 +14,7 @@ export class TaskHarvest extends Task { } isValidTask() { - return _.sum(this.creep.carry) < this.creep.carryCapacity; + return _.sum(this.creep.carry) < this.creep.carryCapacity * .95; } isValidTarget() { From 05eba448986a269c6da27030872a1693be874f74 Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 26 Jun 2019 00:55:14 -0700 Subject: [PATCH 136/137] Basic combat intel damage matrix --- src/directives/defense/invasionDefense.ts | 3 +++ src/intel/CombatIntel.ts | 26 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 5b27611e7..38b54b38b 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -70,6 +70,9 @@ export class DirectiveInvasionDefense extends Directive { if (!this.room || this.room.hostiles.length > 0) { this.memory.safeSince = Game.time; } + if (this.room && this.room!.name == 'W13N45') { + CombatIntel.computeCreepDamagePotentialMatrix(this.room.dangerousPlayerHostiles); + } // If there are no hostiles left in the room and everyone's healed, then remove the flag if (this.room && this.room.hostiles.length == 0 && Game.time - this.memory.safeSince > 100 && this.room.hostileStructures.length == 0) { diff --git a/src/intel/CombatIntel.ts b/src/intel/CombatIntel.ts index c10f02cca..a07f90c4e 100644 --- a/src/intel/CombatIntel.ts +++ b/src/intel/CombatIntel.ts @@ -11,6 +11,7 @@ import {boostResources} from '../resources/map_resources'; import {Cartographer} from '../utilities/Cartographer'; import {toCreep, Zerg} from '../zerg/Zerg'; import {RoomIntel} from './RoomIntel'; +import {Visualizer} from "../visuals/Visualizer"; interface CombatIntelMemory { cache: { @@ -96,6 +97,31 @@ export class CombatIntel { } + static computeCreepDamagePotentialMatrix(room: Room, creeps: Creep[], startingMatrix?: CostMatrix): CostMatrix | undefined { + if (room) { + const matrix = startingMatrix || new PathFinder.CostMatrix(); + + creeps.forEach(creep => { + const meleeAttack = CombatIntel.getAttackPotential(creep); + const rangedAttack = CombatIntel.getRangedAttackPotential(creep); + // const heal = CombatIntel.getHealPotential(creep); + if (meleeAttack > 0) { + creep.pos.neighbors.forEach(pos => + matrix.set(pos.x, pos.y, matrix.get(pos.x, pos.y) + meleeAttack * ATTACK_POWER)) + } + if (rangedAttack > 0) { + creep.pos.getPositionsInRange(3).forEach(pos => + matrix.set(pos.x, pos.y, matrix.get(pos.x, pos.y) + rangedAttack * RANGED_ATTACK_POWER)) + } + }); + + Visualizer.displayCostMatrix(matrix, room.name); + + return matrix; + } + } + + // Fallback and exit calculations ================================================================================== private findBestExit(matrix: CostMatrix, towers: StructureTower[], From 75385eec3a6db5cb048c484707625169879fa48b Mon Sep 17 00:00:00 2001 From: Matthew Roy Date: Wed, 17 Jul 2019 11:24:25 -0700 Subject: [PATCH 137/137] Adding power spawn processing --- src/Colony.ts | 8 ++++++++ src/hiveClusters/commandCenter.ts | 8 ++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Colony.ts b/src/Colony.ts index c23be5588..451937220 100644 --- a/src/Colony.ts +++ b/src/Colony.ts @@ -546,6 +546,13 @@ export class Colony { return allAssets; } + private runPowerSpawn() { + if (this.powerSpawn && this.assets.energy > 300000 && this.powerSpawn.energy > 50 + && this.powerSpawn.power > 0) { + this.powerSpawn.processPower(); + } + } + /** * Initializes the state of the colony each tick */ @@ -567,6 +574,7 @@ export class Colony { this.linkNetwork.run(); // Run the link network this.roadLogistics.run(); // Run the road network this.roomPlanner.run(); // Run the room planner + this.runPowerSpawn(); // Run power spawn - short term this.stats(); // Log stats per tick } diff --git a/src/hiveClusters/commandCenter.ts b/src/hiveClusters/commandCenter.ts index 51e0c07ef..fc799f281 100644 --- a/src/hiveClusters/commandCenter.ts +++ b/src/hiveClusters/commandCenter.ts @@ -135,8 +135,12 @@ export class CommandCenter extends HiveCluster { } } // Refill power spawn - if (this.powerSpawn && this.powerSpawn.energy < this.powerSpawn.energyCapacity) { - this.transportRequests.requestInput(this.powerSpawn, Priority.NormalLow); + if (this.powerSpawn) { + if (this.powerSpawn.energy < this.powerSpawn.energyCapacity) { + this.transportRequests.requestInput(this.powerSpawn, Priority.NormalLow); + } else if (this.powerSpawn.power < this.powerSpawn.powerCapacity) { + this.transportRequests.requestInput(this.powerSpawn, Priority.Low, {resourceType: RESOURCE_POWER}); + } } // Refill nuker with low priority if (this.nuker) {