-
Notifications
You must be signed in to change notification settings - Fork 0
Strategy Extensions
Actions represent discrete goals, but the possible targets and variations within an action might have different priorities based on the selected Task or Behaviour. For example haulers will prefer full containers being filled by miners or other rooms, and workers will prefer the closest energy source.
TL;DR getStrategyHandler will look for a named function in the hierarchy of a Creep's logic. First Task then Behaviour then Action can define standard strategies.
The mechanisms of this parameterization is fulfilled by two standard patterns:
- scoring callbacks:
*Score
- represents a range of value within the context of an action
- intended to represent a continuous list of options across multiple prioritizable actions
- logical decision callbacks
is*
orcan*
- represents the logical branching of possible actions
Strategies may be added to several different game entities or logical components. Their callbacks can be described differently per implementation as is needed.
Standard strategies will be provided for Creeps.
- agent - the component whose operations are parameterized with strategies, e.g. creep
- client - the components who provide parameterizations, e.g. action, behaviour, task
-
getStrategyHandler(key, method, ...args)
- retrieve a strategy function from the pinned or generated strategy -
currentStrategy
- the current pinned strategy -
strategy(...key)
- generate and retrieve a strategy object (ignoring the pinned strategy)-
strategy().name
- array containing the client strategies in priority order -
strategy().key
- array containing the client id's in priority order
-
-
staticCustomStrategy(...key)
- a custom strategy which may not select functions based on the runtime state -
customStrategy(...key)
- a custom strategy which will not be cached and may refer to runtime state
-
selectStrategies(...key)
- retrieve the client's provided strategies for the key
- decorateAgent - installation endpoint
- allocateStrategy - pin a strategy to the agent
- freeStrategy - remove a pinned strategy
strategy.decorateAgent([agent|prototype], [...client definitions])
This method injects methods onto the agent (or agent prototype). Each client definition describes how clients can be identified and retrieved:
- definition.default - retrieves the client id for the agent in its current state
- definition.selector - retrieves a client for the given id
The clients form a chain of scopes and the ids of the clients selected in each scope are combined to define the strategy key
for strategy lookup. For example: [uncharging, remoteHauler, remoteMining]
defines the key for the strategy with that given action, behaviour, and task.
Scopes in the client definitions and API accessor methods are in order of priority (low to high). The high priority client scopes take precedence over lower priority client scopes. Low priority function references are literally overwritten as the strategy is constructed.
The client ids may be explicitly specified on API access, but in the current use-case only the lowest priority scope is specified.
let handlerFunction = agent.getStrategyHandler([...scope ids],handlerName, ...handler arguments);
This retrieves a strategy-specific function. The context for the comparison or decision being carried out by this strategy is specified by handler arguments.
The returnVal of this method will be a function to evaluate one or more arguments provided. For example the following is the standard target scoring client pattern:
const targetPool = // ...
const targetScore = agent.getStrategyHandler([lowScope.name], 'targetScore', agent, context);
if (!targetScore) return;
const targets = _.chain(targetPool).map(targetScore).filter('score').sortBy('score').reverse().value();
return targets[0] && targets[0].target || null;
Each client must provide a strategy selector, which will be used to separate interface and implementation of this API. Additionally this could lead to further strategy refinements in the future. The selectStrategies
method returns an array of strategies which will be combined (low priority first) and cached.
client.selectStrategies = function(lowScopeId) {
return [mod.strategies.defaultStrategy, mod.strategies[lowScopeId]];
};
It is recommended that clients provide higher-order functions in order to build scoring and logical functions in the context of the game state. For example if the state important to a score function is a pair of underlying rooms, but the entities being scored are structures inside the second room the client code for this strategy handler would look like:
client.defaultStrategy.targetScore = function (room1, room2) {
// setup calculations comparing 2 rooms
return function(targetStructure) {
// perform calculations evaluating the target structure, return that structure's score
}
}
Each Strategy Agent can be asked to explain who they are and what they are doing:
> Game.getObjectById('58d2eb8ca45ad69f4dbdf5f2').explain()
< [creep hauler-1250-2]: ttl:152 pos:[room W24N19 pos 8,26] assigned:[storing,hauler,]
The creep's responsibility to explain its current critical state (e.g. ttl and position) is specified using the explainAgent(agent)
function.
Each client (e.g. action, behaviour, task, squad) can explain their specific agenda using the explain(agent)
function.
- Strategy functions may never return undefined.
- If the argument list is empty the strategy function will be returned without invoking.
- For example
getStrategyHandler([], 'method')
will return the value ofcreep.action.defaultStrategy.method
- Similarly calling
getStrategyHandler([], 'method', 5)
will invoke and return the result ofcreep.action.defaultStrategy.method(5)
- For example
Moving withdrawing.isValidAction to a strategy lets the direct check for privateer behaviour to be eliminated. Recycler mixins produce a massive simplification to the nextAction function.
https://github.com/ScreepsOCS/screeps.behaviour-action-pattern/pull/693/files