Skip to content

Commit

Permalink
feat: Move all but one transporter source into dispatcher system.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mirroar committed Aug 8, 2024
1 parent f468c8a commit b5df7e4
Show file tree
Hide file tree
Showing 8 changed files with 360 additions and 242 deletions.
7 changes: 6 additions & 1 deletion src/dispatcher/resource-destination/drop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ export default class DropDestination extends TaskProvider<DropDestinationTask, R
addDropResourceTasks(context: ResourceDestinationContext, options: DropDestinationTask[]) {
const creep = context.creep;

const terminal = this.room.terminal;
const terminalNeedsSpaceForEnergy = terminal && (terminal.store.getFreeCapacity() + terminal.store.getUsedCapacity(RESOURCE_ENERGY)) < 5000;
for (const resourceType of getResourcesIn(creep.store)) {
const storageTarget = creep.room.getBestStorageTarget(creep.store[resourceType], resourceType);
if (storageTarget) continue;
const wouldBlockTerminal = storageTarget === terminal
&& terminalNeedsSpaceForEnergy
&& resourceType !== RESOURCE_ENERGY;
if (storageTarget && !wouldBlockTerminal) continue;

// Resources only get dropped if we have nowhere to store them.
options.push({
Expand Down
3 changes: 3 additions & 0 deletions src/dispatcher/resource-destination/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ export default class StorageDestination extends StructureDestination<StorageDest
addStoreResourceTasks(context: ResourceDestinationContext, options: StorageDestinationTask[]) {
const creep = context.creep;

const terminal = this.room.terminal;
const terminalNeedsSpaceForEnergy = terminal && (terminal.store.getFreeCapacity() + terminal.store.getUsedCapacity(RESOURCE_ENERGY)) < 5000;
for (const resourceType of getResourcesIn(creep.store)) {
const storageTarget = creep.room.getBestStorageTarget(creep.store[resourceType], resourceType);
if (!storageTarget) continue;
if (resourceType !== RESOURCE_ENERGY && terminalNeedsSpaceForEnergy) continue;

options.push({
priority: 0,
Expand Down
45 changes: 45 additions & 0 deletions src/dispatcher/resource-source/container.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import StructureSource from 'dispatcher/resource-source/structure';
import { getResourcesIn } from 'utils/store';

interface ContainerSourceTask extends StructureSourceTask {
type: 'container';
Expand All @@ -22,6 +23,7 @@ export default class ContainerSource extends StructureSource<ContainerSourceTask
const options: ContainerSourceTask[] = [];

this.addContainerEnergySourceOptions(options, context);
this.addContainerResourceSourceOptions(options, context);

return options;
}
Expand Down Expand Up @@ -82,4 +84,47 @@ export default class ContainerSource extends StructureSource<ContainerSourceTask
options.push(option);
}
}

private addContainerResourceSourceOptions(options: ContainerSourceTask[], context: ResourceSourceContext) {
const room = this.room;
// We need a decent place to store these resources.
if (!room.terminal && !room.storage) return;

// Take non-energy out of containers.
const containers = room.structuresByType[STRUCTURE_CONTAINER] || [];

for (const container of containers) {
const assignedResourceType = this.getAssignedResourceType(container);
for (const resourceType of getResourcesIn(container.store)) {
if (resourceType === RESOURCE_ENERGY) continue;
if (container.store[resourceType] === 0) continue;
if (
resourceType === assignedResourceType
&& container.store.getUsedCapacity(resourceType) < CONTAINER_CAPACITY / 2
) continue;

const option: ContainerSourceTask = {
priority: 3,
weight: container.store[resourceType] / 20, // @todo Also factor in distance.
type: this.getType(),
target: container.id,
resourceType,
};

option.priority -= room.getCreepsWithOrder('container', container.id).length * 2;

options.push(option);
}
}
}

private getAssignedResourceType(container: StructureContainer): ResourceConstant | null {
for (const mineral of this.room.minerals) {
if (container.id !== mineral.getNearbyContainer()?.id) continue;

return mineral.mineralType;
}

return null;
}
}
5 changes: 5 additions & 0 deletions src/dispatcher/resource-source/dispatcher.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import Dispatcher from 'dispatcher/dispatcher';

import ContainerSource from 'dispatcher/resource-source/container';
import DropSource from 'dispatcher/resource-source/drop';
import FactorySource from 'dispatcher/resource-source/factory';
import GraveSource from 'dispatcher/resource-source/grave';
import LabSource from 'dispatcher/resource-source/lab';
import LinkSource from 'dispatcher/resource-source/link';
import OverfullExtensionSource from 'dispatcher/resource-source/overfull-extension';
Expand All @@ -23,7 +26,9 @@ export default class ResourceSourceDispatcher extends Dispatcher<ResourceSourceT
constructor(readonly room: Room) {
super();
this.addProvider(new ContainerSource(room));
this.addProvider(new DropSource(room));
this.addProvider(new FactorySource(room));
this.addProvider(new GraveSource(room));
this.addProvider(new LabSource(room));
this.addProvider(new LinkSource(room));
this.addProvider(new OverfullExtensionSource(room));
Expand Down
146 changes: 146 additions & 0 deletions src/dispatcher/resource-source/drop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import TaskProvider from 'dispatcher/task-provider';
import { ENEMY_STRENGTH_NONE } from 'room-defense';
import { getDangerMatrix } from 'utils/cost-matrix';

interface DropSourceTask extends ResourceSourceTask {
type: 'drop';
target: Id<Resource>;
}

export default class DropSource extends TaskProvider<DropSourceTask, ResourceSourceContext> {
constructor(readonly room: Room) {
super();
}

getType(): 'drop' {
return 'drop';
}

getHighestPriority() {
return 5;
}

getTasks(context: ResourceSourceContext) {
const options: DropSourceTask[] = [];

this.addDroppedResourceOptions(options, context);

return options;
}

private addDroppedResourceOptions(options: DropSourceTask[], context: ResourceSourceContext) {
const creep = context.creep;

// Look for dropped resources.
const targets = this.room.find(FIND_DROPPED_RESOURCES, {
filter: resource => {
if (resource.amount < 10) return false;

const result = PathFinder.search(creep.pos, resource.pos);
if (result.incomplete) return false;

return true;
},
});

for (const target of targets) {
if (context.resourceType && context.resourceType !== target.resourceType) return;

const option: DropSourceTask = {
priority: 4,
weight: target.amount / (target.resourceType === RESOURCE_ENERGY ? 100 : 30), // @todo Also factor in distance.
type: this.getType(),
target: target.id,
resourceType: target.resourceType,
};

if (target.resourceType === RESOURCE_POWER) {
option.priority++;
}
else if (target.resourceType === RESOURCE_ENERGY) {
// Get storage location, since that is a low priority source for transporters.
const storagePosition = creep.room.getStorageLocation();

if (storagePosition && target.pos.x === storagePosition.x && target.pos.y === storagePosition.y) {
option.priority = creep.memory.role === 'transporter' ? (this.getStoragePriority(creep) + ((creep.room.storage || creep.room.terminal) ? 1 : 0)) : 5;
}
else {
if (target.amount < 100) option.priority--;
if (target.amount < 200) option.priority--;

// If spawn / extensions need filling, transporters should not pick up
// energy from random targets as readily, instead prioritize storage.
if (creep.room.energyAvailable < creep.room.energyCapacityAvailable && creep.room.getCurrentResourceAmount(RESOURCE_ENERGY) > 5000 && creep.memory.role === 'transporter') option.priority -= 2;

if (creep.room.storage && creep.room.getFreeStorage() < target.amount && creep.room.getEffectiveAvailableEnergy() > 20_000) {
// If storage is super full, try leaving stuff on the ground.
option.priority -= 2;
}

}

}

if (creep.room.getFreeStorage() < target.amount) {
// If storage is super full, try leaving stuff on the ground.
continue;
}

if (target.amount < creep.store.getCapacity() * 2) {
// We only need to limit the number of creeps picking up resources if the amount is small.
option.priority -= this.room.getCreepsWithOrder('drop', target.id).length * 2;
}

options.push(option);
}
}

getStoragePriority(creep: Creep): number {
const room = creep.room;
if (room.energyAvailable < room.energyCapacityAvailable * 0.9) {
// Spawning is important, so get energy when needed.
return 4;
}

if (room.terminal && room.storage && room.storage.store.energy > 5000 && room.terminal.store.energy < room.storage.store.energy * 0.05 && !room.isClearingTerminal()) {
// Take some energy out of storage to put into terminal from time to time.
// @todo This really should be in the resource destination for the terminal.
return 2;
}

return 0;
}

isValid(task: DropSourceTask, context: ResourceSourceContext): boolean {
const resource = Game.getObjectById(task.target);
if (!resource) return false;
if (resource.amount === 0) return false;
if (context.creep.store.getFreeCapacity(resource.resourceType) === 0) return false;
if (!this.isSafePosition(context.creep, resource.pos)) return false;

const terminal = this.room.terminal;
const terminalNeedsSpaceForEnergy = terminal && (terminal.store.getFreeCapacity() + terminal.store.getUsedCapacity(RESOURCE_ENERGY)) < 5000;
if (task.resourceType !== RESOURCE_ENERGY && terminalNeedsSpaceForEnergy) return false;

return true;
}

isSafePosition(creep: Creep | PowerCreep, pos: RoomPosition): boolean {
if (!creep.room.isMine()) return true;
if (creep.room.defense.getEnemyStrength() === ENEMY_STRENGTH_NONE) return true;

const matrix = getDangerMatrix(creep.room.name);
if (matrix.get(pos.x, pos.y) > 0) return false;

return true;
}

execute(task: DropSourceTask, context: ResourceSourceContext): void {
const creep = context.creep;
const target = Game.getObjectById(task.target);

creep.whenInRange(1, target, () => {
creep.pickup(target);
});
}
}
120 changes: 120 additions & 0 deletions src/dispatcher/resource-source/grave.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import TaskProvider from "dispatcher/task-provider";
import { ENEMY_STRENGTH_NONE } from "room-defense";
import { getDangerMatrix } from "utils/cost-matrix";
import { getResourcesIn } from "utils/store";

interface GraveSourceTask extends ResourceSourceTask {
type: 'grave';
target: Id<Tombstone | Ruin>;
}

export default class GraveSource extends TaskProvider<GraveSourceTask, ResourceSourceContext> {
constructor(readonly room: Room) {
super();
}

getType(): 'grave' {
return 'grave';
}

getHighestPriority() {
return 5;
}

getTasks(context: ResourceSourceContext) {
const options: GraveSourceTask[] = [];

this.addGraveResourceOptions(options, context);

return options;
}

private addGraveResourceOptions(options: GraveSourceTask[], context: ResourceSourceContext) {
const creep = context.creep;

// Look for tombstones and ruins with resources.
const targets = (this.room.find(FIND_TOMBSTONES) as Array<Tombstone | Ruin>)
.concat(this.room.find(FIND_RUINS))
.filter(target => {
return target.store.getUsedCapacity() > 10;
});

for (const target of targets) {
// @todo It might be more effective to only create one task per tombstone / ruin and pick up all resources at once.
for (const resourceType of getResourcesIn(target.store)) {
if (context.resourceType && resourceType !== context.resourceType) continue;

const amount = target.store.getUsedCapacity(resourceType);
const option: GraveSourceTask = {
priority: 4,
weight: amount / (resourceType === RESOURCE_ENERGY ? 100 : 30), // @todo Also factor in distance.
type: this.getType(),
target: target.id,
resourceType: resourceType,
};

if (resourceType === RESOURCE_POWER) {
option.priority++;
}
else if (resourceType === RESOURCE_ENERGY) {
if (amount < 100) option.priority--;
if (amount < 200) option.priority--;

// If spawn / extensions need filling, transporters should not pick up
// energy from random targets as readily, instead prioritize storage.
if (creep.room.energyAvailable < creep.room.energyCapacityAvailable && creep.room.getCurrentResourceAmount(RESOURCE_ENERGY) > 5000 && creep.memory.role === 'transporter') option.priority -= 2;

if (creep.room.storage && creep.room.getFreeStorage() < amount && creep.room.getEffectiveAvailableEnergy() > 20_000) {
// If storage is super full, try leaving stuff on the ground.
option.priority -= 2;
}
}

if (creep.room.getFreeStorage() < target.store.getUsedCapacity(resourceType)) {
// If storage is super full, try leaving stuff on the ground.
continue;
}

if (target.store.getUsedCapacity() < creep.store.getCapacity() * 2) {
// We only need to limit the number of creeps picking up resources if the amount is small.
option.priority -= this.room.getCreepsWithOrder('grave', target.id).length * 2;
}

options.push(option);
}
}
}

isValid(task: GraveSourceTask, context: ResourceSourceContext): boolean {
const tombstone = Game.getObjectById(task.target);
if (!tombstone) return false;
if (tombstone.store.getUsedCapacity(task.resourceType) === 0) return false;
if (context.creep.store.getFreeCapacity(task.resourceType) === 0) return false;
if (!this.isSafePosition(context.creep, tombstone.pos)) return false;

const terminal = this.room.terminal;
const terminalNeedsSpaceForEnergy = terminal && (terminal.store.getFreeCapacity() + terminal.store.getUsedCapacity(RESOURCE_ENERGY)) < 5000;
if (task.resourceType !== RESOURCE_ENERGY && terminalNeedsSpaceForEnergy) return false;

return true;
}

isSafePosition(creep: Creep | PowerCreep, pos: RoomPosition): boolean {
if (!creep.room.isMine()) return true;
if (creep.room.defense.getEnemyStrength() === ENEMY_STRENGTH_NONE) return true;

const matrix = getDangerMatrix(creep.room.name);
if (matrix.get(pos.x, pos.y) > 0) return false;

return true;
}

execute(task: GraveSourceTask, context: ResourceSourceContext): void {
const creep = context.creep;
const target = Game.getObjectById(task.target);

creep.whenInRange(1, target, () => {
creep.withdraw(target, task.resourceType);
});
}
}
Loading

0 comments on commit b5df7e4

Please sign in to comment.