Add a new Room Layout Generator

This commit is contained in:
uwap 2026-01-02 23:28:03 +01:00
parent 3922f367a2
commit 28d2b2d38b
19 changed files with 499 additions and 362 deletions

View file

@ -1,62 +0,0 @@
declare global {
interface CreepMemory {
state?: number;
}
}
export interface ChainableAction {
or: (action: Action) => ChainableAction;
and: (action: Action) => ChainableAction;
andThen: (action: Action) => ChainableAction;
repeat: () => void;
}
export const NoOp: Action = (creep: Creep, state: number = 0) => ({
or: () => NoOp(creep, state),
and: () => NoOp(creep, state),
andThen: () => NoOp(creep, state),
repeat: () => {},
});
export const Success: Action = (creep: Creep, state: number = 0) => ({
or: () => Success(creep, state),
and: (action: Action) => action(creep, state),
andThen: (action: Action) => {
creep.memory.state = state + 1;
return action(creep, state + 1);
},
repeat: () => {
creep.memory.state = 0;
},
});
export const InProgress: Action = (creep: Creep, state: number = 0) => ({
or: () => InProgress(creep, state),
and: (action: Action) => action(creep, state),
andThen: () => NoOp(creep, state),
repeat: () => {},
});
export const Fail: Action = (creep: Creep, state: number = 0) => ({
or: (action: Action) => action(creep, state),
and: () => NoOp(creep, state),
andThen: () => NoOp(creep, state),
repeat: () => {
console.log("Warning: Last task in series failed for creep " + creep.name);
},
});
export const createAction
= (name: string, action: (c: Creep) => Action): Action => {
return (creep: Creep, state: number = 0) => {
if ((creep.memory.state ?? 0) > state) {
return Success(creep, state);
}
return action(creep)(creep, state);
};
};
export const runAction = (creep: Creep, action: Action): ChainableAction =>
action(creep);
export type Action = (creep: Creep, state?: number) => ChainableAction;

View file

@ -1,24 +0,0 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const buildConstructionSite = () =>
createAction("buildConstructionSite", (creep: Creep) => {
const cs = creep.pos.findClosestByRange(FIND_CONSTRUCTION_SITES);
if (!cs) {
return Fail;
}
switch (creep.build(cs)) {
case OK: {
return InProgress;
}
case ERR_NOT_ENOUGH_RESOURCES: {
return Success;
}
case ERR_NOT_IN_RANGE: {
return moveTo(cs);
}
default: {
return Fail;
}
}
});

View file

@ -1,24 +0,0 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const harvestFromClosestActiveSource = () =>
createAction("harvestFromClosestActiveSource", (creep: Creep) => {
const source = creep.pos.findClosestByPath(FIND_SOURCES_ACTIVE);
if (!source) {
return Fail;
}
if (creep.store.getFreeCapacity(RESOURCE_ENERGY) === 0) {
return Success;
}
switch (creep.harvest(source)) {
case OK: {
return InProgress;
}
case ERR_NOT_IN_RANGE: {
return moveTo(source);
}
default: {
return Fail;
}
}
});

View file

@ -1,16 +0,0 @@
import { createAction, Fail, InProgress } from "./Action";
export const moveTo = (pos: _HasRoomPosition | RoomPosition) =>
createAction("moveTo", (creep: Creep) => {
switch (creep.travelTo(pos)) {
case OK: {
return InProgress;
}
case ERR_TIRED: {
return InProgress;
}
default: {
return Fail;
}
}
});

View file

@ -1,25 +0,0 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const repairStructure = () =>
createAction("repairStructure", (creep: Creep) => {
const cs = creep.pos.findClosestByRange(FIND_STRUCTURES,
{ filter: str => str.hits < str.hitsMax * 0.8 });
if (!cs) {
return Fail;
}
switch (creep.repair(cs)) {
case OK: {
return InProgress;
}
case ERR_NOT_ENOUGH_RESOURCES: {
return Success;
}
case ERR_NOT_IN_RANGE: {
return moveTo(cs);
}
default: {
return Fail;
}
}
});

