Skip to content

Instantly share code, notes, and snippets.

@RGBKnights
Created April 11, 2023 18:01
Show Gist options
  • Save RGBKnights/1bbbb2a020bdd15376b21d122079b586 to your computer and use it in GitHub Desktop.
Save RGBKnights/1bbbb2a020bdd15376b21d122079b586 to your computer and use it in GitHub Desktop.
Screeps Arena - Generators
import {
ATTACK,
BODYPART_COST,
BodyPartConstant,
CARRY,
ERR_BUSY,
ERR_FULL,
ERR_INVALID_ARGS,
ERR_INVALID_TARGET,
ERR_NOT_ENOUGH_ENERGY,
ERR_NOT_ENOUGH_RESOURCES,
ERR_NOT_IN_RANGE,
ERR_NOT_OWNER,
ERR_NO_BODYPART,
ERR_TIRED,
HEAL,
MOVE,
OK,
RESOURCE_ENERGY,
ResourceConstant,
TOUGH,
WORK
} from "game/constants";
import { CostMatrix, MoveToOpts } from "game/path-finder";
import { Creep, GameObject, Store, Structure, StructureContainer, StructureSpawn } from "game/prototypes";
import { getCpuTime, getObjectsByPrototype } from "game/utils";
import { arenaInfo } from "game";
class Context {
private counter: number;
private logCpuTime: boolean;
private generators: Map<number, Generator<void, void, void>>;
public constructor(logCpuTime = false, iGenerators: Generator<void, void, void>[] = []) {
this.counter = 0;
this.logCpuTime = logCpuTime;
this.generators = new Map<number, Generator<void, void, void>>(iGenerators.map((obj, i) => [i, obj] as [number, Generator<void, void, void>]));
}
public add(g: Generator<void, void, void>): void {
this.generators.set(this.counter++, g);
}
public update(): boolean {
for (const [key, generator] of this.generators) {
try {
const result = generator.next();
if (result.done === true) this.generators.delete(key);
} catch (error: unknown) {
this.generators.delete(key);
if (error instanceof String) {
console.log("Error", error);
} else if (error instanceof Error) {
console.log(error.name, error.message);
}
}
}
if (this.logCpuTime) {
const limit = (getCpuTime() / arenaInfo.cpuTimeLimit) * 100;
console.log(`Generator CPU: ${limit.toFixed(1)}%`);
}
return this.generators.size > 0;
}
}
export const context = new Context(true, []);
function* awaitProduction(spawn: StructureSpawn): Generator<void, void, void> {
while (spawn.spawning) {
yield;
}
}
function* awaitEnergy<S extends { store: Store<ResourceConstant> }>(s: S, required: number): Generator<void, void, void> {
while (s.store.energy < required) {
yield;
}
}
function* queueProduction<C extends Creep>(body: BodyPartConstant[]): Generator<void, C, void> {
const spawn = getObjectsByPrototype(StructureSpawn).find(s => s.my === true) as StructureSpawn;
const required = body.map(b => BODYPART_COST[b]).reduce((sum, c) => sum + c, 0);
yield* awaitEnergy(spawn, required);
const result = spawn.spawnCreep(body);
switch (result.error) {
case ERR_BUSY:
throw new Error(`Cant spawn creep [${body.toString()}] because the spawn is already busy`);
case ERR_INVALID_ARGS:
throw new Error(`Cant spawn creep [${body.toString()}] because body is invalid`);
case ERR_NOT_ENOUGH_ENERGY:
throw new Error(`Cant spawn creep [${body.toString()}] because not enough energy`);
default:
yield;
break;
}
yield* awaitProduction(spawn);
return result.object as C;
}
function* withdraw(creep: Creep, target: Structure): Generator<void, void, void> {
while (creep.exists && target.exists) {
const result = creep.withdraw(target, RESOURCE_ENERGY);
switch (result) {
case ERR_NOT_OWNER:
throw new Error(`Creep ${creep.id} is not mine`);
case ERR_INVALID_ARGS:
throw new Error(`Creep ${creep.id} dose not have a CARRY body part`);
case ERR_INVALID_TARGET:
throw new Error(`Target ${target.id} is invalid`);
case ERR_NOT_ENOUGH_RESOURCES:
case ERR_FULL:
return;
case ERR_NOT_IN_RANGE:
case OK:
yield;
break;
default:
throw new Error(`Unexpected Error: ${result}`);
}
}
}
function* transfer(creep: Creep, target: Structure): Generator<void, void, void> {
while (creep.exists && target.exists) {
const result = creep.transfer(target, RESOURCE_ENERGY);
switch (result) {
case ERR_NOT_OWNER:
throw new Error(`Creep ${creep.id} is not mine`);
case ERR_INVALID_ARGS:
throw new Error(`Creep ${creep.id} dose not have a CARRY body part`);
case ERR_INVALID_TARGET:
throw new Error(`Creep ${creep.id} has in invalid target ${target.id}`);
case ERR_NOT_ENOUGH_RESOURCES:
case ERR_FULL:
return;
case ERR_NOT_IN_RANGE:
case OK:
yield;
break;
default:
throw new Error(`Unexpected Error: ${result}`);
}
}
}
function* move<T extends GameObject>(creep: Creep, target: T, range = 1): Generator<void, void, void> {
// TODO: Create path...
while (creep.exists && target.exists) {
switch (creep.moveTo(target)) {
case ERR_NOT_OWNER:
throw new Error(`Creep ${creep.id} is not mine`);
case ERR_NO_BODYPART:
return;
case ERR_TIRED:
yield;
break;
case OK:
yield;
if (creep.getRangeTo(target) > range) {
break;
} else {
return;
}
default:
throw new Error(`Unexpected Error`);
}
}
}
function* whenAll(...generators: Generator<void, void, void>[]): Generator<void, void, void> {
const ctx = new Context(false, generators);
while (ctx.update()) {
yield;
}
}
function part(p: BodyPartConstant, a: number, includeMovement = false): BodyPartConstant[] {
return [...Array<BodyPartConstant>(a).fill(p), ...Array<BodyPartConstant>(includeMovement ? a : 0).fill(MOVE)];
}
const WORKER = [...part(MOVE, 6), ...part(CARRY, 2), ...part(WORK, 1)];
const ATTACKER = [...part(TOUGH, 6), ...part(MOVE, 6), ...part(ATTACK, 8)];
const HEALER = [...part(MOVE, 5), ...part(HEAL, 3)];
function* production(): Generator<void, void, void> {
const c1 = yield* queueProduction<Creep>(WORKER);
context.add(worker(c1));
for (;;) {
const c4 = yield* queueProduction<Creep>(ATTACKER);
const c5 = yield* queueProduction<Creep>(ATTACKER);
const c6 = yield* queueProduction<Creep>(HEALER);
context.add(squad([c4, c5, c6]));
}
}
function* worker(creep: Creep): Generator<void, void, void> {
const spawn = getObjectsByPrototype(StructureSpawn).find(s => s.my === true) as StructureSpawn;
const containers = getObjectsByPrototype(StructureContainer);
const collection = spawn.findInRange(containers, 10);
for (const container of collection) {
for (; container.exists && container.store.energy > 0; ) {
yield* whenAll(move(creep, container), withdraw(creep, container));
yield* whenAll(move(creep, spawn), transfer(creep, spawn));
}
}
}
function* squad(creeps: Creep[]): Generator<void, void, void> {
const spawn = getObjectsByPrototype(StructureSpawn).find(s => s.my === false);
if (spawn === undefined) throw new Error("No Enemy Spawn");
for (;;) {
// Build Cost Matrix for Area
const cost = new CostMatrix();
cost.set(0, 0, 0);
const options = {
flee: false,
maxOps: 1000,
maxCost: 500,
heuristicWeight: 1.2,
range: 1,
plainCost: 2,
swampCost: 10,
costMatrix: cost,
ignore: []
} as MoveToOpts;
for (const creep of creeps) {
creep.moveTo(spawn, options);
creep.attack(spawn);
// creep.attack(creeps)
}
yield;
}
}
// Default Generators
context.add(production());
export function loop(): void {
context.update();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment