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";
|
import SpawnHandler from "spawnHandler";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -11,8 +13,8 @@ declare global {
|
|||||||
*/
|
*/
|
||||||
interface SourceState {
|
interface SourceState {
|
||||||
id: string;
|
id: string;
|
||||||
|
spots: SourcePositionMatrix;
|
||||||
pos: RoomPosition;
|
pos: RoomPosition;
|
||||||
maxHarvesters: number|null;
|
|
||||||
currentHarvesters: number;
|
currentHarvesters: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +32,9 @@ declare global {
|
|||||||
interface CreepMemory {
|
interface CreepMemory {
|
||||||
role: string;
|
role: string;
|
||||||
room: string;
|
room: string;
|
||||||
|
spawnId: string;
|
||||||
working: boolean;
|
working: boolean;
|
||||||
|
destination?: CreepDestination;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Syntax for adding proprties to `global` (ex "global.log")
|
// 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 { RoleHandler } from "./roleHandler.interface";
|
||||||
|
import { getNextEmptySpot } from "types/source";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HarvesterHandler extends RoleHandler {
|
class HarvesterHandler extends RoleHandler {
|
||||||
public static run(creep: Creep, state: GameState): GameState {
|
public static run(creep: Creep, state: GameState): GameState {
|
||||||
const source = this.findClosestSource(creep, state);
|
switch (creep.memory.destination?.type) {
|
||||||
|
case "spawn":
|
||||||
console.log(`Running HarvesterHandler for creep: ${creep.name}`);
|
this.onSpawnDestination(creep, state);
|
||||||
if (creep.store.getFreeCapacity() > 0) {
|
break;
|
||||||
if (creep.harvest(source) === ERR_NOT_IN_RANGE) {
|
default:
|
||||||
creep.moveTo(source, { reusePath: true });
|
this.onSourceDestination(creep, state);
|
||||||
}
|
break;
|
||||||
} else {
|
|
||||||
const spawn = Game.spawns['Spawn1'];
|
|
||||||
if (creep.transfer(spawn, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) {
|
|
||||||
creep.moveTo(spawn, { reusePath: true });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static findClosestSource(creep: Creep, state: GameState): Source|null {
|
private static onSourceDestination(creep: Creep, state: GameState) {
|
||||||
for (const source of creep.room.find(FIND_SOURCES)) {
|
if (!creep.memory.destination) {
|
||||||
|
const sources = this.findClosestSource(creep, state);
|
||||||
}
|
if (sources.length > 0) {
|
||||||
|
creep.memory.destination = {
|
||||||
let closestSourceId = null;
|
id: sources[0].id,
|
||||||
for (const sourceId in state.sourcesStates) {
|
type: "source",
|
||||||
if (!closestSourceId) {
|
sourceSpot: getNextEmptySpot(state.sourcesStates[sources[0].id].spots) || { x: 0, y: 0 }
|
||||||
closestSourceId = sourceId;
|
};
|
||||||
}
|
} else {
|
||||||
const sourceInfo = state.sourcesStates[sourceId];
|
console.warn(`No sources found for creep: ${creep.name}`);
|
||||||
const creepPos = creep.pos;
|
return;
|
||||||
if (creepPos.getRangeTo(sourceInfo["pos"]) < creepPos.getRangeTo(state.sourcesStates[closestSourceId]["pos"])) {
|
|
||||||
closestSourceId = sourceId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!closestSourceId) {
|
const source = getSourceById(creep.memory.destination.id);
|
||||||
console.warn(`No sources found for creep: ${creep.name}`);
|
if (!source) {
|
||||||
return null;
|
console.warn(`Source not found for creep: ${creep.name}`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSourceById(closestSourceId);
|
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 { CreepRequisition, CreepRole, CreepRoles, RoleDefinition } from "types/creeps";
|
||||||
import { DEFAULT_GAME_CONFIG } from "types/gameConfig";
|
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";
|
import { get_role_const as get_role_cost } from "utils/funcs/get_role_const";
|
||||||
|
|
||||||
class SpawnHandler {
|
class SpawnHandler {
|
||||||
@@ -12,17 +14,17 @@ class SpawnHandler {
|
|||||||
public run(state: GameState): GameState {
|
public run(state: GameState): GameState {
|
||||||
this.updateSpawnState(state);
|
this.updateSpawnState(state);
|
||||||
|
|
||||||
for(const name in Game.creeps) {
|
// for(const name in Game.creeps) {
|
||||||
const creep = Game.creeps[name];
|
// const creep = Game.creeps[name];
|
||||||
|
|
||||||
const roleDefinition = CreepRoles[creep.memory.role as CreepRole];
|
// const roleDefinition = CreepRoles[creep.memory.role as CreepRole];
|
||||||
if (!roleDefinition) {
|
// if (!roleDefinition) {
|
||||||
console.warn(`Creep ${creep.name} has an unknown role: ${creep.memory.role}`);
|
// console.warn(`Creep ${creep.name} has an unknown role: ${creep.memory.role}`);
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
|
||||||
state = roleDefinition.handler.run(creep, state);
|
// state = roleDefinition.handler.run(creep, state);
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.validateSpawnState();
|
this.validateSpawnState();
|
||||||
|
|
||||||
@@ -40,9 +42,21 @@ class SpawnHandler {
|
|||||||
state.sourcesStates[sourceId] = {
|
state.sourcesStates[sourceId] = {
|
||||||
"id": sourceId,
|
"id": sourceId,
|
||||||
"pos": source.pos,
|
"pos": source.pos,
|
||||||
"maxHarvesters": null,
|
"spots": createSourcePositionMatrix(),
|
||||||
"currentHarvesters": 0
|
"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: {
|
memory: {
|
||||||
role: role.name,
|
role: role.name,
|
||||||
room: this.spawn.room.name,
|
room: this.spawn.room.name,
|
||||||
|
spawnId: this.spawn.id,
|
||||||
working: false
|
working: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { HarvesterHandler, RoleHandler } from "roleHandlers";
|
import { HarvesterHandler, RoleHandler } from "roleHandlers";
|
||||||
|
import { PositionDelta } from "./source";
|
||||||
|
|
||||||
export type RoleDefinition = {
|
export type RoleDefinition = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -31,3 +32,21 @@ export const CreepRoles = {
|
|||||||
export type CreepRole = keyof typeof CreepRoles;
|
export type CreepRole = keyof typeof CreepRoles;
|
||||||
|
|
||||||
export type CreepRequisition = Record<CreepRole, number>;
|
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}
|
* @type {GameConfig}
|
||||||
*/
|
*/
|
||||||
export const DEFAULT_GAME_CONFIG: GameConfig = {
|
export const DEFAULT_GAME_CONFIG: GameConfig = {
|
||||||
maxCreeps: 15,
|
maxCreeps: 1,
|
||||||
minCreepsPerRole: {
|
minCreepsPerRole: {
|
||||||
harvester: 15,
|
harvester: 1,
|
||||||
upgrader: 0,
|
upgrader: 0,
|
||||||
builder: 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