View file

@ -1,33 +0,0 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
type TransferTarget = Creep
| StructureSpawn
| StructureContainer
| StructureStorage
| StructureExtension
| StructureTower;
export const transferEnergy = (target: TransferTarget | null) =>
createAction("transferEnergy", (creep: Creep) => {
if (target == null) {
return Fail;
}
if (target.store.getFreeCapacity(RESOURCE_ENERGY) === 0) {
return Fail;
}
switch (creep.transfer(target, RESOURCE_ENERGY)) {
case OK: {
return InProgress;
}
case ERR_NOT_ENOUGH_RESOURCES: {
return Success;
}
case ERR_NOT_IN_RANGE: {
return moveTo(target);
}
default: {
return Fail;
}
}
});

View file

@ -1,20 +0,0 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const upgradeController = (controller: StructureController) =>
createAction("upgradeController", (creep: Creep) => {
switch (creep.upgradeController(controller)) {
case OK: {
return InProgress;
}
case ERR_NOT_ENOUGH_RESOURCES: {
return Success;
}
case ERR_NOT_IN_RANGE: {
return moveTo(controller);
}
default: {
return Fail;
}
}
});

View file

@ -1,27 +0,0 @@
import { createAction, Fail, InProgress, Success } from "./Action";
import { moveTo } from "./moveTo";
export const withdrawEnergy
= (target: StructureContainer | StructureStorage | null) =>
createAction("withdrawEnergy", (creep: Creep) => {
if (target == null) {
return Fail;
}
if (target.store.getUsedCapacity(RESOURCE_ENERGY) === 0) {
return Fail;
}
switch (creep.withdraw(target, RESOURCE_ENERGY)) {
case OK: {
return InProgress;
}
case ERR_FULL: {
return Success;
}
case ERR_NOT_IN_RANGE: {
return moveTo(target);
}
default: {
return Fail;
}
}
});

View file

@ -1,47 +0,0 @@
export const buildContainers = (room: Room) => {
const controller = room.controller;
if (controller == null) {
return;
}
if (controller.level < 2
&& room.find(FIND_MY_STRUCTURES,
{ filter: STRUCTURE_EXTENSION }).length > 1) {
return;
}
const sources = room.find(FIND_SOURCES);
for (const source of sources) {
const pos = source.pos;
room.createConstructionSite(pos.x - 1, pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x + 1, pos.y, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x, pos.y - 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x, pos.y + 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x - 1, pos.y + 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x + 1, pos.y - 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x - 1, pos.y - 1, STRUCTURE_CONTAINER);
room.createConstructionSite(pos.x + 1, pos.y + 1, STRUCTURE_CONTAINER);
}
if (controller.level < 4) {
return;
}
const terrain = room.getTerrain();
if (terrain.get(controller.pos.x, controller.pos.y - 3)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x, controller.pos.y - 3, STRUCTURE_STORAGE);
}
else if (terrain.get(controller.pos.x, controller.pos.y + 3)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x, controller.pos.y + 3, STRUCTURE_STORAGE);
}
else if (terrain.get(controller.pos.x - 3, controller.pos.y)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x - 3, controller.pos.y, STRUCTURE_STORAGE);
}
else if (terrain.get(controller.pos.x + 3, controller.pos.y)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
controller.pos.x + 3, controller.pos.y, STRUCTURE_STORAGE);
}
};

View file

