As I've been working towards the first Utility AI test, I decided to fix my poor implementation of a Blackboard class. My blackboard is loosely based on this concept; it's an intermediate storage of the decisions and calculations of the AI system, for example:
- Where's the closest enemy?
- How many enemies are within melee distance?
- Is my health at a critical level?
Before, it was effectively a bunch of map<string, DataType >, and I used strings to access. Of course accessing maps all the time is going to be deadly to performance if I'm doing hundreds/thousands of accesses per turn, so I wanted to switch to a more future-proof, efficient system, which has the performance of enums, without having to recompile when I'm adding stuff.
The new system uses the following semantics:
- Property: A name for a blackboard entry, e.g. "ClosestEnemy"
- PropertyGroup: The group that a property belongs. All properties within a group have a single type. E.g. "Distance"
- PropertyMap: A map of property group names to properties, implemented as a vector<vector<string>>. One entry is special and contains a list of property groups. This is stored in the global registry.
- PropertyIndex: a pair (group_index, property_index). This is used to uniquely identify a property of a group
Now the blackboard is effectively a vector< pair< vector<T> /*property values*/, int /*group*/> >, and I'm using PropertyIndex to access it.
So, we create the property index once from strings (e.g. specified in JSON) and we're using the indices for fast access. One potential downside is that if we want to have only one property set for a group, and that property has an index of 10, we then need to allocate all 11 elements. But this can be easily solved by putting the property in its own group.
Blackboards need to be filled explicitly. Then, there's the Input Cache, which is my implementation of an automatically filled blackboard. From my last post regarding Utility AI , this would be the storage and calculation logic of values, which are the inputs to the response curves. So, these values behave exactly like a blackboard, but they're just limited to be floats only, in range [0,1], and I should provide an automatic way to calculate these values if required. So, this can reuse the above mentioned machinery: fetching a value from the input cache is done simply by using a PropertyIndex, as the Input Cache uses its own group to store the float values.
Next time, a decision example of a lumberjack shooting an endless horde of wolves, deciding upon a use of a shotgun, a pistol, a knife and idling using Utility AI.