Skip to content

Strategy Extensions

Karl the Pagan edited this page Mar 22, 2017 · 7 revisions

Behaviours and Actions: Parameterized using Strategies

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.

Scoring Functions

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* or can*
    • represents the logical branching of possible actions

Strategy API

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.

Fundamental Strategy participants

  • agent - the component whose operations are parameterized with strategies, e.g. creep
  • client - the components who provide parameterizations, e.g. action, behaviour, task

Agent API

  • 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

Agent Callbacks

  • 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

Client Callbacks

  • selectStrategies(...key) - retrieve the client's provided strategies for the key

Library API

  • decorateAgent - installation endpoint
  • allocateStrategy - pin a strategy to the agent
  • freeStrategy - remove a pinned strategy

Instrumenting an Agent

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.

Client Scopes

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.

Retrieving Strategies

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;

Implementing Strategies

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
  }
}

Explain API

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 Function Special Requirements

  • 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 of creep.action.defaultStrategy.method
    • Similarly calling getStrategyHandler([], 'method', 5) will invoke and return the result of creep.action.defaultStrategy.method(5)

Example Strategy Migrations

recycler and privateer mixin isValidAction for uncharging, withdrawing, and travelling

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