@ -1,34 +0,0 @@
const extentionsAvailable = (roomlevel: number) => {
return roomlevel > 2 ? roomlevel * 10 - 20 : (roomlevel - 1) * 5;
};
export const buildExtentions = (room: Room) => {
const spawns = room.find(FIND_MY_SPAWNS);
if (spawns.length < 1) return;
const spawn = spawns[0];
const exts = extentionsAvailable(room.controller?.level ?? 0);
const terrain = room.getTerrain();
const sqroffset = Math.sqrt(exts) / 2;
for (let x = -Math.floor(sqroffset); x < sqroffset; x++) {
for (let y = -Math.floor(sqroffset); y < sqroffset; y++) {
room.visual.circle(spawn.pos.x + x * 2, spawn.pos.y + y * 2);
if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD);
}
if (terrain.get(spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
spawn.pos.x + x * 2, spawn.pos.y + y * 2 - 1, STRUCTURE_ROAD);
}
if (terrain.get(spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2)
!== TERRAIN_MASK_WALL) {
room.createConstructionSite(
spawn.pos.x + x * 2 - 1, spawn.pos.y + y * 2, STRUCTURE_ROAD);
}
room.createConstructionSite(
spawn.pos.x + x * 2, spawn.pos.y + y * 2, STRUCTURE_EXTENSION);
}
}
};

View file

@ -1,27 +0,0 @@
export const buildRoads = (room: Room) => {
const rc = room.controller?.level ?? 0;
if (rc < 2) {
return;
}
const sources: _HasRoomPosition[] = room.find(FIND_SOURCES);
const sourcesAndSpawns = sources.concat(room.find(FIND_MY_SPAWNS));
const sourcesAndMinerals = sources.concat(room.find(FIND_MINERALS));
room.visual.clear();
for (const source of sourcesAndSpawns) {
for (const source2 of rc > 4 ? sourcesAndMinerals : sources) {
const path = source.pos.findPathTo(source2, {
ignoreCreeps: true,
ignoreRoads: true,
});
for (const point of path) {
if ((point.x === source.pos.x && point.y === source.pos.y)
|| (point.x === source2.pos.x && point.y === source2.pos.y)) {
continue;
}
room.visual.line(point.x, point.y,
point.x - point.dx, point.y - point.dy);
room.createConstructionSite(point.x, point.y, STRUCTURE_ROAD);
}
}
}
};

448
src/RoomPlanner/index.ts Normal file
View file

