Action generators #
In addition to exporting properties in a specification, you
export action generators. A generator is an object of the
ActionGenerator class. An action generator
generates values of type
Tree<ActionTemplate>.
Like with default properties, there are default action generators provided by Bombadil. These will get you a long way, but there are times where you’ll need to define your own action generators.
For every state that Bombadil captures, all action generators are run, contributing to a tree structure of possible action templates. These are called action templates and are parameterized by ranges of values. Bombadil then randomly picks one in that tree, and picks random values within the ranges for the parameters. Why a tree, though? It’s because the branches are weighted — equally, by default. But you can override this to control the probability of an action being picked.
To define a custom action generator, you use the
actions function, which takes a thunk that returns
an array of action templates:
export const myAction = actions(() => {
return [
...
];
});In the returned array, each element is a value of the
ActionTemplate type, provided by the NPM
package. See the TypeScript
source for reference.
Here’s a generator for clicks in the center of a
canvas element:
const canvas = extract((state) => {
const canvas = state.document.querySelector("#my-canvas");
if (!canvas) {
return null;
}
const rect = canvas.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
return {
fingerprint: getFingerprint(canvas),
point: {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2,
}
};
}
return null;
});
export const clickCanvas = actions(() => {
return canvas.current ? [{ Click: canvasClick.current }] : [];
});For double-click actions, specify the delay between clicks in milliseconds (0-1000ms):
export const doubleClickCanvas = actions(() => {
return canvas.current ? [{
DoubleClick: {
...canvas,
delayMillis: 100,
}
}] : [];
});Most action parameters can also be specified as ranges, where Bombadil picks a random value within the range:
export const doubleClickCanvas = actions(() => {
return canvas.current ? [{
DoubleClick: {
...canvas,
delayMillis: [50, 500],
}
}] : [];
});The actions you return must be possible to perform in the
current state. Your action generators should therefore depend on
cells and
validate your actions before returning them, as done with
canvasCenter in the previous example. Another
example is the back action generator provided by
Bombadil, which checks that there’s a history entry to go back
to, otherwise returning [].
To give actions different weights, use the
weighted combinator and wrap each subgenerator in
an array with the weight as the first element:
export const navigation = weighted([
[10, back],
[1, forward],
[1, reload],
]);