/**
* The Blackboard is the memory structure required by `BehaviorTree` and its
* nodes. It only have 2 public methods: `set` and `get`. These methods works
* in 3 different contexts: global, per tree, and per node per tree.
*
* Suppose you have two different trees controlling a single object with a
* single blackboard, then:
*
* - In the global context, all nodes will access the stored information.
* - In per tree context, only nodes sharing the same tree share the stored
* information.
* - In per node per tree context, the information stored in the blackboard
* can only be accessed by the same node that wrote the data.
*
* The context is selected indirectly by the parameters provided to these
* methods, for example:
*
* // getting/setting variable in global context
* blackboard.set('testKey', 'value');
* var value = blackboard.get('testKey');
*
* // getting/setting variable in per tree context
* blackboard.set('testKey', 'value', tree.id);
* var value = blackboard.get('testKey', tree.id);
*
* // getting/setting variable in per node per tree context
* blackboard.set('testKey', 'value', tree.id, node.id);
* var value = blackboard.get('testKey', tree.id, node.id);
*
* Note: Internally, the blackboard store these memories in different
* objects, being the global on `_baseMemory`, the per tree on `_treeMemory`
* and the per node per tree dynamically create inside the per tree memory
* (it is accessed via `_treeMemory[id].nodeMemory`). Avoid to use these
* variables manually, use `get` and `set` instead.
*
* @module b3
* @class Blackboard
**/
export default class Blackboard {
/**
* Initialization method.
* @method initialize
* @constructor
**/
constructor() {
this._baseMemory = {};
this._treeMemory = {};
}
/**
* Internal method to retrieve the tree context memory. If the memory does
* not exist, this method creates it.
*
* @method _getTreeMemory
* @param {String} treeScope The id of the tree in scope.
* @return {Object} The tree memory.
* @protected
**/
_getTreeMemory(treeScope) {
if (!this._treeMemory[treeScope]) {
this._treeMemory[treeScope] = {
'nodeMemory' : {},
'openNodes' : [],
'traversalDepth' : 0,
'traversalCycle' : 0,
};
}
return this._treeMemory[treeScope];
}
/**
* Internal method to retrieve the node context memory, given the tree
* memory. If the memory does not exist, this method creates is.
*
* @method _getNodeMemory
* @param {String} treeMemory the tree memory.
* @param {String} nodeScope The id of the node in scope.
* @return {Object} The node memory.
* @protected
**/
_getNodeMemory(treeMemory, nodeScope) {
var memory = treeMemory.nodeMemory;
if (!memory[nodeScope]) {
memory[nodeScope] = {};
}
return memory[nodeScope];
}
/**
* Internal method to retrieve the context memory. If treeScope and
* nodeScope are provided, this method returns the per node per tree
* memory. If only the treeScope is provided, it returns the per tree
* memory. If no parameter is provided, it returns the global memory.
* Notice that, if only nodeScope is provided, this method will still
* return the global memory.
*
* @method _getMemory
* @param {String} treeScope The id of the tree scope.
* @param {String} nodeScope The id of the node scope.
* @return {Object} A memory object.
* @protected
**/
_getMemory(treeScope, nodeScope) {
var memory = this._baseMemory;
if (treeScope) {
memory = this._getTreeMemory(treeScope);
if (nodeScope) {
memory = this._getNodeMemory(memory, nodeScope);
}
}
return memory;
}
/**
* Stores a value in the blackboard. If treeScope and nodeScope are
* provided, this method will save the value into the per node per tree
* memory. If only the treeScope is provided, it will save the value into
* the per tree memory. If no parameter is provided, this method will save
* the value into the global memory. Notice that, if only nodeScope is
* provided (but treeScope not), this method will still save the value into
* the global memory.
*
* @method set
* @param {String} key The key to be stored.
* @param {String} value The value to be stored.
* @param {String} treeScope The tree id if accessing the tree or node
* memory.
* @param {String} nodeScope The node id if accessing the node memory.
**/
set(key, value, treeScope, nodeScope) {
var memory = this._getMemory(treeScope, nodeScope);
memory[key] = value;
}
/**
* Retrieves a value in the blackboard. If treeScope and nodeScope are
* provided, this method will retrieve the value from the per node per tree
* memory. If only the treeScope is provided, it will retrieve the value
* from the per tree memory. If no parameter is provided, this method will
* retrieve from the global memory. If only nodeScope is provided (but
* treeScope not), this method will still try to retrieve from the global
* memory.
*
* @method get
* @param {String} key The key to be retrieved.
* @param {String} treeScope The tree id if accessing the tree or node
* memory.
* @param {String} nodeScope The node id if accessing the node memory.
* @return {Object} The value stored or undefined.
**/
get(key, treeScope, nodeScope) {
var memory = this._getMemory(treeScope, nodeScope);
return memory[key];
}
/**
* Clear tree memory. If no treeScope, clear all memory.
*
* @method clear
* @param {String} treeScope The tree id.
**/
clear(treeScope)
{
if (treeScope) {
this._treeMemory[treeScope] = {
'nodeMemory' : {},
'openNodes' : [],
'traversalDepth' : 0,
'traversalCycle' : 0,
};
return;
}
this._baseMemory = {};
this._treeMemory = {};
}
/**
* Set value from Key-Value object.
*
* @method setFromObject
* @param {Object} sourceObject The data to be stored.
* @param {String} treeScope The tree id if accessing the tree or node
* memory.
* @param {String} nodeScope The node id if accessing the node memory.
**/
setFromObject(sourceObject, treeScope= '', nodeScope= '')
{
var memory = this._getMemory(treeScope, nodeScope);
Object.assign(memory, sourceObject);
return memory;
}
};