@ -0,0 +1,448 @@
import profiler from "screeps-profiler";
const roomSize = 50;
const OBSTACLE = 1000;
const UNSET = 999;
declare global {
interface RoomMemory {
_planner: string
}
}
const structureCoding: (BuildableStructureConstant | null)[] =
[null, STRUCTURE_ROAD, STRUCTURE_CONTAINER, STRUCTURE_EXTENSION, STRUCTURE_FACTORY,
STRUCTURE_EXTRACTOR, STRUCTURE_WALL, STRUCTURE_SPAWN, STRUCTURE_POWER_SPAWN,
STRUCTURE_STORAGE, STRUCTURE_NUKER, STRUCTURE_TERMINAL, STRUCTURE_LAB,
STRUCTURE_LINK, STRUCTURE_TOWER, STRUCTURE_RAMPART];
const getCoord = (x: number, y: number): number => {
return x + y * roomSize;
}
const distanceTransform = (mask: (0 | 999 | 1000)[]): number[] => {
let arr: number[] = new Array(...mask);
for (let i = 0; arr.find((x) => x === UNSET) != null; i++) {
for (let x = 0; x < roomSize; x++) {
for (let y = 0; y < roomSize; y++) {
if (arr[getCoord(x, y)] === i) {
for (let dx = -1; dx < 2; dx++) {
for (let dy = -1; dy < 2; dy++) {
if (x + dx < 0 || y + dy < 0 || x + dx >= 50 || y + dy >= 50
|| arr[getCoord(x, y)] === OBSTACLE) {
continue;
}
if (arr[getCoord(x + dx, y + dy)] === UNSET) {
arr[getCoord(x + dx, y + dy)] = i + 1;
}
}
}
}
}
}
}
return arr;
};
/* const renderHeatmap = (mask: number[], visual: RoomVisual) => {
for (let x = 0; x < 50; x++) {
for (let y = 0; y < 50; y++) {
if (mask[getCoord(x, y)] === OBSTACLE) {
continue;
}
visual.rect(x - 0.5, y - 0.5, 1, 1, {
fill: "#" + (127 + mask[getCoord(x, y)] * 3).toString(16)
+ 127..toString(16)
+ (255 - mask[getCoord(x, y)] * 5).toString(16),
opacity: 0.2
}).text(mask[getCoord(x, y)].toString(), x, y + 0.25);
}
}
} */
const createBuildSites = (room: Room) => {
let structures = new Array();
for (let i = 0; i < room.memory._planner.length; i++) {
structures.push(
structureCoding[(room.memory._planner.charCodeAt(i) - 32) & 0b11111]
);
structures.push(
structureCoding[(room.memory._planner.charCodeAt(i) - 32) >> 5]
);
}
for (let x = 0; x < roomSize; x++) {
for (let y = 0; y < roomSize; y++) {
if ((room.controller?.level ?? 0) < 2
&& structures[getCoord(x, y)] != STRUCTURE_CONTAINER) {
continue;
}
if ((room.controller?.level ?? 0) < 3
&& !(structures[getCoord(x, y)] == STRUCTURE_CONTAINER
|| structures[getCoord(x, y)] == STRUCTURE_ROAD
|| structures[getCoord(x, y)] == STRUCTURE_EXTENSION)) {
continue;
}
if ((room.controller?.level ?? 0) < 4
&& !(structures[getCoord(x, y)] == STRUCTURE_CONTAINER
|| structures[getCoord(x, y)] == STRUCTURE_ROAD
|| structures[getCoord(x, y)] == STRUCTURE_EXTENSION
|| structures[getCoord(x, y)] == STRUCTURE_RAMPART
|| structures[getCoord(x, y)] == STRUCTURE_TOWER)) {
continue;
}
if ((room.controller?.level ?? 0) < 5
&& !(structures[getCoord(x, y)] == STRUCTURE_CONTAINER
|| structures[getCoord(x, y)] == STRUCTURE_ROAD
|| structures[getCoord(x, y)] == STRUCTURE_EXTENSION
|| structures[getCoord(x, y)] == STRUCTURE_RAMPART
|| structures[getCoord(x, y)] == STRUCTURE_TOWER
|| structures[getCoord(x, y)] == STRUCTURE_WALL)) {
continue;
}
if ((room.controller?.level ?? 0) < 6
&& !(structures[getCoord(x, y)] == STRUCTURE_CONTAINER
|| structures[getCoord(x, y)] == STRUCTURE_ROAD
|| structures[getCoord(x, y)] == STRUCTURE_EXTENSION
|| structures[getCoord(x, y)] == STRUCTURE_RAMPART
|| structures[getCoord(x, y)] == STRUCTURE_TOWER
|| structures[getCoord(x, y)] == STRUCTURE_WALL
|| structures[getCoord(x, y)] == STRUCTURE_STORAGE)) {
continue;
}
if ((room.controller?.level ?? 0) < 8
&& !(structures[getCoord(x, y)] == STRUCTURE_CONTAINER
|| structures[getCoord(x, y)] == STRUCTURE_ROAD
|| structures[getCoord(x, y)] == STRUCTURE_EXTENSION
|| structures[getCoord(x, y)] == STRUCTURE_RAMPART
|| structures[getCoord(x, y)] == STRUCTURE_TOWER
|| structures[getCoord(x, y)] == STRUCTURE_WALL
|| structures[getCoord(x, y)] == STRUCTURE_STORAGE
|| structures[getCoord(x, y)] == STRUCTURE_EXTRACTOR)) {
continue;
}
if (structures[getCoord(x, y)] != null) {
if (room.createConstructionSite(x, y, structures[getCoord(x, y)])
=== ERR_FULL) {
return;
}
}
}
}
}
export default profiler.registerFN(function RoomPlanner(room: Room) {
if (room.controller == null || !room.controller.my) {
return;
}
if (Game.cpu.bucket < 100 && room.name != "sim") {
return;
}
if (room.name != "sim" && Game.time % 113 !== 0) {
return;
}
if (room.memory._planner != null) {
createBuildSites(room);
return;
}
const terrain = room.getTerrain();
let mask: number[] = new Array(roomSize * roomSize).fill(0);
for (let x = 0; x < roomSize; x++) {
for (let y = 0; y < roomSize; y++) {
mask[getCoord(x, y)] = terrain.get(x, y);
}
}
const wallDistance = distanceTransform(
mask.map((t) => t === TERRAIN_MASK_WALL ? 0 : UNSET));
const controllerCoord =
(room.controller?.pos.x ?? 0) + (room.controller?.pos.y ?? -1) * roomSize;
const controllerDistance = distanceTransform(
mask.map((t, i) =>
i === controllerCoord ? 0 : (
t === TERRAIN_MASK_WALL ? OBSTACLE : UNSET
))
);
const buildCenter = wallDistance.map(
(x, i) => [x * 2.5 - controllerDistance[i], i]).sort(
(a, b) => b[0] - a[0])[0][1];
const buildCenterX = buildCenter % roomSize;
const buildCenterY = Math.floor(buildCenter / roomSize);
const buildCenterPos = room.getPositionAt(buildCenterX, buildCenterY)!;
/* room.visual.rect(buildCenterX - 0.5, buildCenterY - 0.5, 1, 1, {
stroke: "#FF0000",
}); */
//
// Build structures
//
let structures: (BuildableStructureConstant | null)[]
= new Array(roomSize * roomSize);
// Build Roads + Containers
const roadTargets = (room.sources as (_HasRoomPosition | null)[])
.concat([room.controller, room.mineral])
.concat([room.find(FIND_EXIT_TOP)?.[0],
room.find(FIND_EXIT_LEFT)?.[0],
room.find(FIND_EXIT_RIGHT)?.[0],
room.find(FIND_EXIT_BOTTOM)?.[0]]
.map((pos) => pos == null ? null : ({ pos, highRange: true })));
for (let target of roadTargets) {
if (target == null) { continue; }
const { path } = PathFinder.search(buildCenterPos,
{ pos: target.pos, range: "highRange" in target ? 3 : 1 }, {
swampCost: 10,
plainCost: 2,
roomCallback: function(roomName) {
if (roomName != room.name) {
return false;
}
let costs = new PathFinder.CostMatrix;
for (let x = 0; x < roomSize; x++) {
for (let y = 0; y < roomSize; y++) {
if (structures[getCoord(x, y)] === STRUCTURE_ROAD) {
costs.set(x, y, 1);
}
}
}
return costs;
}
});
for (let i = 0; i < path.length; i++) {
const pos = path[i];
structures[getCoord(pos.x, pos.y)]
= (i === path.length - 1 && "energy" in target)
? STRUCTURE_CONTAINER : STRUCTURE_ROAD;
}
}
// Build Core
for (let dx = -2; dx < 3; dx++) {
for (let dy = -2; dy < 3; dy++) {
structures[getCoord(buildCenterX + dx, buildCenterY + dy)]
= STRUCTURE_ROAD;
}
}
structures[getCoord(buildCenterX, buildCenterY)] = STRUCTURE_RAMPART;
structures[getCoord(buildCenterX + 1, buildCenterY)] = STRUCTURE_STORAGE;
structures[getCoord(buildCenterX + 1, buildCenterY - 1)] = STRUCTURE_TERMINAL;
structures[getCoord(buildCenterX, buildCenterY - 1)] = STRUCTURE_LINK;
structures[getCoord(buildCenterX - 1, buildCenterY - 1)] = STRUCTURE_FACTORY;
structures[getCoord(buildCenterX - 1, buildCenterY)] = STRUCTURE_NUKER;
structures[getCoord(buildCenterX - 1, buildCenterY + 1)] = STRUCTURE_SPAWN;
structures[getCoord(buildCenterX, buildCenterY + 1)] = STRUCTURE_POWER_SPAWN;
structures[getCoord(buildCenterX + 1, buildCenterY + 1)] = STRUCTURE_ROAD;
// Labs
const coreCorners = [
[buildCenterX - 2, buildCenterY - 2],
[buildCenterX - 2, buildCenterY + 2],
[buildCenterX + 2, buildCenterY - 2],
[buildCenterX + 2, buildCenterY + 2],
].map(([x, y]) => [x, y, controllerDistance[x + y * roomSize]]
).sort(([_x, _y, i], [_x2, _y2, j]) => i - j);
for (let [x, y, i] of coreCorners) {
const directionX = (x - buildCenterX) / Math.abs(x - buildCenterX);
const directionY = (y - buildCenterY) / Math.abs(y - buildCenterY);
let ok = true;
s: for (let dx = 0; Math.abs(dx) < 4; dx += directionX) {
for (let dy = 0; Math.abs(dy) < 4; dy += directionY) {
if ((structures[getCoord(x + dx, y + dy)] == null
|| structures[getCoord(x + dx, y + dy)] === STRUCTURE_ROAD)
&& mask[getCoord(x + dx, y + dy)] === 0) {
continue;
}
ok = false;
break s;
}
}
if (ok) {
for (let dx = 0; Math.abs(dx) < 5; dx += directionX) {
for (let dy = 0; Math.abs(dy) < 5; dy += directionY) {
if (Math.abs(dx) == Math.abs(dy)) {
structures[getCoord(x + dx, y + dy)] = STRUCTURE_ROAD;
continue;
}
if (Math.abs(dx) === 4 || Math.abs(dy) === 4) {
if (mask[getCoord(x + dx, y + dy)] === 0) {
structures[getCoord(x + dx, y + dy)] = STRUCTURE_ROAD;
}
}
if (!(dx === 0 && Math.abs(dy) === 3)
&& !(dy === 0 && Math.abs(dx) === 3)) {
structures[getCoord(x + dx, y + dy)] = STRUCTURE_LAB;
}
}
}
break;
}
}
// Extensions
let extensions = 0;
a: for (let dx = 0; dx < roomSize / 2; dx++) {
for (let dy = 0; dy < wallDistance[getCoord(buildCenterX, buildCenterY)]; dy++) {
for (let cardinal = 0; cardinal < 4; cardinal++) {
const x = cardinal < 2 ? buildCenterX + dx : buildCenterX - dx;
const y = cardinal % 2 == 0 ? buildCenterY + dy : buildCenterY - dy;
if (extensions >= 60) {
break a;
}
if (structures[getCoord(x, y)] == null
&& (mask[getCoord(x, y)] === 0
|| mask[getCoord(x, y)] === TERRAIN_MASK_SWAMP)) {
let ok = false;
s: for (let ddx = -1; ddx < 2; ddx++) {
for (let ddy = -1; ddy < 2; ddy++) {
if (structures[getCoord(x + ddx, y + ddy)] === STRUCTURE_ROAD) {
ok = true;
break s;
}
}
}
if (ok) {
if (x % 2 == y % 3 || x % 3 == y % 2) {
structures[getCoord(x, y)] = STRUCTURE_ROAD;
} else {
structures[getCoord(x, y)] = STRUCTURE_EXTENSION;
extensions++;
}
}
}
}
}
}
if (room.mineral != null) {
structures[getCoord(room.mineral.pos.x, room.mineral.pos.y)]
= STRUCTURE_EXTRACTOR;
}
// Walls
let smallestX = roomSize;
let largestX = 0;
let smallestY = roomSize;
let largestY = 0;
for (let x = 0; x < roomSize; x++) {
for (let y = 0; y < roomSize; y++) {
if (structures[getCoord(x, y)] != null
&& structures[getCoord(x, y)] != STRUCTURE_ROAD
&& structures[getCoord(x, y)] != STRUCTURE_CONTAINER) {
if (x < smallestX) {
smallestX = x;
}
if (x > largestX) {
largestX = x;
}
if (y < smallestY) {
smallestY = y;
}
if (y > largestY) {
largestY = y;
}
}
}
}
smallestX = Math.max(2, smallestX - 3);
smallestY = Math.max(2, smallestY - 3);
largestX = Math.min(48, largestX + 3);
largestY = Math.min(48, largestY + 3);
const centerLine = Math.floor(largestY / 2 + smallestY / 2);
const { path } = PathFinder.search(
room.getPositionAt(smallestX - 1, centerLine + 1)!,
room.getPositionAt(smallestX - 1, centerLine - 1)!,
{
plainCost: 100,
swampCost: 100,
roomCallback: function(roomName) {
if (roomName != room.name) {
return false;
}
let costs = new PathFinder.CostMatrix;
for (let x = 0; x < roomSize; x++) {
for (let y = 0; y < roomSize; y++) {
if (mask[getCoord(x, y)] === TERRAIN_MASK_WALL) {
costs.set(x, y, 1);
}
if (structures[getCoord(x, y)] != null) {
if (structures[getCoord(x, y)] == STRUCTURE_ROAD) {
costs.set(x, y, 254);
} else {
costs.set(x, y, 255);
}
}
}
}
for (let x = smallestX; x <= largestX; x++) {
for (let y = smallestY; y <= largestY; y++) {
costs.set(x, y, 255);
}
}
for (let x = 0; x < smallestX; x++) {
costs.set(x, centerLine, 255);
}
return costs;
},
});
path.push(room.getPositionAt(smallestX - 1, centerLine)!);
for (let i = 0; i < path.length; i++) {
let s = structures[getCoord(path[i].x, path[i].y)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x + 1, path[i].y)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x - 1, path[i].y)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x + 1, path[i].y + 1)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x + 1, path[i].y - 1)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x - 1, path[i].y + 1)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x - 1, path[i].y - 1)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x, path[i].y + 1)] === STRUCTURE_ROAD
|| structures[getCoord(path[i].x, path[i].y - 1)] === STRUCTURE_ROAD
? STRUCTURE_RAMPART
: STRUCTURE_WALL;
if (mask[getCoord(path[i].x, path[i].y)] != TERRAIN_MASK_WALL) {
structures[getCoord(path[i].x, path[i].y)] = s;
} else {
continue;
}
if (mask[getCoord(path[i].x+1, path[i].y)] != TERRAIN_MASK_WALL
&& structures[getCoord(path[i].x+1, path[i].y)] != STRUCTURE_RAMPART) {
structures[getCoord(path[i].x+1, path[i].y)] = s;
}if (mask[getCoord(path[i].x-1, path[i].y)] != TERRAIN_MASK_WALL
&& structures[getCoord(path[i].x-1, path[i].y)] != STRUCTURE_RAMPART) {
structures[getCoord(path[i].x-1, path[i].y)] = s;
}if (mask[getCoord(path[i].x, path[i].y+1)] != TERRAIN_MASK_WALL
&& structures[getCoord(path[i].x, path[i].y+1)] != STRUCTURE_RAMPART) {
structures[getCoord(path[i].x, path[i].y+1)] = s;
}if (mask[getCoord(path[i].x, path[i].y-1)] != TERRAIN_MASK_WALL
&& structures[getCoord(path[i].x, path[i].y-1)] != STRUCTURE_RAMPART) {
structures[getCoord(path[i].x, path[i].y-1)] = s;
}
}
// Render
// renderHeatmap(controllerDistance, room.visual);
/* for (let x = 0; x < roomSize; x++) {
for (let y = 0; y < roomSize; y++) {
if (structures[getCoord(x, y)] != null) {
room.visual.structure(x, y, structures[getCoord(x, y)]);
}
}
}
room.visual.connectRoads(); */
let str = "";
for (let i = 0; i < structures.length; i += 2) {
str += String.fromCharCode(32 +
(structureCoding.findIndex((s) => s == structures[i]))
+ (structureCoding.findIndex((s) => s == structures[i + 1]) << 5)
);
}
room.memory._planner = str;
});

