Spawn Room Detection
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { CreepDestination } from "types/creeps";
|
||||
import { SourcePositionMatrix, SourceSpotStatus } from "./types/source";
|
||||
import SpawnHandler from "spawnHandler";
|
||||
|
||||
declare global {
|
||||
@@ -11,8 +13,8 @@ declare global {
|
||||
*/
|
||||
interface SourceState {
|
||||
id: string;
|
||||
spots: SourcePositionMatrix;
|
||||
pos: RoomPosition;
|
||||
maxHarvesters: number|null;
|
||||
currentHarvesters: number;
|
||||
}
|
||||
|
||||
@@ -30,7 +32,9 @@ declare global {
|
||||
interface CreepMemory {
|
||||
role: string;
|
||||
room: string;
|
||||
spawnId: string;
|
||||
working: boolean;
|
||||
destination?: CreepDestination;
|
||||
}
|
||||
|
||||
// Syntax for adding proprties to `global` (ex "global.log")
|
||||
|
||||
@@ -1,50 +1,79 @@
|
||||
import getSourceById from "utils/funcs/get_source_by_id";
|
||||
import { getSourceById, getSpawnById } from "utils/funcs/get_by_id";
|
||||
import { RoleHandler } from "./roleHandler.interface";
|
||||
import { getNextEmptySpot } from "types/source";
|
||||
|
||||
|
||||
|
||||
class HarvesterHandler extends RoleHandler {
|
||||
public static run(creep: Creep, state: GameState): GameState {
|
||||
const source = this.findClosestSource(creep, state);
|
||||
|
||||
console.log(`Running HarvesterHandler for creep: ${creep.name}`);
|
||||
if (creep.store.getFreeCapacity() > 0) {
|
||||
if (creep.harvest(source) === ERR_NOT_IN_RANGE) {
|
||||
creep.moveTo(source, { reusePath: true });
|
||||
}
|
||||
} else {
|
||||
const spawn = Game.spawns['Spawn1'];
|
||||
if (creep.transfer(spawn, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) {
|
||||
creep.moveTo(spawn, { reusePath: true });
|
||||
}
|
||||
switch (creep.memory.destination?.type) {
|
||||
case "spawn":
|
||||
this.onSpawnDestination(creep, state);
|
||||
break;
|
||||
default:
|
||||
this.onSourceDestination(creep, state);
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private static findClosestSource(creep: Creep, state: GameState): Source|null {
|
||||
for (const source of creep.room.find(FIND_SOURCES)) {
|
||||
|
||||
}
|
||||
|
||||
let closestSourceId = null;
|
||||
for (const sourceId in state.sourcesStates) {
|
||||
if (!closestSourceId) {
|
||||
closestSourceId = sourceId;
|
||||
}
|
||||
const sourceInfo = state.sourcesStates[sourceId];
|
||||
const creepPos = creep.pos;
|
||||
if (creepPos.getRangeTo(sourceInfo["pos"]) < creepPos.getRangeTo(state.sourcesStates[closestSourceId]["pos"])) {
|
||||
closestSourceId = sourceId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!closestSourceId) {
|
||||
private static onSourceDestination(creep: Creep, state: GameState) {
|
||||
if (!creep.memory.destination) {
|
||||
const sources = this.findClosestSource(creep, state);
|
||||
if (sources.length > 0) {
|
||||
creep.memory.destination = {
|
||||
id: sources[0].id,
|
||||
type: "source",
|
||||
sourceSpot: getNextEmptySpot(state.sourcesStates[sources[0].id].spots) || { x: 0, y: 0 }
|
||||
};
|
||||
} else {
|
||||
console.warn(`No sources found for creep: ${creep.name}`);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return getSourceById(closestSourceId);
|
||||
const source = getSourceById(creep.memory.destination.id);
|
||||
if (!source) {
|
||||
console.warn(`Source not found for creep: ${creep.name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (creep.harvest(source) === ERR_NOT_IN_RANGE) {
|
||||
creep.moveTo(source, { reusePath: true });
|
||||
}
|
||||
}
|
||||
|
||||
private static onSpawnDestination(creep: Creep, state: GameState) {
|
||||
if (!creep.memory.destination) {
|
||||
creep.memory.destination = {
|
||||
id: creep.memory.spawnId,
|
||||
type: "spawn"
|
||||
}
|
||||
}
|
||||
if (creep.store.getFreeCapacity() <= 0) {
|
||||
delete creep.memory.destination;
|
||||
return;
|
||||
}
|
||||
|
||||
const spawn = getSpawnById(creep.memory.destination.id);
|
||||
if (!spawn) {
|
||||
console.warn(`Spawn not found for creep: ${creep.name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (creep.transfer(spawn, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) {
|
||||
creep.moveTo(spawn, { reusePath: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static findClosestSource(creep: Creep, state: GameState): Source[] {
|
||||
return Object.keys(state.sourcesStates)
|
||||
.map(sourceId => getSourceById(sourceId))
|
||||
.filter(source => source !== null)
|
||||
.sort((a, b) => creep.pos.getRangeTo(a) - creep.pos.getRangeTo(b));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { CreepRequisition, CreepRole, CreepRoles, RoleDefinition } from "types/creeps";
|
||||
import { DEFAULT_GAME_CONFIG } from "types/gameConfig";
|
||||
import { createSourcePositionMatrix, forEachMatrixSpot, getPositionWithDelta, setSpotStatus, SourceSpotStatus } from "types/source";
|
||||
import { checkPositionWalkable } from "utils/funcs/check_position";
|
||||
import { get_role_const as get_role_cost } from "utils/funcs/get_role_const";
|
||||
|
||||
class SpawnHandler {
|
||||
@@ -12,17 +14,17 @@ class SpawnHandler {
|
||||
public run(state: GameState): GameState {
|
||||
this.updateSpawnState(state);
|
||||
|
||||
for(const name in Game.creeps) {
|
||||
const creep = Game.creeps[name];
|
||||
// for(const name in Game.creeps) {
|
||||
// const creep = Game.creeps[name];
|
||||
|
||||
const roleDefinition = CreepRoles[creep.memory.role as CreepRole];
|
||||
if (!roleDefinition) {
|
||||
console.warn(`Creep ${creep.name} has an unknown role: ${creep.memory.role}`);
|
||||
continue;
|
||||
}
|
||||
// const roleDefinition = CreepRoles[creep.memory.role as CreepRole];
|
||||
// if (!roleDefinition) {
|
||||
// console.warn(`Creep ${creep.name} has an unknown role: ${creep.memory.role}`);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
state = roleDefinition.handler.run(creep, state);
|
||||
}
|
||||
// state = roleDefinition.handler.run(creep, state);
|
||||
// }
|
||||
|
||||
this.validateSpawnState();
|
||||
|
||||
@@ -40,9 +42,21 @@ class SpawnHandler {
|
||||
state.sourcesStates[sourceId] = {
|
||||
"id": sourceId,
|
||||
"pos": source.pos,
|
||||
"maxHarvesters": null,
|
||||
"spots": createSourcePositionMatrix(),
|
||||
"currentHarvesters": 0
|
||||
};
|
||||
forEachMatrixSpot(state.sourcesStates[sourceId].spots, (delta, status) => {
|
||||
if (status !== SourceSpotStatus.UNKNOWN) {
|
||||
return; // Skip known spots
|
||||
}
|
||||
const pos = getPositionWithDelta(source.pos, delta);
|
||||
if (checkPositionWalkable(pos)) {
|
||||
setSpotStatus(state.sourcesStates[sourceId].spots, delta, SourceSpotStatus.EMPTY);
|
||||
} else {
|
||||
setSpotStatus(state.sourcesStates[sourceId].spots, delta, SourceSpotStatus.INVALID);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,6 +92,7 @@ class SpawnHandler {
|
||||
memory: {
|
||||
role: role.name,
|
||||
room: this.spawn.room.name,
|
||||
spawnId: this.spawn.id,
|
||||
working: false
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { HarvesterHandler, RoleHandler } from "roleHandlers";
|
||||
import { PositionDelta } from "./source";
|
||||
|
||||
export type RoleDefinition = {
|
||||
name: string;
|
||||
@@ -31,3 +32,21 @@ export const CreepRoles = {
|
||||
export type CreepRole = keyof typeof CreepRoles;
|
||||
|
||||
export type CreepRequisition = Record<CreepRole, number>;
|
||||
|
||||
|
||||
|
||||
|
||||
export type SpawnDestination = {
|
||||
id: string; // ID of the spawn
|
||||
type: "spawn";
|
||||
}
|
||||
|
||||
export type SourceDestination = {
|
||||
id: string; // ID of the source
|
||||
type: "source";
|
||||
sourceSpot: PositionDelta; // Position delta for the source spot
|
||||
}
|
||||
|
||||
|
||||
export type CreepDestination = SpawnDestination | SourceDestination;
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ export type GameConfig = {
|
||||
* @type {GameConfig}
|
||||
*/
|
||||
export const DEFAULT_GAME_CONFIG: GameConfig = {
|
||||
maxCreeps: 15,
|
||||
maxCreeps: 1,
|
||||
minCreepsPerRole: {
|
||||
harvester: 15,
|
||||
harvester: 1,
|
||||
upgrader: 0,
|
||||
builder: 0
|
||||
}
|
||||
|
||||
79
src/types/source.ts
Normal file
79
src/types/source.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
export const SourceSpotStatus = {
|
||||
INVALID: "-2",
|
||||
CENTER: "-1",
|
||||
UNKNOWN: "0",
|
||||
EMPTY: "1",
|
||||
OCCUPIED: "2",
|
||||
} as const;
|
||||
|
||||
|
||||
export type SourceSpotStatus = (typeof SourceSpotStatus)[keyof typeof SourceSpotStatus];
|
||||
|
||||
|
||||
export type PositionDeltaValue = -1 | 0 | 1;
|
||||
|
||||
export type PositionDelta = {
|
||||
x: PositionDeltaValue;
|
||||
y: PositionDeltaValue;
|
||||
}
|
||||
|
||||
export type SourcePositionMatrix = [
|
||||
SourceSpotStatus, SourceSpotStatus, SourceSpotStatus,
|
||||
SourceSpotStatus, SourceSpotStatus, SourceSpotStatus,
|
||||
SourceSpotStatus, SourceSpotStatus, SourceSpotStatus
|
||||
]
|
||||
|
||||
|
||||
export const createSourcePositionMatrix = () : SourcePositionMatrix => {
|
||||
return [
|
||||
SourceSpotStatus.UNKNOWN, SourceSpotStatus.UNKNOWN, SourceSpotStatus.UNKNOWN,
|
||||
SourceSpotStatus.UNKNOWN, SourceSpotStatus.CENTER, SourceSpotStatus.UNKNOWN,
|
||||
SourceSpotStatus.UNKNOWN, SourceSpotStatus.UNKNOWN, SourceSpotStatus.UNKNOWN
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
export const getNextEmptySpot = (matrix: SourcePositionMatrix): PositionDelta | null => {
|
||||
const index = matrix.findIndex( status => status === SourceSpotStatus.EMPTY || status === SourceSpotStatus.UNKNOWN);
|
||||
|
||||
if (index === -1) {
|
||||
return null; // No empty spot found
|
||||
}
|
||||
|
||||
return {
|
||||
x: (index % 3 - 1) as PositionDeltaValue, // Convert index to x delta (-1, 0, 1)
|
||||
y: (Math.floor(index / 3) - 1) as PositionDeltaValue // Convert index to y delta (-1, 0, 1)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export const setSpotStatus = (matrix: SourcePositionMatrix, delta: PositionDelta, status: SourceSpotStatus): void => {
|
||||
const x = delta.x + 1; // Convert to index (0, 1, 2)
|
||||
const y = delta.y + 1; // Convert to index (0, 1, 2)
|
||||
|
||||
if (x < 0 || x > 2 || y < 0 || y > 2) {
|
||||
throw new Error("Invalid position delta for source position matrix.");
|
||||
}
|
||||
|
||||
const index = y * 3 + x; // Calculate the index in the flat array
|
||||
matrix[index] = status;
|
||||
}
|
||||
|
||||
|
||||
export const getPositionWithDelta = (pos: RoomPosition, delta: PositionDelta): RoomPosition => {
|
||||
return new RoomPosition(
|
||||
pos.x + delta.x,
|
||||
pos.y + delta.y,
|
||||
pos.roomName
|
||||
);
|
||||
}
|
||||
|
||||
export const forEachMatrixSpot = (matrix: SourcePositionMatrix, callback: (delta: PositionDelta, status: SourceSpotStatus) => void): void => {
|
||||
for (let y = -1; y <= 1; y++) {
|
||||
for (let x = -1; x <= 1; x++) {
|
||||
const delta: PositionDelta = { x: x as PositionDeltaValue, y: y as PositionDeltaValue };
|
||||
const index = (y + 1) * 3 + (x + 1); // Convert delta to index
|
||||
callback(delta, matrix[index]);
|
||||
}
|
||||
}
|
||||
};
|
||||
5
src/utils/funcs/check_position.ts
Normal file
5
src/utils/funcs/check_position.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const checkPositionWalkable = (pos: RoomPosition) => {
|
||||
// Check if the position is not obstructed by a wall
|
||||
const terrain = pos.lookFor(LOOK_TERRAIN);
|
||||
return terrain.length === 0 || terrain[0] !== 'wall'
|
||||
}
|
||||
30
src/utils/funcs/get_by_id.ts
Normal file
30
src/utils/funcs/get_by_id.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export const getSourceById = (sourceId: string): Source | null => {
|
||||
if (!sourceId) {
|
||||
console.warn("getSourceById called with an empty or undefined sourceId.");
|
||||
return null;
|
||||
}
|
||||
|
||||
const source = Game.getObjectById<Source>(sourceId);
|
||||
if (!source) {
|
||||
console.warn(`No source found with ID: ${sourceId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
export const getSpawnById = (spawnId: string): StructureSpawn | null => {
|
||||
if (!spawnId) {
|
||||
console.warn("getSpawnById called with an empty or undefined spawnId.");
|
||||
return null;
|
||||
}
|
||||
|
||||
const spawn = Game.getObjectById<StructureSpawn>(spawnId);
|
||||
if (!spawn) {
|
||||
console.warn(`No spawn found with ID: ${spawnId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return spawn;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
const getSourceById = (sourceId: string): Source | null => {
|
||||
if (!sourceId) {
|
||||
console.warn("getSourceById called with an empty or undefined sourceId.");
|
||||
return null;
|
||||
}
|
||||
|
||||
const source = Game.getObjectById<Source>(sourceId);
|
||||
if (!source) {
|
||||
console.warn(`No source found with ID: ${sourceId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
export default getSourceById;
|
||||
Reference in New Issue
Block a user