Skip to content

Commit

Permalink
Abathur now checks to see if colony can acquire ingredients for a rea…
Browse files Browse the repository at this point in the history
…ction before assigning it
  • Loading branch information
bencbartlett committed Jun 16, 2018
1 parent 08638dd commit c074be1
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 58 deletions.
6 changes: 3 additions & 3 deletions src/Overmind_obfuscated.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/declarations/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ interface ITradeNetwork {
sell(terminal: StructureTerminal, resource: ResourceConstant, amount?: number): void;

buyMineral(terminal: StructureTerminal, mineralType: ResourceConstant, amount: number): void;

init(): void;

run(): void;
}

declare var Overmind: IOvermind;
Expand Down
2 changes: 1 addition & 1 deletion src/directives/defense/invasionDefense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class DirectiveInvasionDefense extends Directive {
hostile.getActiveBodyparts(WORK) > 0);
let rangedHostiles = _.filter(this.room.hostiles, hostile => hostile.getActiveBodyparts(RANGED_ATTACK) > 0);
if (this.colony.stage > ColonyStage.Larva && percentWalls > 0.5) {
this.overlords.archer = new RangedDefenseOverlord(this, useBoosts);
this.overlords.rangedDefense = new RangedDefenseOverlord(this, useBoosts);
} else if (meleeHostiles.length > 0) {
this.overlords.meleeDefense = new MeleeDefenseOverlord(this, useBoosts);
} else if (Game.time % 10 == 0) {
Expand Down
3 changes: 2 additions & 1 deletion src/hiveClusters/evolutionChamber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {TransportRequestGroup} from '../logistics/TransportRequestGroup';
import {Priority} from '../priorities/priorities';
import {Zerg} from '../Zerg';
import {TraderJoe} from '../logistics/TradeNetwork';
import {rightArrow} from '../utilities/stringConstants';

const LabStatus = {
Idle : 0,
Expand Down Expand Up @@ -151,7 +152,7 @@ export class EvolutionChamber extends HiveCluster {
case LabStatus.Idle:
if (this.memory.activeReaction) {
let [ing1, ing2] = REAGENTS[this.memory.activeReaction.mineralType];
log.info(`${this.room.print}: starting synthesis of ${ing1} + ${ing2} -> ` +
log.info(`${this.room.print}: starting synthesis of ${ing1} + ${ing2} ${rightArrow} ` +
this.memory.activeReaction.mineralType);
this.memory.status = LabStatus.AcquiringMinerals;
this.memory.statusTick = Game.time;
Expand Down
9 changes: 9 additions & 0 deletions src/intel/combatIntel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export class CombatIntel {
}
}

// Cost matrix calculations

private computeCostMatrix(): CostMatrix | undefined {
if (this.room) {
let matrix = new PathFinder.CostMatrix();
Expand Down Expand Up @@ -257,6 +259,13 @@ export class CombatIntel {
return selfHealing + neighborHealing + rangedHealing;
}

// Creep position calculations =====================================================================================

// // Distance from a given creep to the nearest rampart or wall; Infinity if no barriers in room
// static distanceToBarrier(creep: Creep): number {
//
// }

static getPositionsNearEnemies(hostiles: Creep[], range = 0): RoomPosition[] {
return _.unique(_.flatten(_.map(hostiles, hostile =>
hostile.pos.getPositionsInRange(range, false, true))));
Expand Down
5 changes: 4 additions & 1 deletion src/logistics/LogisticsNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {EnergyStructure, isEnergyStructure, isStoreStructure, StoreStructure} fr
import {DirectiveLogisticsRequest} from '../directives/logistics/logisticsRequest';
import {Mem} from '../Memory';
import {TransporterSetup} from '../overlords/core/transporter';
import {minMax} from '../utilities/utils';

export type LogisticsTarget =
EnergyStructure
Expand Down Expand Up @@ -325,12 +326,14 @@ export class LogisticsNetwork {
let request = this.requests[requestID];
if (request) {
let carry = transporter.carry;
let remainingCapacity = transporter.carryCapacity - _.sum(carry);
let resourceAmount = -1 * this.predictedRequestAmount(transporter, request, nextAvailability);
// ^ need to multiply amount by -1 since transporter is doing complement of what request needs
if (carry[request.resourceType]) {
carry[request.resourceType]! += resourceAmount;
carry[request.resourceType] = minMax(carry[request.resourceType]!, 0, remainingCapacity);
} else {
carry[request.resourceType] = Math.max(resourceAmount, 0);
carry[request.resourceType] = minMax(resourceAmount, 0, remainingCapacity);
}
return carry;
}
Expand Down
21 changes: 16 additions & 5 deletions src/logistics/TradeNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ const TraderMemoryDefaults: TraderMemory = {
equalizeIndex: 0,
};

// Maximum prices I'm willing to pay to buy various resources - based on shard2 market data in June 2018
// (might not always be up to date)
export const maxMarketPrices: { [resourceType: string]: number } = {
default : 5.0,
[RESOURCE_HYDROGEN] : 0.3,
[RESOURCE_OXYGEN] : 0.25,
[RESOURCE_UTRIUM] : 0.3,
[RESOURCE_LEMERGIUM]: 0.25,
[RESOURCE_KEANIUM] : 0.25,
[RESOURCE_ZYNTHIUM] : 0.25,
[RESOURCE_CATALYST] : 0.4,
};

@profile
export class TraderJoe implements ITradeNetwork {

Expand All @@ -33,9 +46,6 @@ export class TraderJoe implements ITradeNetwork {
market: {
reserveCredits: 10000, // Always try to stay above this amount
boostCredits : 15000, // You can buy boosts directly off market while above this amount
maxPrice : { // Maximum price you're willing to pay for various resources
default: 5.0,
},
orders : {
timeout : 100000, // Remove sell orders after this many ticks if remaining amount < cleanupAmount
cleanupAmount: 10, // RemainingAmount threshold to remove expiring orders
Expand Down Expand Up @@ -241,7 +251,7 @@ export class TraderJoe implements ITradeNetwork {
order => order.type == ORDER_SELL && order.resourceType == mineralType && order.amount >= amount
);
let bestOrder = minBy(ordersForMineral, (order: Order) => order.price);
let maxPrice = TraderJoe.settings.market.maxPrice.default;
let maxPrice = maxMarketPrices[mineralType] || maxMarketPrices.default;
if (bestOrder && bestOrder.price <= maxPrice) {
let response = Game.market.deal(bestOrder.id, amount, terminal.room.name);
this.logTransaction(bestOrder, terminal.room.name, amount, response);
Expand All @@ -250,10 +260,11 @@ export class TraderJoe implements ITradeNetwork {

private logTransaction(order: Order, destinationRoomName: string, amount: number, response: number): void {
let action = order.type == ORDER_SELL ? 'bought' : 'sold';
let cost = (order.price * amount).toFixed(2);
let fee = order.roomName ? Game.market.calcTransactionCost(amount, order.roomName, destinationRoomName) : 0;
let roomName = Game.rooms[destinationRoomName] ? Game.rooms[destinationRoomName].print : destinationRoomName;
log.info(`${roomName}: ${action} ${amount} of ${order.resourceType} at ${order.roomName}. ` +
`Price: ${order.price * amount} credits Fee: ${fee} energy Response: ${response}`);
`Price: ${cost} credits Fee: ${fee} energy Response: ${response}`);
}


Expand Down
21 changes: 12 additions & 9 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,18 @@ VersionMigration.run();

// Main loop
function main(): void {
if (Game.cpu.bucket < 500) return; // Don't run anything at low bucket
Mem.clean(); // Clean memory
global.Overmind = new _Overmind(); // Instantiate the Overmind
Overmind.build(); // Build phase: instantiate caches and colony components
Overmind.init(); // Init phase: spawning and energy requests
Overmind.run(); // Run phase: execute state-changing actions
Overmind.visuals(); // Draw visuals
Stats.run(); // Record statistics
sandbox(); // Sandbox: run any testing code
if (Game.cpu.bucket > 500) {
Mem.clean(); // Clean memory
global.Overmind = new _Overmind(); // Instantiate the Overmind
Overmind.build(); // Build phase: instantiate caches and colony components
Overmind.init(); // Init phase: spawning and energy requests
Overmind.run(); // Run phase: execute state-changing actions
Overmind.visuals(); // Draw visuals
Stats.run(); // Record statistics
sandbox(); // Sandbox: run any testing code
} else {
log.warning(`CPU bucket is critically low (${Game.cpu.bucket}) - skipping this tick!`);
}
}

export function loop(): void {
Expand Down
6 changes: 3 additions & 3 deletions src/overlords/core/upgrader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ export class UpgradingOverlord extends Overlord {
return;
}
// Sign controller if needed
if (!this.upgradeSite.controller.signedByMe && // <DO-NOT-MODIFY>: see license
!this.upgradeSite.controller.signedByScreeps) { // <DO-NOT-MODIFY>
upgrader.task = Tasks.signController(this.upgradeSite.controller); // <DO-NOT-MODIFY>
if (!this.upgradeSite.controller.signedByMe &&
!this.upgradeSite.controller.signedByScreeps) {
upgrader.task = Tasks.signController(this.upgradeSite.controller);
return;
}
upgrader.task = Tasks.upgrade(this.upgradeSite.controller);
Expand Down
4 changes: 2 additions & 2 deletions src/overlords/core/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ export class WorkerOverlord extends Overlord {
let workPartsPerWorker = _.filter(this.generateProtoCreep(setup).body, part => part == WORK).length;
if (this.colony.stage == ColonyStage.Larva) {
// At lower levels, try to saturate the energy throughput of the colony
let MAX_WORKERS = 7; // Maximum number of workers to spawn
const MAX_WORKERS = 7; // Maximum number of workers to spawn
let energyPerTick = _.sum(_.map(this.colony.miningSites, site => site.energyPerTick));
let energyPerTickPerWorker = 1.1 * workPartsPerWorker; // Average energy per tick when workers are working
let workerUptime = 0.8;
let numWorkers = Math.ceil(energyPerTick / (energyPerTickPerWorker * workerUptime));
this.wishlist(Math.min(numWorkers, MAX_WORKERS), setup);
} else {
// At higher levels, spawn workers based on construction and repair that needs to be done
let MAX_WORKERS = 3; // Maximum number of workers to spawn
const MAX_WORKERS = 4; // Maximum number of workers to spawn
let constructionTicks = _.sum(_.map(this.colony.constructionSites,
site => Math.max(site.progressTotal - site.progress, 0)))
/ BUILD_POWER; // Math.max for if you manually set progress on private server
Expand Down
28 changes: 12 additions & 16 deletions src/overlords/defense/rangedDefense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import {Zerg} from '../../Zerg';
import {OverlordPriority} from '../../priorities/priorities_overlords';
import {DirectiveTargetSiege} from '../../directives/targeting/siegeTarget';
import {CombatOverlord} from '../CombatOverlord';
import {CreepSetup} from '../CreepSetup';
import {boostResources} from '../../resources/map_resources';
Expand All @@ -11,7 +10,12 @@ import {profile} from '../../profiler/decorator';
import {CombatIntel} from '../../intel/combatIntel';

const HydraliskSetup = new CreepSetup('hydralisk', {
pattern : [RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, HEAL, MOVE, MOVE],
pattern : [RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, HEAL, MOVE, MOVE, MOVE, MOVE],
sizeLimit: Infinity,
});

const BoostedHydraliskSetup = new CreepSetup('hydralisk', {
pattern : [TOUGH, TOUGH, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, HEAL, MOVE],
sizeLimit: Infinity,
});

Expand Down Expand Up @@ -49,20 +53,12 @@ export class RangedDefenseOverlord extends CombatOverlord {

private findTarget(archer: Zerg): Creep | Structure | undefined {
if (this.room) {
// Prioritize specifically targeted structures first
let targetingDirectives = DirectiveTargetSiege.find(this.room.flags) as DirectiveTargetSiege[];
let targetedStructures = _.compact(_.map(targetingDirectives,
directive => directive.getTarget())) as Structure[];
if (targetedStructures.length > 0) {
return this.findClosestReachable(archer.pos, targetedStructures);
} else {
// Target nearby hostile creeps
let creepTarget = this.findClosestHostile(archer, false, false);
if (creepTarget) return creepTarget;
// Target nearby hostile structures
let structureTarget = this.findClosestPrioritizedStructure(archer);
if (structureTarget) return structureTarget;
}
// Target nearby hostile creeps
let creepTarget = this.findClosestHostile(archer, false, false);
if (creepTarget) return creepTarget;
// Target nearby hostile structures
let structureTarget = this.findClosestPrioritizedStructure(archer);
if (structureTarget) return structureTarget;
}
}

Expand Down
74 changes: 62 additions & 12 deletions src/resources/Abathur.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {Colony, getAllColonies} from '../Colony';
import {REAGENTS} from './map_resources';
import {mergeSum, minMax} from '../utilities/utils';
import {profile} from '../profiler/decorator';
import {maxMarketPrices, TraderJoe} from '../logistics/TradeNetwork';
import {Mem} from '../Memory';

const _priorityStock: { [key: string]: number } = {
XGHO2: 1000, // For toughness
Expand Down Expand Up @@ -68,10 +70,19 @@ for (let resourceType in _wantedStock) {
wantedStock.push(stock);
}

interface AbathurMemory {
sleepUntil: number;
}

const AbathurMemoryDefaults = {
sleepUntil: 0
};

@profile
export class Abathur {

colony: Colony;
memory: AbathurMemory;
priorityStock: Reaction[];
wantedStock: Reaction[];
assets: { [resourceType: string]: number };
Expand All @@ -81,10 +92,12 @@ export class Abathur {
static settings = {
minBatchSize: 100, // anything less than this wastes time
maxBatchSize: 1000, // manager carry capacity
sleepTime : 100, // sleep for this many ticks once you can't make anything
};

constructor(colony: Colony) {
this.colony = colony;
this.memory = Mem.wrap(this.colony.memory, 'abathur', AbathurMemoryDefaults);
this.priorityStock = priorityStock;
this.wantedStock = wantedStock;
this.assets = colony.assets;
Expand All @@ -106,32 +119,69 @@ export class Abathur {
return this._globalAssets;
}

private canBuyIngredientsForReaction(reactionQueue: Reaction[]): boolean {
// TODO
return false;
private canReceiveBasicMineralsForReaction(mineralQuantities: { [resourceType: string]: number },
amount: number): boolean {
for (let mineral in mineralQuantities) {
if (!this.someColonyHasExcess(<ResourceConstant>mineral, mineralQuantities[mineral])) {
return false;
}
}
return true;
}

// hasExcess(mineralType: ResourceConstant): boolean {
// return this.assets[mineralType] > Math.max((_wantedStock[mineralType] || 0),
// (_priorityStock[mineralType] || 0));
// }
//
// private someColonyHasExcess(mineralType: ResourceConstant): boolean {
// return _.any(getAllColonies(), colony => colony.abathur.hasExcess(mineralType));
// }

private canBuyBasicMineralsForReaction(mineralQuantities: { [resourceType: string]: number },
priceSensitive = true): boolean {
if (Game.market.credits < TraderJoe.settings.market.reserveCredits) {
return false;
}
for (let mineral in mineralQuantities) {
let maxPrice = maxMarketPrices.default;
if (priceSensitive && maxMarketPrices[mineral]) {
maxPrice = maxMarketPrices[mineral];
}
if (Overmind.tradeNetwork.priceOf(<ResourceConstant>mineral) > maxPrice) {
return false;
}
}
return true;
}

hasExcess(mineralType: ResourceConstant, excessAmount = 0): boolean {
return this.assets[mineralType] - excessAmount > Math.max((_wantedStock[mineralType] || 0),
(_priorityStock[mineralType] || 0));
}

private someColonyHasExcess(mineralType: ResourceConstant, excessAmount = 0): boolean {
return _.any(getAllColonies(), colony => colony.abathur.hasExcess(mineralType, excessAmount));
}

/* Generate a queue of reactions to produce the most needed compound */
getReactionQueue(verbose = false): Reaction[] {
// Return nothing if you are sleeping; prevents wasteful reaction queue calculations
if (Game.time < this.memory.sleepUntil) {
return [];
}
// Compute the reaction queue for the highest priority item that you should be and can be making
let stocksToCheck = [_priorityStock, _wantedStock];
for (let stocks of stocksToCheck) {
for (let resourceType in stocks) {
let amountOwned = this.assets[resourceType] || 0;
let amountNeeded = stocks[resourceType];
if (amountOwned < amountNeeded) { // if there is a shortage of this resource
return this.buildReactionQueue(<ResourceConstant>resourceType, amountNeeded - amountOwned, verbose);
let reactionQueue = this.buildReactionQueue(<ResourceConstant>resourceType,
amountNeeded - amountOwned, verbose);
let missingBaseMinerals = this.getMissingBasicMinerals(reactionQueue);
if (!_.any(missingBaseMinerals)
|| this.canReceiveBasicMineralsForReaction(missingBaseMinerals, amountNeeded + 1000)
|| this.canBuyBasicMineralsForReaction(missingBaseMinerals)) {
return reactionQueue;
}
}
}
}
// If there's nothing you can make, sleep for 100 ticks
this.memory.sleepUntil = Game.time + Abathur.settings.sleepTime;
return [];
}

Expand Down
Loading

0 comments on commit c074be1

Please sign in to comment.