Implementing Agents

The Agent can follow roughly the same structure proposed in the notes on the Thing. You might want to use a state machine to define allowable state transitions, and to keep track of them.

// script.js
import * as Agent from './agent.js';

let state = {
  agent: { speed: 1 }
}

const use = () => {
  const { agent } = state;
  // Use properties of agent...
};

const update = () => {
  // Calculate new agent & save
  saveState({
    agent: Agent.update(state.agent)
  });

  // Do other state updates?
}
// agent.js
/**
 * Updates an agent, returning the new version
 */
const update = (agent) => {
  let { currentState, speed } = agent;

  // Incrementally change speed more or less,
  // depending on current state
  let speedModifier = 0;
  switch (currentState.state) {
    case `waking`:
      speedModifier = 0.002;
      break;
    case `awake`:
      speedModifier = 0;
      break;
    case `dozing`:
      speedModifier = -0.001;
      break;
  }

  speed += (speedModifier * speed);

  // Return changed Agent
  return {
    ...agent,
    speed
  };
};

Pitfalls

Be careful whether discrete states are the best for the thing you are trying to express. Only use them when a sharp transition matches what you are trying to model.

For example, animals don’t go from ‘hungry’ to ‘satisfied’ states. Rather we might model it as a dimension: hunger being a value from 0 to 1, with 1 being ‘100%’ hungry.

Some states might make more sense as bipolar values (-1..1), where 0 represents neutral. For example, a bipolar ‘growth’ might mean -1 is decaying at full pace, 0 is holding-steady while 1 means growing at full pace (and all values in between, naturally).

As with the Thing, fold-in variables as a modulation. Avoid if-then or switch statements that trigger behaviour based on values. In the code example earlier on this page, we use a switch to change the rate speed’s modulation rather than setting speed itself. If we did that, there would be abrupt changes.