View file

@ -16,7 +16,7 @@ export const runBuild = profiler.registerFN((creep: Creep): TaskStatus => {
return TaskStatus.DONE;
}
if (creep.store.getUsedCapacity(RESOURCE_ENERGY) === 0) {
if (creep.store.energy === 0) {
return TaskStatus.DONE;
}
@ -28,7 +28,6 @@ export const runBuild = profiler.registerFN((creep: Creep): TaskStatus => {
if (target == null
|| creep.build(target) === ERR_NOT_IN_RANGE) {
creep.travelTo(task.targetPos);
return TaskStatus.IN_PROGRESS;
}
return TaskStatus.DONE;
return TaskStatus.IN_PROGRESS;
}, "runBuild");

35
src/Tasks/Pickup.ts Normal file
View file

@ -0,0 +1,35 @@
import { TaskData, TaskStatus, TaskType } from "./Task";
export const Pickup
= (target: Resource): TaskData => ({
target,
targetPos: target.pos,
type: TaskType.Pickup,
options: {},
data: { resource: target.resourceType },
});
export const runPickup = (creep: Creep): TaskStatus => {
const task = creep.task;
if (task == null) {
return TaskStatus.DONE;
}
if (task.target == null && task.targetPos.roomName == creep.room.name) {
return TaskStatus.DONE;
}
const target = task.target as Resource;
const resource: ResourceConstant
= (task.data as { resource: ResourceConstant }).resource;
if (creep.store.getFreeCapacity(resource) == 0) {
return TaskStatus.DONE;
}
if (target == null
|| creep.pickup(target) === ERR_NOT_IN_RANGE) {
creep.travelTo(task.targetPos);
return TaskStatus.IN_PROGRESS;
}
return TaskStatus.DONE;
};

View file

@ -1,5 +1,5 @@
import {
packId, packPos, unpackId, unpackPos,
packPos, unpackPos,
} from "../../deps/screeps-packrat/src/packrat";
export enum TaskType {
@ -9,6 +9,7 @@ export enum TaskType {
Transfer,
Build,
Repair,
Pickup
}
export enum TaskStatus {

View file

@ -30,6 +30,9 @@ export const runWithdraw = (creep: Creep): TaskStatus => {
if (task == null) {
return TaskStatus.DONE;
}
if (task.target == null && task.targetPos.roomName == creep.room.name) {
return TaskStatus.DONE;
}
const target = task.target as Structure | Tombstone | Ruin;
const opts = task.options as WithdrawOptions;

View file

@ -7,6 +7,7 @@ import { runTransfer, Transfer } from "./Transfer";
import { Build, runBuild } from "./Build";
import { Repair, runRepair } from "./Repair";
import profiler from "screeps-profiler";
import { Pickup, runPickup } from "./Pickup";
export { TaskType, TaskStatus } from "./Task";
declare global {
@ -29,6 +30,8 @@ const runTask = profiler.registerFN((creep: Creep): TaskStatus => {
return runBuild(creep);
case TaskType.Repair:
return runRepair(creep);
case TaskType.Pickup:
return runPickup(creep);
default:
return TaskStatus.DONE;
}
@ -45,12 +48,16 @@ Creep.prototype.run = function (generator?: (creep: Creep) => TaskData | null) {
}
};
export default profiler.registerObject({
const Tasks = {
Harvest,
Upgrade,
Withdraw,
Transfer,
Build,
Repair,
Pickup,
...TaskStatus,
}, "Tasks");
};
profiler.registerObject(Tasks, "Tasks");
export default Tasks;

View file

@ -1,13 +1,3 @@
import { Action, Fail } from "../Actions/Action";
export const notNull
= <T>(x: T | null | undefined, f: (y: T) => Action): Action => {
if (x == null) {
return Fail;
}
return f(x);
};
export const closestTowerToFill = (pos: RoomPosition): StructureTower | null =>
pos.findClosestByRange(FIND_MY_STRUCTURES, {
filter: structure => structure.structureType === STRUCTURE_TOWER

View file

@ -1,7 +0,0 @@
declare module "screeps-profiler" {
export const registerFN: <T extends Function>(f: T, n?: string) => T;
export const registerObject: <T extends Object>(f: T, n?: string) => T;
export const registerClass: <T extends Object>(f: T, n?: string) => T;
export const enable: () => void;
export const wrap: (f: () => void) => () => void;
};