Repository: oguzeroglu/Ego
Branch: master
Commit: bf2c0af38947
Files: 30
Total size: 116.3 KB
Directory structure:
gitextract_frxhfo50/
├── .babelrc
├── .gitignore
├── LICENSE
├── README.md
├── js/
│ ├── Ego.js
│ ├── decision-tree/
│ │ ├── Decision.js
│ │ ├── DecisionTree.js
│ │ ├── Knowledge.js
│ │ ├── decision-methods/
│ │ │ ├── DecisionMethod.js
│ │ │ ├── IsFalse.js
│ │ │ ├── IsInRange.js
│ │ │ └── IsTrue.js
│ │ └── math/
│ │ └── Range.js
│ ├── state-machine/
│ │ ├── State.js
│ │ ├── StateMachine.js
│ │ └── Transition.js
│ └── util/
│ └── UUIDV4.js
├── package.json
├── rollup.config.js
└── test/
├── decision-tree/
│ ├── DecisionTest.js
│ ├── DecisionTreeTest.js
│ ├── KnowledgeTest.js
│ ├── decision-methods/
│ │ ├── DecisionMethodTest.js
│ │ ├── IsFalseTest.js
│ │ ├── IsInRangeTest.js
│ │ └── IsTrueTest.js
│ └── math/
│ └── RangeTest.js
└── state-machine/
├── StateMachineTest.js
├── StateTest.js
└── TransitionTest.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": [ "es2015" ]
}
================================================
FILE: .gitignore
================================================
build
node_modules
.DS_Store
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Oğuz Eroğlu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Ego
Ego is a lightweight decision making library for game AI. It provides decision trees and state machines (hierarchical and finite).
Ego is originally designed to be used by the [ROYGBIV engine](https://github.com/oguzeroglu/ROYGBIV), however can easily be used outside of ROYGBIV as well.
# Getting Started
## Index
- [Including It](#including-it)
- [Client-side Javascript](#client-side-javascript)
- [NodeJS](#nodejs)
- [Using Decision trees](#using-decision-trees)
- [Creating the Knowledge](#creating-the-knowledge)
- [Creating Decisions](#creating-decisions)
- [Connecting the Decisions](#connecting-the-decisions)
- [Creating the Decision Tree](#creating-the-decision-tree)
- [Putting It All Together](#putting-it-all-together)
- [Using State Machines](#using-state-machines)
- [Creating the Knowledge](#creating-the-knowledge-1)
- [Creating States](#creating-states)
- [Creating Transitions](#creating-transitions)
- [Creating the State Machine](#creating-the-state-machine)
- [Putting It All Together](#putting-it-all-together-1)
- [Note About Hierarchical State Machines](#note-about-hierarchical-state-machines)
## Including It
### Client-side Javascript
Download the latest release from [releases page](https://github.com/oguzeroglu/Ego/releases). Then include the `ego.min.js` file into your project via `<script>` tag:
`<script src="PATH_TO_ego.min.js"></script>`
### NodeJS
Install it via npm:
`npm i @oguz.eroglu/ego-js`
Then include the module into your project via `require`:
```javascript
var Ego = require("@oguz.eroglu/ego-js");
```
## Using Decision Trees
To get started with decision trees, let's create this decision tree with Ego:

### Creating the Knowledge
We'll start by creating the `Knowledge`. Knowledge class can contain boolean, numerical (integer/float) and vector (with x, y, z and length properties) information. For this decision tree, we need these informations to put into our knowledge:
* isEnemyVisible (of type boolean)
* isEnemyAudible (of type boolean)
* isEnemyOnFlank (of type boolean)
* distanceToEnemy (of type 3D vector)
```javascript
// Instantiate a Knowledge
var knowledge = new Ego.Knowledge();
// add isEnemyVisible information
knowledge.addBooleanInformation("isEnemyVisible", false);
// add isEnemyAudible information
knowledge.addBooleanInformation("isEnemyAudible", false);
// add isEnemyOnFlank information
knowledge.addBooleanInformation("isEnemyOnFlank", false);
// add distanceToEnemy information. We'll assume the enemy is 100 units far away on the axis x.
knowledge.addVectorInformation("distanceToEnemy", 100, 0, 0);
```
### Creating Decisions
We'll continue by creating the decisions. Ego provides these three decision methods:
* `IsTrue` to check if given boolean information is true.
* `IsFalse` to check if given boolean information is false.
* `IsInRange` to check if given numerical information or the length of given vector information is within given range.
We can reuse the decision methods across several decisions. For this decision tree, we need `IsTrue` and `IsInRange` decision methods. Let's create them:
```javascript
// create the IsTrue decision method
var isTrue = new Ego.IsTrue();
// create the IsInRange decision method. We'll start by creating a Range object.
// The first parameter is the lower bound and the second is the upper bound.
// Note that Ego Range instances are inclusive by default.
// Since we'd like to check if given distance is less than 10 units
// we'll set the lower bound to -Infinity and the upper bound to 10.
// [-Infinity, 10[
var range = new Ego.Range(-Infinity, 10);
range.makeUpperBoundExclusive();
// Create the decision method
var isInRange = new Ego.IsInRange(range);
```
```javascript
// the first parameter is the information name inside the Knowledge
// the second is the type of the information
// the third is the decision method
var isEnemyVisible = new Ego.Decision("isEnemyVisible", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var isEnemyAudible = new Ego.Decision("isEnemyAudible", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var isEnemyOnFlank = new Ego.Decision("isEnemyOnFlank", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var distanceLessThan10Units = new Ego.Decision("distanceToEnemy", Ego.InformationTypes.TYPE_VECTOR, isInRange);
```
### Connecting the Decisions
We'll then connect the decisions that we've created. If we connect a decision with another decision, Ego will continue to perform the decision tests. If we connect it with any other type (such as a String or an Object), Ego will act as if the decision is done and return the connected object:
```javascript
// If enemy is visible, we'll test if enemy is close.
isEnemyVisible.setYesNode(distanceLessThan10Units);
// If enemy is not visible, we'll test if enemy is audible.
isEnemyVisible.setNoNode(isEnemyAudible);
// If enemy is audible, we'll creep. We can simply return a string in this case.
isEnemyAudible.setYesNode("creep");
// If enemy is not audible, we'll do nothing. We can simply return a string in this case.
isEnemyAudible.setNoNode("doNothing");
// If enemy is less than 10 units away, we'll attack
distanceLessThan10Units.setYesNode("attack");
// We'll perform isEnemyOnFlank test otherwise
distanceLessThan10Units.setNoNode(isEnemyOnFlank);
// If enemy is on flank, we'll move
isEnemyOnFlank.setYesNode("move");
// We'll attack otherwise
isEnemyOnFlank.setNoNode("attack");
```
### Creating the Decision Tree
Now that we're all set, we can create our Decision Tree. We need to specifiy the root decision when creating the decision tree. In this case, the root decision is `isEnemyVisible`:
```javascript
var decisionTree = new Ego.DecisionTree(isEnemyVisible);
```
We can now make decisions. Since given our knowledge the enemy is not visible and also not audible, the initial decision result would be to do nothing.
```javascript
decisionTree.makeDecision(knowledge); // returns "doNothing"
```
We can always update our knowledge with new information and make new decisions. Let's update `isEnemyVisible` information. Since now enemy is visible and also the length of the distance vector is less than 10 units, we'll now chose to attack:
```javascript
// update isEnemyVisible information
knowledge.updateBooleanInformation("isEnemyVisible", true);
decisionTree.makeDecision(knowledge); // returns "attack"
```
In the same way, if we put the enemy 500 units away on the axis X (the length would be than more than 10 units) and the enemy is now on flank, we'll chose to move instead.
```javascript
// update knowledge
knowledge.updateVectorInformation("distanceToEnemy", 500, 0, 0);
knowledge.updateBooleanInformation("isEnemyOnFlank", true);
decisionTree.makeDecision(knowledge); // return "move"
```
### Putting It All Together
```javascript
var Ego = require("@oguz.eroglu/ego-js");
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isEnemyVisible", false);
knowledge.addBooleanInformation("isEnemyAudible", false);
knowledge.addBooleanInformation("isEnemyOnFlank", false);
knowledge.addVectorInformation("distanceToEnemy", 100, 0, 0);
var isTrue = new Ego.IsTrue();
var range = new Ego.Range(-Infinity, 10);
range.makeUpperBoundExclusive();
var isInRange = new Ego.IsInRange(range);
var isEnemyVisible = new Ego.Decision("isEnemyVisible", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var isEnemyAudible = new Ego.Decision("isEnemyAudible", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var isEnemyOnFlank = new Ego.Decision("isEnemyOnFlank", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var distanceLessThan10Units = new Ego.Decision("distanceToEnemy", Ego.InformationTypes.TYPE_VECTOR, isInRange);
isEnemyVisible.setYesNode(distanceLessThan10Units);
isEnemyVisible.setNoNode(isEnemyAudible);
isEnemyAudible.setYesNode("creep");
isEnemyAudible.setNoNode("doNothing");
distanceLessThan10Units.setYesNode("attack");
distanceLessThan10Units.setNoNode(isEnemyOnFlank);
isEnemyOnFlank.setYesNode("move");
isEnemyOnFlank.setNoNode("attack");
var decisionTree = new Ego.DecisionTree(isEnemyVisible);
knowledge.updateBooleanInformation("isEnemyVisible", true);
knowledge.updateVectorInformation("distanceToEnemy", 500, 0, 0);
knowledge.updateBooleanInformation("isEnemyOnFlank", true);
console.log(decisionTree.makeDecision(knowledge));
```
## Using State Machines
To get started with state machines, let's create this state machine with Ego:

### Creating the Knowledge
Just like Decision Trees, State Machines also use a Knowledge in order to decide if it's possible to switch from one state to another. For this example we'll need following informations to store in our Knowledge:
* isWeakEnemySeen (of type boolean)
* isStrongEnemySeen (of type boolean)
* health (of type float)
* distanceToEnemy (of type Vector)
`isWeakEnemySeen` and `isStrongEnemySeen` boolean informations will be set to true when the player sees a weak or a strong enemy. We'll use `health` information to decide whether the player is losing the fight or not. `distanceToEnemy` will be used to decide if the player is successfully escaped from an enemy.
```javascript
// create the knowledge
var knowledge = new Ego.Knowledge();
// create isWeakEnemySeen information
knowledge.addBooleanInformation("isWeakEnemySeen", false);
// create isStrongEnemySeen information
knowledge.addBooleanInformation("isStrongEnemySeen", false);
// create health information. Initially we'll set this to 100.
knowledge.addNumericalInformation("health", 100);
// create distanceToEnemy information. Initially we'll assume the enemy is 500 units far away on the axis X.
knowledge.addVectorInformation("distanceToEnemy", 500, 0, 0);
```
### Creating States
We'll move on by creating the states. For this example we need 3 states: `OnGuard`, `Fight` and `RunAway`:
```javascript
// create OnGuard state
var onGuardState = new Ego.State("OnGuard");
// create Fight state
var fightState = new Ego.State("Fight");
// create RunAway state
var runAwayState = new Ego.State("RunAway");
```
### Creating Transitions
We then need to create Transitions. Transitions are a subclass of Decisions, so they work the same way. In addition to Decisions, we need to define the source state and the target state as well in order to create a Transition.
We'll first define the decision methods just like we did while working with Decision Trees. For this example we need these decision methods:
* `IsTrue` to check if strong or weak enemy is seen (`isStrongEnemySeen` and `isWeakEnemySeen` informations)
* `IsInRange` to check if the player is losing the fight (`health` information is less than 20)
* Another `IsInRange` to check if the player successfully escaped (The length of `distanceToEnemy` is greater than 100 units)
```javascript
// create IsTrue decision method
var isTrue = new Ego.IsTrue();
// create IsInRange decision method for the health information.
// define the range
var lessThan20Range = new Ego.Range(-Infinity, 20);
lessThan20Range.makeUpperBoundExclusive();
// create the Decision method
var lessThan20 = new Ego.IsInRange(lessThan20Range);
// create IsInRange decision method for the distanceToEnemy information
// define the range
var greaterThan100Range = new Ego.Range(100, Infinity);
greaterThan100Range.makeLowerBoundExclusive();
// create the Decision method
var greaterThan100 = new Ego.IsInRange(greaterThan100Range);
```
Now that we have our decision methods ready, we can create our Transitions. The first parameter is the source state, the second is the target state, the third is the information name that we use inside our Knowledge, the fourth is the type of the information (either `Ego.InformationTypes.TYPE_BOOLEAN` or `Ego.InformationTypes.TYPE_NUMERICAL` or `Ego.InformationTypes.TYPE_VECTOR`) and the fifth is the decision method.
```javascript
// Transition for: See weak enemy
var seeWeakEnemyTransition = new Ego.Transition(onGuardState, fightState, "isWeakEnemySeen", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
// Transition for: See strong enemy
var seeStrongEnemyTransition = new Ego.Transition(onGuardState, runAwayState, "isStrongEnemySeen", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
// Transition for: Losing fight
var losingFightTransition = new Ego.Transition(fightState, runAwayState, "health", Ego.InformationTypes.TYPE_NUMERICAL, lessThan20);
// Transition for: Escaped
var escapedTransition = new Ego.Transition(runAwayState, onGuardState, "distanceToEnemy", Ego.InformationTypes.TYPE_VECTOR, greaterThan100);
```
### Creating the State Machine
Now that we have our states and transitions ready, we can create our State Machine. While creating the State Machine, we need to pass a name as the first argument, and the knowledge as the second one:
```javascript
// create the state machine
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
// add states
stateMachine.addState(onGuardState);
stateMachine.addState(runAwayState);
stateMachine.addState(fightState);
// add transitions
stateMachine.addTransition(seeWeakEnemyTransition);
stateMachine.addTransition(seeStrongEnemyTransition);
stateMachine.addTransition(losingFightTransition);
stateMachine.addTransition(escapedTransition);
// set the initial (entry) state
stateMachine.setEntryState(onGuardState);
```
Now that we have our state machine ready, we want to update it and listen for state changes. Note that state machines are updated recursively, that's why it's important not to create circular transitions to avoid infinite loops.
```javascript
// listen for state changes
stateMachine.onStateChanged(function(newState){
console.log("New state is: " + newState.getName());
});
// update the state machine
stateMachine.update();
```
This will print out: `New state is: OnGuard`. Since none of our transition conditions are satisfied, we're stuck in the initial state. Now, let's update the knowledge to jump to other states:
```javascript
// the player has seen a weak enemy
knowledge.updateBooleanInformation("isWeakEnemySeen", true);
stateMachine.update();
```
Now that the player has seen a weak enemy, this will additionally print out: `New state is: Fight`. Let's lose the fight:
```javascript
knowledge.updateNumericalInformation("health", 10);
knowledge.updateVectorInformation("distanceToEnemy", 3, 0, 0);
stateMachine.update(); // prints out: New state is: RunAway
```
### Putting It All Together
```javascript
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isWeakEnemySeen", false);
knowledge.addBooleanInformation("isStrongEnemySeen", false);
knowledge.addNumericalInformation("health", 100);
knowledge.addVectorInformation("distanceToEnemy", 500, 0, 0);
var onGuardState = new Ego.State("OnGuard");
var fightState = new Ego.State("Fight");
var runAwayState = new Ego.State("RunAway");
var isTrue = new Ego.IsTrue();
var lessThan20Range = new Ego.Range(-Infinity, 20);
lessThan20Range.makeUpperBoundExclusive();
var lessThan20 = new Ego.IsInRange(lessThan20Range);
var greaterThan100Range = new Ego.Range(100, Infinity);
greaterThan100Range.makeLowerBoundExclusive();
var greaterThan100 = new Ego.IsInRange(greaterThan100Range);
var seeWeakEnemyTransition = new Ego.Transition(onGuardState, fightState, "isWeakEnemySeen", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var seeStrongEnemyTransition = new Ego.Transition(onGuardState, runAwayState, "isStrongEnemySeen", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var losingFightTransition = new Ego.Transition(fightState, runAwayState, "health", Ego.InformationTypes.TYPE_NUMERICAL, lessThan20);
var escapedTransition = new Ego.Transition(runAwayState, onGuardState, "distanceToEnemy", Ego.InformationTypes.TYPE_VECTOR, greaterThan100);
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
stateMachine.addState(onGuardState);
stateMachine.addState(runAwayState);
stateMachine.addState(fightState);
stateMachine.addTransition(seeWeakEnemyTransition);
stateMachine.addTransition(seeStrongEnemyTransition);
stateMachine.addTransition(losingFightTransition);
stateMachine.addTransition(escapedTransition);
stateMachine.setEntryState(onGuardState);
stateMachine.onStateChanged(function(newState){
console.log("New state is: " + newState.getName());
});
stateMachine.update();
knowledge.updateBooleanInformation("isWeakEnemySeen", true);
stateMachine.update();
knowledge.updateNumericalInformation("health", 10);
knowledge.updateVectorInformation("distanceToEnemy", 3, 0, 0);
stateMachine.update();
```
### Note About Hierarchical State Machines
Ego supports hierarchical state machines and cross hierarchy transitions. If a state machine is passed rather than a state to `StateMachine#addState` API, Ego automatically updates the child state machine if the current state of the parent state machine is the child state machine and the parent state machine is updated.
# License
Ego uses MIT license.
================================================
FILE: js/Ego.js
================================================
export { Knowledge } from "./decision-tree/Knowledge";
export { DecisionMethod } from "./decision-tree/decision-methods/DecisionMethod";
export { IsTrue } from "./decision-tree/decision-methods/IsTrue";
export { IsFalse } from "./decision-tree/decision-methods/IsFalse";
export { IsInRange } from "./decision-tree/decision-methods/IsInRange";
export { Range } from "./decision-tree/math/Range";
export { Decision, InformationTypes } from "./decision-tree/Decision";
export { DecisionTree } from "./decision-tree/DecisionTree";
export { State } from "./state-machine/State";
export { Transition } from "./state-machine/Transition";
export { StateMachine } from "./state-machine/StateMachine";
================================================
FILE: js/decision-tree/Decision.js
================================================
var InformationTypes = {
TYPE_BOOLEAN: "TYPE_BOOLEAN",
TYPE_NUMERICAL: "TYPE_NUMERICAL",
TYPE_VECTOR: "TYPE_VECTOR"
};
var Decision = function(informationName, informationType, decisionMethod){
this._informationName = informationName;
this._informationType = informationType;
this._decisionMethod = decisionMethod;
this._yesNode = null;
this._noNode = null;
}
Decision.prototype.clone = function(){
var cloned = new Decision(this._informationName, this._informationType, this._decisionMethod.clone());
var yesNode = this.getYesNode();
var noNode = this.getNoNode();
if (yesNode instanceof Decision){
cloned.setYesNode(yesNode.clone());
}else{
cloned.setYesNode(yesNode);
}
if (noNode instanceof Decision){
cloned.setNoNode(noNode.clone());
}else{
cloned.setNoNode(noNode);
}
return cloned;
}
Decision.prototype.setYesNode = function(node){
this._yesNode = node;
}
Decision.prototype.getYesNode = function(){
return this._yesNode;
}
Decision.prototype.setNoNode = function(node){
this._noNode = node;
}
Decision.prototype.getNoNode = function(){
return this._noNode;
}
Decision.prototype.make = function(knowledge){
var informationType = this._informationType;
var informationName = this._informationName;
var information = null;
if (informationType == InformationTypes.TYPE_BOOLEAN){
information = knowledge.getBooleanInformation(informationName);
}else if (informationType == InformationTypes.TYPE_NUMERICAL){
information = knowledge.getNumericalInformation(informationName);
}else if (informationType == InformationTypes.TYPE_VECTOR){
information = knowledge.getVectorInformation(informationName);
}else{
throw new Error("No such information type.");
}
if (information == null){
throw new Error("No such information in knowledge: " + informationName);
}
if (this._decisionMethod.perform(information)){
return this._yesNode;
}
return this._noNode;
}
export { Decision };
export { InformationTypes };
================================================
FILE: js/decision-tree/DecisionTree.js
================================================
import { Decision } from "./Decision";
var DecisionTree = function(rootNode){
if (!(rootNode instanceof Decision)){
throw new Error("Root node must be an instance of Ego.Decision");
}
this._rootNode = rootNode;
}
DecisionTree.prototype.makeDecision = function(knowledge){
var currentNode = this._rootNode;
while(currentNode instanceof Decision){
currentNode = currentNode.make(knowledge);
}
return currentNode;
}
DecisionTree.prototype.clone = function(){
return new DecisionTree(this._rootNode.clone());
}
export { DecisionTree };
================================================
FILE: js/decision-tree/Knowledge.js
================================================
var Knowledge = function(){
this._booleanMap = {};
this._numericalMap = {};
this._vectorMap = {};
}
Knowledge.prototype.clone = function(){
var cloned = new Knowledge();
for (var infoName in this._booleanMap){
cloned.addBooleanInformation(infoName, this.getBooleanInformation(infoName));
}
for (var infoName in this._numericalMap){
cloned.addNumericalInformation(infoName, this.getNumericalInformation(infoName));
}
for (var infoName in this._vectorMap){
var vect = this.getVectorInformation(infoName);
cloned.addVectorInformation(infoName, vect.x, vect.y, vect.z);
}
return cloned;
}
Knowledge.prototype.addBooleanInformation = function(name, booleanValue){
if (this._hasBooleanInformation(name)){
return false;
}
this._booleanMap[name] = !!booleanValue;
return true;
}
Knowledge.prototype.addNumericalInformation = function(name, numericalValue){
if (this._hasNumericalInformation(name)){
return false;
}
var float = parseFloat(numericalValue);
if (isNaN(float)){
return false;
}
this._numericalMap[name] = float;
return true;
}
Knowledge.prototype.addVectorInformation = function(name, x, y, z){
if (this._hasVectorInformation(name)){
return false;
}
var parsedX = parseFloat(x);
var parsedY = parseFloat(y);
var parsedZ = parseFloat(z);
if (isNaN(parsedX) || isNaN(parsedY) || isNaN(parsedZ)){
return false;
}
var xSqr = parsedX * parsedX;
var ySqr = parsedY * parsedY;
var zSqr = parsedZ * parsedZ;
this._vectorMap[name] = {x: parsedX, y: parsedY, z: parsedZ, length: Math.sqrt(xSqr + ySqr + zSqr), isVector: true};
return true;
}
Knowledge.prototype.getBooleanInformation = function(name){
if (this._hasBooleanInformation(name)){
return this._booleanMap[name];
}
return null;
}
Knowledge.prototype.getNumericalInformation = function(name){
if (this._hasNumericalInformation(name)){
return this._numericalMap[name];
}
return null;
}
Knowledge.prototype.getVectorInformation = function(name){
if (this._hasVectorInformation(name)){
return this._vectorMap[name];
}
return null;
}
Knowledge.prototype.updateBooleanInformation = function(name, booleanValue){
if (!this._hasBooleanInformation(name)){
return false;
}
this._booleanMap[name] = !!booleanValue;
return true;
}
Knowledge.prototype.updateNumericalInformation = function(name, numericalValue){
if (!this._hasNumericalInformation(name)){
return false;
}
var float = parseFloat(numericalValue);
if (isNaN(float)){
return false;
}
this._numericalMap[name] = float;
return true;
}
Knowledge.prototype.updateVectorInformation = function(name, x, y, z){
if (!this._hasVectorInformation(name)){
return false;
}
var parsedX = parseFloat(x);
var parsedY = parseFloat(y);
var parsedZ = parseFloat(z);
if (isNaN(parsedX) || isNaN(parsedY) || isNaN(parsedZ)){
return false;
}
var xSqr = parsedX * parsedX;
var ySqr = parsedY * parsedY;
var zSqr = parsedZ * parsedZ;
var vect = this._vectorMap[name];
vect.x = parsedX;
vect.y = parsedY;
vect.z = parsedZ;
vect.length = Math.sqrt(xSqr + ySqr + zSqr);
return true;
}
Knowledge.prototype.deleteBooleanInformation = function(name){
if (!this._hasBooleanInformation(name)){
return false;
}
delete this._booleanMap[name];
return true;
}
Knowledge.prototype.deleteNumericalInformation = function(name){
if (!this._hasNumericalInformation(name)){
return false;
}
delete this._numericalMap[name];
return true;
}
Knowledge.prototype.deleteVectorInformation = function(name){
if (!this._hasVectorInformation(name)){
return false;
}
delete this._vectorMap[name];
return true;
}
Knowledge.prototype._hasBooleanInformation = function(name){
return !(typeof this._booleanMap[name] === "undefined");
}
Knowledge.prototype._hasNumericalInformation = function(name){
return !(typeof this._numericalMap[name] === "undefined");
}
Knowledge.prototype._hasVectorInformation = function(name){
return !(typeof this._vectorMap[name] === "undefined");
}
export { Knowledge };
================================================
FILE: js/decision-tree/decision-methods/DecisionMethod.js
================================================
var DecisionMethod = function(parameter){
this._parameter = parameter;
}
DecisionMethod.prototype.updateParameter = function(parameter){
this._parameter = parameter;
}
DecisionMethod.prototype.perform = function(val){
return true;
}
DecisionMethod.prototype.clone = function(){
var clonedParam = (this._parameter && this._parameter.clone)? this._parameter.clone(): this._parameter;
return new this.constructor(clonedParam);
}
export { DecisionMethod };
================================================
FILE: js/decision-tree/decision-methods/IsFalse.js
================================================
import { DecisionMethod } from "./DecisionMethod";
var IsFalse = function(){
DecisionMethod.call(this, null);
}
IsFalse.prototype = Object.create(DecisionMethod.prototype);
IsFalse.prototype.perform = function(val){
return val == false;
}
Object.defineProperty(IsFalse.prototype, 'constructor', { value: IsFalse, enumerable: false, writable: true });
export { IsFalse };
================================================
FILE: js/decision-tree/decision-methods/IsInRange.js
================================================
import { DecisionMethod } from "./DecisionMethod";
var IsInRange = function(range){
DecisionMethod.call(this, range);
}
IsInRange.prototype = Object.create(DecisionMethod.prototype);
IsInRange.prototype.perform = function(val){
var range = this._parameter;
if (val.isVector){
return range.testNumber(val.length);
}
return range.testNumber(val);
}
Object.defineProperty(IsInRange.prototype, 'constructor', { value: IsInRange, enumerable: false, writable: true });
export { IsInRange };
================================================
FILE: js/decision-tree/decision-methods/IsTrue.js
================================================
import { DecisionMethod } from "./DecisionMethod";
var IsTrue = function(){
DecisionMethod.call(this, null);
}
IsTrue.prototype = Object.create(DecisionMethod.prototype);
IsTrue.prototype.perform = function(val){
return val == true;
}
Object.defineProperty(IsTrue.prototype, 'constructor', { value: IsTrue, enumerable: false, writable: true });
export { IsTrue };
================================================
FILE: js/decision-tree/math/Range.js
================================================
var Range = function(lowerBound, upperBound){
this._isLowerBoundInclusive = true;
this._isUpperBoundInclusive = true;
this._lowerBound = lowerBound;
this._upperBound = upperBound;
}
Range.prototype.clone = function(){
var cloned = new Range(this._lowerBound, this._upperBound);
if (this._isLowerBoundInclusive){
cloned.makeLowerBoundInclusive();
}else{
cloned.makeLowerBoundExclusive();
}
if (this._isUpperBoundInclusive){
cloned.makeUpperBoundInclusive();
}else{
cloned.makeUpperBoundExclusive();
}
return cloned;
}
Range.prototype.makeLowerBoundInclusive = function(){
this._isLowerBoundInclusive = true;
}
Range.prototype.makeLowerBoundExclusive = function(){
this._isLowerBoundInclusive = false;
}
Range.prototype.makeUpperBoundInclusive = function(){
this._isUpperBoundInclusive = true;
}
Range.prototype.makeUpperBoundExclusive = function(){
this._isUpperBoundInclusive = false;
}
Range.prototype.updateLowerBound = function(lowerBound){
this._lowerBound = lowerBound;
}
Range.prototype.updateUpperBound = function(upperBound){
this._upperBound = upperBound;
}
Range.prototype.testNumber = function(number){
var lower = this._lowerBound;
var upper = this._upperBound;
if (this._isUpperBoundInclusive){
if (number > upper){
return false;
}
}else{
if (number >= upper){
return false;
}
}
if (this._isLowerBoundInclusive){
if (number < lower){
return false;
}
}else{
if (number <= lower){
return false;
}
}
return true;
}
export { Range };
================================================
FILE: js/state-machine/State.js
================================================
import uuidv4 from "../util/UUIDV4";
var State = function(name){
this._name = name;
this._id = uuidv4();
this._parent = null;
}
State.prototype.clone = function(){
return new State(this.getName());
}
State.prototype.setParent = function(parent){
if (this._parent){
return false;
}
this._parent = parent;
return true;
}
State.prototype.removeParent = function(){
this._parent = null;
}
State.prototype.getID = function(){
return this._id;
}
State.prototype.getParent = function(){
return this._parent;
}
State.prototype.getName = function(){
return this._name;
}
export { State };
================================================
FILE: js/state-machine/StateMachine.js
================================================
import { State } from "./State";
var StateMachine = function(name, knowledge){
State.call(this, name);
this._knowledge = knowledge;
this._statesByID = {};
this._transitionsByStateID = {};
this._stateChangedCallbackFunction = null;
this._entryState = null;
this._currentState = null;
}
StateMachine.prototype = Object.create(State.prototype);
StateMachine.prototype.clone = function(overrideKnowledge, idsObj){
var clonedStateMachine = new StateMachine(this.getName(), overrideKnowledge || this._knowledge.clone());
var clonedIDs = idsObj || {};
for (var stateID in this._statesByID){
var state = this._statesByID[stateID];
var cloned = clonedIDs[stateID] || state.clone(overrideKnowledge || null, clonedIDs);
clonedStateMachine.addState(cloned);
clonedIDs[stateID] = cloned;
if (this._entryState == state){
clonedStateMachine.setEntryState(cloned);
}
}
for (var stateID in this._transitionsByStateID){
var transitions = this._transitionsByStateID[stateID];
for (var i = 0; i < transitions.length; i ++){
var transition = transitions[i];
var source = transition.getSourceNode();
var target = transition.getTargetNode();
var overrideSource = clonedIDs[source.getID()] || null;
var overrideTarget = clonedIDs[target.getID()] || null;
var cloned = transition.clone(overrideSource, overrideTarget, overrideKnowledge || null, clonedIDs);
clonedStateMachine.addTransition(cloned);
clonedIDs[source.getID()] = cloned.getSourceNode();
clonedIDs[target.getID()] = cloned.getTargetNode();
}
}
return clonedStateMachine;
}
StateMachine.prototype.hasState = function(state){
if (state.getID() == this.getID()){
return true;
}
var result = false;
for (var stateID in this._statesByID){
var curState = this._statesByID[stateID];
result = result || curState.getID() == state.getID();
if (curState instanceof StateMachine){
result = result || curState.hasState(state);
}
}
return result;
}
StateMachine.prototype.addState = function(state){
if (state instanceof StateMachine && state.hasState(this)){
return false;
}
if (state.setParent(this)){
this._statesByID[state.getID()] = state;
this._transitionsByStateID[state.getID()] = [];
return true;
}
return false;
}
StateMachine.prototype.removeState = function(state){
if (state.getParent() != this){
return false;
}
if (this._currentState && this._currentState.getID() == state.getID()){
throw new Error("Cannot remove the active state.");
}
if (this._entryState && this._entryState.getID() == state.getID()){
this._entryState = null;
}
state.removeParent();
delete this._statesByID[state.getID()];
delete this._transitionsByStateID[state.getID()];
return true;
}
StateMachine.prototype.addTransition = function(transition){
var sourceNode = transition.getSourceNode();
if (sourceNode.getParent() == this){
var existingTransitions = this._transitionsByStateID[sourceNode.getID()];
for (var i = 0; i < existingTransitions.length; i ++){
var existingTransition = existingTransitions[i];
if (existingTransition.getTargetNode() == transition.getTargetNode()){
return false;
}
}
existingTransitions.push(transition);
return true;
}
return false;
}
StateMachine.prototype.removeTransition = function(transition){
var sourceNode = transition.getSourceNode();
if (sourceNode.getParent() == this){
var transitions = this._transitionsByStateID[sourceNode.getID()];
var index = null;
for (var i = 0; i < transitions.length; i ++){
if (transitions[i].getTargetNode() == transition.getTargetNode()){
index = i;
break;
}
}
if (index == null){
return false;
}
transitions.splice(index, 1);
return true;
}
return false;
}
StateMachine.prototype.setEntryState = function(state){
if (!this._statesByID[state.getID()]){
return false;
}
this._entryState = state;
return true;
}
StateMachine.prototype.onStateChanged = function(callbackFunction){
this._stateChangedCallbackFunction = callbackFunction;
}
StateMachine.prototype.reset = function(){
this._currentState = null;
}
StateMachine.prototype.onCrossHierarchyTransition = function(node){
this._changeState(node);
this.update();
}
StateMachine.prototype._onNewState = function(newState){
if (this._stateChangedCallbackFunction){
this._stateChangedCallbackFunction(newState);
}
}
StateMachine.prototype._getNextState = function(currentState){
if (currentState == null){
return null;
}
var transitions = this._transitionsByStateID[currentState.getID()];
var knowledge = this._knowledge;
for (var i = 0; i < transitions.length; i ++){
var transition = transitions[i];
if (transition.isPossible(knowledge)){
return transition;
}
}
return null;
}
StateMachine.prototype._changeState = function(newState){
if (this._currentState && this._currentState instanceof StateMachine){
this._currentState.reset();
}
this._currentState = newState;
this._onNewState(newState);
}
StateMachine.prototype.update = function(){
if (!this._entryState){
throw new Error("Entry state not set. Cannot update the StateMachine.");
}
if (!this._currentState){
this._changeState(this._entryState);
}
var transition = this._getNextState(this._currentState);
while (transition){
var targetNode = transition.getTargetNode();
var targetParent = targetNode.getParent();
if (targetParent == this){
this._changeState(transition.getTargetNode());
}else{
if (targetParent != null){
targetParent.onCrossHierarchyTransition(targetNode);
}
this.reset();
}
transition = this._getNextState(this._currentState);
}
if (this._currentState instanceof StateMachine){
this._currentState.update();
}
}
Object.defineProperty(StateMachine.prototype, 'constructor', { value: StateMachine, enumerable: false, writable: true });
export { StateMachine };
================================================
FILE: js/state-machine/Transition.js
================================================
import { Decision } from "../decision-tree/Decision";
var Transition = function(source, target, infoName, infoType, decisionMethod){
Decision.call(this, infoName, infoType, decisionMethod);
this.setYesNode(target);
this.setNoNode(source);
}
Transition.prototype = Object.create(Decision.prototype);
Transition.prototype.clone = function(overrideSource, overrideTarget, overrideKnowledge, idsObj){
var source = null;
var target = null;
if (overrideSource){
source = overrideSource;
}else{
source = this.getSourceNode().clone(overrideKnowledge || null, idsObj || null);
}
if (overrideTarget){
target = overrideTarget;
}else{
target = this.getTargetNode().clone(overrideKnowledge || null, idsObj || null);
}
return new Transition(source, target, this._informationName, this._informationType, this._decisionMethod.clone());
}
Transition.prototype.isPossible = function(knowledge){
var decisionResult = this.make(knowledge);
if (decisionResult == this._yesNode){
return true;
}
return false;
}
Transition.prototype.getSourceNode = function(){
return this.getNoNode();
}
Transition.prototype.getTargetNode = function(){
return this.getYesNode();
}
Object.defineProperty(Transition.prototype, 'constructor', { value: Transition, enumerable: false, writable: true });
export { Transition };
================================================
FILE: js/util/UUIDV4.js
================================================
var uuidv4 = function(){
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
export default uuidv4 ;
================================================
FILE: package.json
================================================
{
"name": "@oguz.eroglu/ego-js",
"version": "1.1.0",
"description": "A lightweight decision making library for game AI.",
"main": "build/Ego.js",
"module": "build/Ego.mjs",
"scripts": {
"build": "rollup --config rollup.config.js --environment BUILD:production && minify ./build/Ego.js -o ./build/Ego.min.js",
"test": "npm run build && mocha test/**/*"
},
"repository": {
"type": "git",
"url": "git+https://github.com/oguzeroglu/ego.git"
},
"keywords": [
"game-ai",
"decision",
"game-development",
"roygbiv"
],
"author": "Oguz Eroglu",
"license": "MIT",
"bugs": {
"url": "https://github.com/oguzeroglu/ego/issues"
},
"homepage": "https://github.com/oguzeroglu/ego#readme",
"dependencies": {
},
"devDependencies": {
"babel-eslint": "^7.1.1",
"babel-minify": "^0.5.1",
"babel-plugin-external-helpers": "^6.18.0",
"babel-preset-es2015": "^6.18.0",
"babel-register": "^6.18.0",
"babelrc-rollup": "^3.0.0",
"eslint": "^4.1.1",
"expect.js": "^0.3.1",
"istanbul": "^0.4.5",
"mocha": "^3.2.0",
"rollup": "^0.43.0",
"rollup-plugin-babel": "^2.7.1",
"rollup-plugin-istanbul": "^1.1.0",
"rollup-watch": "^4.3.1"
}
}
================================================
FILE: rollup.config.js
================================================
import babel from 'rollup-plugin-babel';
import babelrc from 'babelrc-rollup';
import istanbul from 'rollup-plugin-istanbul';
let pkg = require('./package.json');
let external = Object.keys(pkg.dependencies);
let plugins = [
babel(babelrc())
];
if (process.env.BUILD !== 'production') {
plugins.push(istanbul({
exclude: ['test/**/*', 'node_modules/**/*']
}));
}
export default {
entry: './js/Ego.js',
plugins: plugins,
external: external,
targets: [
{
dest: pkg.main,
format: 'umd',
moduleName: 'Ego',
sourceMap: true
},
{
dest: pkg.module,
format: 'umd',
sourceMap: true,
moduleName: "Ego"
}
]
};
================================================
FILE: test/decision-tree/DecisionTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../build/Ego");
describe("Decision", function(){
it("should export information types", function(){
var informationTypes = Ego.InformationTypes;
expect(informationTypes.TYPE_BOOLEAN).to.eql("TYPE_BOOLEAN");
expect(informationTypes.TYPE_NUMERICAL).to.eql("TYPE_NUMERICAL");
expect(informationTypes.TYPE_VECTOR).to.eql("TYPE_VECTOR");
});
it("should initialize", function(){
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
expect(decision._informationName).to.eql("isStuffHappening");
expect(decision._informationType).to.eql(Ego.InformationTypes.TYPE_BOOLEAN);
expect(decision._decisionMethod).to.equal(decisionMethod);
expect(decision._yesNode).to.eql(null);
expect(decision._noNode).to.eql(null);
});
it("should set yes node", function(){
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var decision2 = new Ego.Decision("canSee", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
decision.setYesNode(decision2);
expect(decision._yesNode).to.equal(decision2);
});
it("should get yes node", function(){
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var decision2 = new Ego.Decision("canSee", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
decision.setYesNode(decision2);
expect(decision.getYesNode()).to.equal(decision2);
});
it("should set no node", function(){
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var decision2 = new Ego.Decision("canSee", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
decision.setNoNode(decision2);
expect(decision._noNode).to.equal(decision2);
});
it("should get no node", function(){
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var decision2 = new Ego.Decision("canSee", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
decision.setNoNode(decision2);
expect(decision.getNoNode()).to.equal(decision2);
});
it("should make given boolean information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
decision.setYesNode("yes");
decision.setNoNode("no");
expect(decision.make(knowledge)).to.eql("yes");
knowledge.updateBooleanInformation("isStuffHappening", false);
expect(decision.make(knowledge)).to.eql("no");
});
it("should make given numerical information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addNumericalInformation("health", 70);
var range = new Ego.Range(0, 30);
var decisionMethod = new Ego.IsInRange(range);
var decision = new Ego.Decision("health", Ego.InformationTypes.TYPE_NUMERICAL, decisionMethod);
decision.setYesNode("yes");
decision.setNoNode("no");
expect(decision.make(knowledge)).to.eql("no");
knowledge.updateNumericalInformation("health", 15);
expect(decision.make(knowledge)).to.eql("yes");
});
it("should make given vector information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addVectorInformation("distance", 100, 100, 100);
var range = new Ego.Range(0, 100);
var decisionMethod = new Ego.IsInRange(range);
var decision = new Ego.Decision("distance", Ego.InformationTypes.TYPE_VECTOR, decisionMethod);
decision.setYesNode("yes");
decision.setNoNode("no");
expect(decision.make(knowledge)).to.eql("no");
range.updateUpperBound(300);
expect(decision.make(knowledge)).to.eql("yes");
});
it("should throw an exception if information type unknown", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.UNKNOWN_INFORMATION, decisionMethod);
decision.setYesNode("yes");
decision.setNoNode("no");
var catchedErr = null;
try {
decision.make(knowledge);
}catch (err){
catchedErr = err;
}
expect(catchedErr).not.to.eql(null);
expect(catchedErr.message).to.eql("No such information type.");
});
it("should throw an exception if information non existent", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var decisionMethod = new Ego.IsTrue();
var decision = new Ego.Decision("stuff", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
decision.setYesNode("yes");
decision.setNoNode("no");
var catchedErr = null;
try {
decision.make(knowledge);
}catch (err){
catchedErr = err;
}
expect(catchedErr).not.to.eql(null);
expect(catchedErr.message).to.eql("No such information in knowledge: stuff")
});
it("should clone", function(){
var decision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var cloned = decision.clone();
expect(decision).to.eql(cloned);
expect(decision === cloned).to.eql(false);
expect(decision._decisionMethod).to.eql(cloned._decisionMethod);
expect(decision._decisionMethod === cloned._decisionMethod).to.eql(false);
var decision2 = new Ego.Decision("age", Ego.InformationTypes.TYPE_NUMERICAL, new Ego.IsInRange(new Ego.Range(70, Infinity)));
var decision3 = new Ego.Decision("areYouOk", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
decision.setYesNode(decision2);
decision.setNoNode(decision3);
decision2.setYesNode("You're old.");
decision2.setNoNode("You're not old.");
decision3.setYesNode("You're ok.");
decision3.setNoNode("You're not ok.");
cloned = decision.clone();
expect(decision).to.eql(cloned);
expect(decision === cloned).to.eql(false);
});
});
================================================
FILE: test/decision-tree/DecisionTreeTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../build/Ego");
describe("DecisionTree", function(){
it("should initialize", function(){
var rootDecision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var decisionTree = new Ego.DecisionTree(rootDecision);
expect(decisionTree._rootNode).to.equal(rootDecision);
});
it("should throw an error on construction if rootNode is not a Decision", function(){
var catchedErr = null;
try {
var decisionTree = new Ego.DecisionTree("not_a_decision");
}catch(err){
catchedErr = err;
}
expect(catchedErr).not.to.eql(null);
expect(catchedErr.message).to.eql("Root node must be an instance of Ego.Decision")
});
it("should clone", function(){
var rootDecision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var decisionTree = new Ego.DecisionTree(rootDecision);
var cloned = decisionTree.clone();
expect(decisionTree).to.eql(cloned);
expect(decisionTree === cloned).to.eql(false);
expect(decisionTree._rootNode).to.eql(cloned._rootNode);
expect(decisionTree._rootNode === cloned._rootNode).to.eql(false);
});
describe("should make decision", function(){
it("root node only", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var rootDecision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
rootDecision.setYesNode("chill.");
rootDecision.setNoNode("make it happen.");
var decisionTree = new Ego.DecisionTree(rootDecision);
expect(decisionTree.makeDecision(knowledge)).to.eql("chill.");
knowledge.updateBooleanInformation("isStuffHappening", false);
expect(decisionTree.makeDecision(knowledge)).to.eql("make it happen.");
});
it("two levels", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
knowledge.addBooleanInformation("doYouWantThis", true);
var isTrueMethod = new Ego.IsTrue();
var rootDecision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, isTrueMethod);
var decision2 = new Ego.Decision("doYouWantThis", Ego.InformationTypes.TYPE_BOOLEAN, isTrueMethod);
var decision3 = new Ego.Decision("doYouWantThis", Ego.InformationTypes.TYPE_BOOLEAN, isTrueMethod);
decision2.setYesNode("chill");
decision2.setNoNode("stop it");
decision3.setYesNode("chill");
decision3.setNoNode("make it happen");
rootDecision.setYesNode(decision2);
rootDecision.setNoNode(decision3);
var decisionTree = new Ego.DecisionTree(rootDecision);
expect(decisionTree.makeDecision(knowledge)).to.eql("chill");
knowledge.updateBooleanInformation("doYouWantThis", false);
expect(decisionTree.makeDecision(knowledge)).to.eql("stop it");
knowledge.updateBooleanInformation("isStuffHappening", false);
knowledge.updateBooleanInformation("doYouWantThis", true);
expect(decisionTree.makeDecision(knowledge)).to.eql("chill");
knowledge.updateBooleanInformation("doYouWantThis", false);
expect(decisionTree.makeDecision(knowledge)).to.eql("make it happen");
});
it("three levels", function(){
var knowledge = new Ego.Knowledge();
knowledge.addNumericalInformation("numberOfFamilyMembers", 4);
knowledge.addNumericalInformation("salary", 100000);
knowledge.addBooleanInformation("isMarried", true);
var familyMemberRange = new Ego.Range(3, Infinity);
familyMemberRange.makeLowerBoundInclusive();
var salaryRange1 = new Ego.Range(80000, Infinity);
salaryRange1.makeLowerBoundInclusive();
var salaryRange2 = new Ego.Range(40000, Infinity);
salaryRange2.makeLowerBoundInclusive();
var decision1 = new Ego.Decision("numberOfFamilyMembers", Ego.InformationTypes.TYPE_NUMERICAL, new Ego.IsInRange(familyMemberRange));
var decision2 = new Ego.Decision("salary", Ego.InformationTypes.TYPE_NUMERICAL, new Ego.IsInRange(salaryRange1));
var decision3 = new Ego.Decision("isMarried", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var decision4 = new Ego.Decision("isMarried", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var decision5 = new Ego.Decision("salary", Ego.InformationTypes.TYPE_NUMERICAL, new Ego.IsInRange(salaryRange1));
decision1.setYesNode(decision3);
decision1.setNoNode(decision2);
decision2.setYesNode("4BHK");
decision2.setNoNode(decision4);
decision3.setYesNode("3BHK");
decision3.setNoNode(decision5);
decision4.setYesNode("3BHK");
decision4.setNoNode("2BHK");
decision5.setYesNode("2BHK");
decision5.setNoNode("1BHK");
var decisionTree = new Ego.DecisionTree(decision1);
expect(decisionTree.makeDecision(knowledge)).to.eql("3BHK");
knowledge.updateBooleanInformation("isMarried", false);
expect(decisionTree.makeDecision(knowledge)).to.eql("2BHK");
knowledge.updateNumericalInformation("salary", 20000);
expect(decisionTree.makeDecision(knowledge)).to.eql("1BHK");
knowledge.updateNumericalInformation("numberOfFamilyMembers", 1);
knowledge.updateNumericalInformation("salary", 100000);
expect(decisionTree.makeDecision(knowledge)).to.eql("4BHK");
knowledge.updateNumericalInformation("salary", 50000);
knowledge.updateBooleanInformation("isMarried", true);
expect(decisionTree.makeDecision(knowledge)).to.eql("3BHK");
knowledge.updateBooleanInformation("isMarried", false);
expect(decisionTree.makeDecision(knowledge)).to.eql("2BHK");
});
describe("clone", function(){
it("root node only", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var rootDecision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
rootDecision.setYesNode("chill.");
rootDecision.setNoNode("make it happen.");
var decisionTree = new Ego.DecisionTree(rootDecision);
var clone = decisionTree.clone();
expect(clone.makeDecision(knowledge)).to.eql("chill.");
knowledge.updateBooleanInformation("isStuffHappening", false);
expect(clone.makeDecision(knowledge)).to.eql("make it happen.");
});
it("two levels", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
knowledge.addBooleanInformation("doYouWantThis", true);
var isTrueMethod = new Ego.IsTrue();
var rootDecision = new Ego.Decision("isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, isTrueMethod);
var decision2 = new Ego.Decision("doYouWantThis", Ego.InformationTypes.TYPE_BOOLEAN, isTrueMethod);
var decision3 = new Ego.Decision("doYouWantThis", Ego.InformationTypes.TYPE_BOOLEAN, isTrueMethod);
decision2.setYesNode("chill");
decision2.setNoNode("stop it");
decision3.setYesNode("chill");
decision3.setNoNode("make it happen");
rootDecision.setYesNode(decision2);
rootDecision.setNoNode(decision3);
var decisionTree = new Ego.DecisionTree(rootDecision);
var clone = decisionTree.clone();
expect(clone.makeDecision(knowledge)).to.eql("chill");
knowledge.updateBooleanInformation("doYouWantThis", false);
expect(clone.makeDecision(knowledge)).to.eql("stop it");
knowledge.updateBooleanInformation("isStuffHappening", false);
knowledge.updateBooleanInformation("doYouWantThis", true);
expect(clone.makeDecision(knowledge)).to.eql("chill");
knowledge.updateBooleanInformation("doYouWantThis", false);
expect(clone.makeDecision(knowledge)).to.eql("make it happen");
});
it("three levels", function(){
var knowledge = new Ego.Knowledge();
knowledge.addNumericalInformation("numberOfFamilyMembers", 4);
knowledge.addNumericalInformation("salary", 100000);
knowledge.addBooleanInformation("isMarried", true);
var familyMemberRange = new Ego.Range(3, Infinity);
familyMemberRange.makeLowerBoundInclusive();
var salaryRange1 = new Ego.Range(80000, Infinity);
salaryRange1.makeLowerBoundInclusive();
var salaryRange2 = new Ego.Range(40000, Infinity);
salaryRange2.makeLowerBoundInclusive();
var decision1 = new Ego.Decision("numberOfFamilyMembers", Ego.InformationTypes.TYPE_NUMERICAL, new Ego.IsInRange(familyMemberRange));
var decision2 = new Ego.Decision("salary", Ego.InformationTypes.TYPE_NUMERICAL, new Ego.IsInRange(salaryRange1));
var decision3 = new Ego.Decision("isMarried", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var decision4 = new Ego.Decision("isMarried", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var decision5 = new Ego.Decision("salary", Ego.InformationTypes.TYPE_NUMERICAL, new Ego.IsInRange(salaryRange1));
decision1.setYesNode(decision3);
decision1.setNoNode(decision2);
decision2.setYesNode("4BHK");
decision2.setNoNode(decision4);
decision3.setYesNode("3BHK");
decision3.setNoNode(decision5);
decision4.setYesNode("3BHK");
decision4.setNoNode("2BHK");
decision5.setYesNode("2BHK");
decision5.setNoNode("1BHK");
var decisionTree = new Ego.DecisionTree(decision1);
var clone = decisionTree.clone();
expect(clone.makeDecision(knowledge)).to.eql("3BHK");
knowledge.updateBooleanInformation("isMarried", false);
expect(clone.makeDecision(knowledge)).to.eql("2BHK");
knowledge.updateNumericalInformation("salary", 20000);
expect(clone.makeDecision(knowledge)).to.eql("1BHK");
knowledge.updateNumericalInformation("numberOfFamilyMembers", 1);
knowledge.updateNumericalInformation("salary", 100000);
expect(clone.makeDecision(knowledge)).to.eql("4BHK");
knowledge.updateNumericalInformation("salary", 50000);
knowledge.updateBooleanInformation("isMarried", true);
expect(clone.makeDecision(knowledge)).to.eql("3BHK");
knowledge.updateBooleanInformation("isMarried", false);
expect(clone.makeDecision(knowledge)).to.eql("2BHK");
});
});
});
});
================================================
FILE: test/decision-tree/KnowledgeTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../build/Ego");
describe("Knowledge", function(){
it("should initialize", function(){
var knowledge = new Ego.Knowledge();
expect(knowledge._booleanMap).to.eql({});
expect(knowledge._numericalMap).to.eql({});
expect(knowledge._vectorMap).to.eql({});
});
it("should add boolean information", function(){
var knowledge = new Ego.Knowledge();
expect(knowledge.addBooleanInformation("isStuffHappening", true)).to.eql(true);
expect(knowledge.addBooleanInformation("isStuffHappening", true)).to.eql(false);
expect(knowledge.addBooleanInformation("isStuffHappening", false)).to.eql(false);
expect(knowledge.addBooleanInformation("canSee", true)).to.eql(true);
});
it("should add numerical information", function(){
var knowledge = new Ego.Knowledge();
expect(knowledge.addNumericalInformation("health", 95.7)).to.eql(true);
expect(knowledge.addNumericalInformation("health", 100)).to.eql(false);
expect(knowledge.addNumericalInformation("ammo", "xx")).to.eql(false);
expect(knowledge.addNumericalInformation("ammo", 30)).to.eql(true);
expect(knowledge.addNumericalInformation("ammo", 40)).to.eql(false);
});
it("should add vector information", function(){
var knowledge = new Ego.Knowledge();
expect(knowledge.addVectorInformation("distance")).to.eql(false);
expect(knowledge.addVectorInformation("distance", 10)).to.eql(false);
expect(knowledge.addVectorInformation("distance", 10, 20)).to.eql(false);
expect(knowledge.addVectorInformation("distance", 10, 20, 30)).to.eql(true);
expect(knowledge.addVectorInformation("distance", 40, 50, 60)).to.eql(false);
expect(knowledge.addVectorInformation("speed", 20.1, 30, 70.3)).to.eql(true);
expect(knowledge.addVectorInformation("acceleration", "x", 70, "y")).to.eql(false);
});
it("should get boolean information", function(){
var knowledge = new Ego.Knowledge();
expect(knowledge.getBooleanInformation("isStuffHappening")).to.eql(null);
knowledge.addBooleanInformation("isStuffHappening", true);
expect(knowledge.getBooleanInformation("isStuffHappening")).to.eql(true);
});
it("should get numerical information", function(){
var knowledge = new Ego.Knowledge();
expect(knowledge.getNumericalInformation("health")).to.eql(null);
knowledge.addNumericalInformation("health", 93.7);
expect(knowledge.getNumericalInformation("health")).to.eql(93.7);
});
it("should get vector information", function(){
var knowledge = new Ego.Knowledge();
expect(knowledge.getVectorInformation("velocity")).to.eql(null);
knowledge.addVectorInformation("velocity", 100.9, 200.9, 300.9);
expect(knowledge.getVectorInformation("velocity")).to.eql({
x: 100.9,
y: 200.9,
z: 300.9,
length: Math.sqrt((100.9 * 100.9) + (200.9 * 200.9) + (300.9 * 300.9)),
isVector: true
})
});
it("should update boolean information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
expect(knowledge.updateBooleanInformation("isStuffHappening", false)).to.eql(true);
expect(knowledge.getBooleanInformation("isStuffHappening")).to.eql(false);
expect(knowledge.updateBooleanInformation("doesItWorthIt", true)).to.eql(false);
});
it("should update numerical information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addNumericalInformation("health", 100);
expect(knowledge.updateNumericalInformation("health", 93.7)).to.eql(true);
expect(knowledge.getNumericalInformation("health")).to.eql(93.7);
expect(knowledge.updateNumericalInformation("health", "xyz")).to.eql(false);
expect(knowledge.getNumericalInformation("health")).to.eql(93.7);
expect(knowledge.updateNumericalInformation("power", 94.7)).to.eql(false);
});
it("should update vector information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addVectorInformation("speed", 100, 200, 300);
expect(knowledge.updateVectorInformation("speed", 100)).to.eql(false);
expect(knowledge.updateVectorInformation("speed", 100, 200)).to.eql(false);
expect(knowledge.updateVectorInformation("speed", "x", "y", "z")).to.eql(false);
expect(knowledge.updateVectorInformation("speed", 90.1, 80.2, 70.3)).to.eql(true);
expect(knowledge.getVectorInformation("speed")).to.eql({
x: 90.1,
y: 80.2,
z: 70.3,
length: Math.sqrt((90.1 * 90.1) + (80.2 * 80.2) + (70.3 * 70.3)),
isVector: true
});
expect(knowledge.updateVectorInformation("notSpeed", 100, 200, 300)).to.eql(false);
});
it("should delete boolean information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
expect(knowledge.deleteBooleanInformation("isStuffHappening")).to.eql(true);
expect(knowledge.getBooleanInformation("isStuffHappening")).to.eql(null);
expect(knowledge.deleteBooleanInformation("isStuffHappening")).to.eql(false);
});
it("should delete numerical information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addNumericalInformation("health", 100);
expect(knowledge.deleteNumericalInformation("health")).to.eql(true);
expect(knowledge.getNumericalInformation("health")).to.eql(null);
expect(knowledge.deleteNumericalInformation("health")).to.eql(false);
});
it("should delete vector information", function(){
var knowledge = new Ego.Knowledge();
knowledge.addVectorInformation("position", 100, 200, 300);
expect(knowledge.deleteVectorInformation("position")).to.eql(true);
expect(knowledge.getVectorInformation("position")).to.eql(null);
expect(knowledge.deleteVectorInformation("position")).to.eql(false);
});
it("should clone", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
knowledge.addNumericalInformation("myAge", 27);
knowledge.addVectorInformation("dist", 100, 200, 300.5);
var cloned = knowledge.clone();
expect(knowledge.getBooleanInformation("isStuffHappening")).to.eql(cloned.getBooleanInformation("isStuffHappening"));
expect(knowledge.getNumericalInformation("myAge")).to.eql(cloned.getNumericalInformation("myAge"));
expect(knowledge.getVectorInformation("dist")).to.eql(cloned.getVectorInformation("dist"));
expect(knowledge).to.eql(cloned);
expect(knowledge === cloned).to.eql(false);
})
});
================================================
FILE: test/decision-tree/decision-methods/DecisionMethodTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../../build/Ego");
describe("DecisionMethod", function(){
it("should initialize", function(){
var decisionMethod = new Ego.DecisionMethod(100);
expect(decisionMethod._parameter).to.eql(100);
});
it("should update parameter", function(){
var decisionMethod = new Ego.DecisionMethod(100);
decisionMethod.updateParameter(200);
expect(decisionMethod._parameter).to.eql(200);
});
it("should perform", function(){
var decisionMethod = new Ego.DecisionMethod(100);
expect(decisionMethod.perform()).to.eql(true);
});
it("should clone", function(){
var decisionMethod = new Ego.DecisionMethod(300);
var cloned = decisionMethod.clone();
expect(decisionMethod).to.eql(cloned);
expect(decisionMethod === cloned).to.eql(false);
var called = false;
decisionMethod = new Ego.DecisionMethod({clone: function(){
called = true;
return this;
}});
cloned = decisionMethod.clone();
expect(decisionMethod).to.eql(cloned);
expect(decisionMethod === cloned).to.eql(false);
expect(called).to.eql(true);
});
});
================================================
FILE: test/decision-tree/decision-methods/IsFalseTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../../build/Ego");
describe("IsFalse", function(){
it("should initialize", function(){
var isFalse = new Ego.IsFalse();
expect(isFalse._parameter).to.eql(null);
});
it("should perform", function(){
var isFalse = new Ego.IsFalse();
expect(isFalse.perform(false)).to.eql(true);
expect(isFalse.perform(true)).to.eql(false);
expect(isFalse.perform(123)).to.eql(false);
expect(isFalse.perform({})).to.eql(false);
});
it("should clone", function(){
var isFalse = new Ego.IsFalse();
var cloned = isFalse.clone();
expect(cloned instanceof Ego.IsFalse).to.eql(true);
expect(isFalse).to.eql(cloned);
expect(isFalse === cloned).to.eql(false);
});
});
================================================
FILE: test/decision-tree/decision-methods/IsInRangeTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../../build/Ego");
describe("IsInRange", function(){
it("should initialize", function(){
var range = new Ego.Range(10, 30);
var isInRange = new Ego.IsInRange(range);
expect(isInRange._parameter).to.equal(range);
});
it("should perform for numbers", function(){
var range = new Ego.Range(10, 30);
var isInRange = new Ego.IsInRange(range);
expect(isInRange.perform(25)).to.equal(true);
expect(isInRange.perform(45)).to.equal(false);
expect(isInRange.perform(-10)).to.equal(false);
});
it("should perform for vectors", function(){
var range = new Ego.Range(10, 30);
var vec1 = {isVector: true, length: 25};
var vec2 = {isVector: true, length: 45};
var vec3 = {isVector: true, length: -10};
var isInRange = new Ego.IsInRange(range);
expect(isInRange.perform(vec1)).to.equal(true);
expect(isInRange.perform(vec2)).to.equal(false);
expect(isInRange.perform(vec3)).to.equal(false);
});
it("should clone", function(){
var range = new Ego.Range(10, 30);
var isInRange = new Ego.IsInRange(range);
var cloned = isInRange.clone();
expect(cloned instanceof Ego.IsInRange).to.eql(true);
expect(cloned._parameter).to.eql(range);
expect(cloned._parameter === range).to.eql(false);
expect(isInRange).to.eql(cloned);
expect(isInRange === cloned).to.eql(false);
});
});
================================================
FILE: test/decision-tree/decision-methods/IsTrueTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../../build/Ego");
describe("IsTrue", function(){
it("should initialize", function(){
var isTrue = new Ego.IsTrue();
expect(isTrue._parameter).to.eql(null);
});
it("should perform", function(){
var isTrue = new Ego.IsTrue();
expect(isTrue.perform(true)).to.eql(true);
expect(isTrue.perform(false)).to.eql(false);
expect(isTrue.perform(123)).to.eql(false);
expect(isTrue.perform({})).to.eql(false);
});
it("should clone", function(){
var isTrue = new Ego.IsTrue();
var cloned = isTrue.clone();
expect(cloned instanceof Ego.IsTrue).to.eql(true);
expect(isTrue).to.eql(cloned);
expect(isTrue === cloned).to.eql(false);
});
});
================================================
FILE: test/decision-tree/math/RangeTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../../build/Ego");
describe("Range", function(){
it("should initialize", function(){
var range = new Ego.Range(20, 30);
expect(range._isUpperBoundInclusive).to.eql(true);
expect(range._isLowerBoundInclusive).to.eql(true);
expect(range._upperBound).to.eql(30);
expect(range._lowerBound).to.eql(20);
});
it("should make lower bound exclusive", function(){
var range = new Ego.Range(20, 30);
range.makeLowerBoundExclusive();
expect(range._isLowerBoundInclusive).to.eql(false);
});
it("should make lower bound inclusive", function(){
var range = new Ego.Range(20, 30);
range.makeLowerBoundExclusive();
range.makeLowerBoundInclusive();
expect(range._isLowerBoundInclusive).to.eql(true);
});
it("should make upper bound exclusive", function(){
var range = new Ego.Range(20, 30);
range.makeUpperBoundExclusive();
expect(range._isUpperBoundInclusive).to.eql(false);
});
it("should make upper bound inclusive", function(){
var range = new Ego.Range(20, 30);
range.makeUpperBoundExclusive();
range.makeUpperBoundInclusive();
expect(range._isUpperBoundInclusive).to.eql(true);
});
it("should update lower bound", function(){
var range = new Ego.Range(20, 30);
range.updateLowerBound(-100);
expect(range._lowerBound).to.eql(-100);
});
it("should update upper bound", function(){
var range = new Ego.Range(20, 30);
range.updateUpperBound(300);
expect(range._upperBound).to.eql(300);
});
it("should test number", function(){
var range = new Ego.Range(20, 30);
expect(range.testNumber(10)).to.eql(false);
expect(range.testNumber(25)).to.eql(true);
expect(range.testNumber(20)).to.eql(true);
expect(range.testNumber(30)).to.eql(true);
expect(range.testNumber(-10)).to.eql(false);
expect(range.testNumber(40)).to.eql(false);
range.makeLowerBoundExclusive();
expect(range.testNumber(20)).to.eql(false);
range.makeUpperBoundExclusive();
expect(range.testNumber(30)).to.eql(false);
range.updateLowerBound(-100);
range.updateUpperBound(300);
expect(range.testNumber(200)).to.eql(true);
expect(range.testNumber(-85)).to.eql(true);
});
it("should clone", function(){
var range1 = new Ego.Range(100, 300);
var range2 = new Ego.Range(-100, 500);
var range3 = new Ego.Range(200, 3000);
var range4 = new Ego.Range(-12.5, 13.5);
range1.makeLowerBoundExclusive();
range1.makeUpperBoundExclusive();
range2.makeLowerBoundExclusive();
range2.makeUpperBoundInclusive();
range3.makeLowerBoundInclusive();
range3.makeUpperBoundExclusive();
range4.makeLowerBoundInclusive();
range4.makeUpperBoundInclusive();
var range1Clone = range1.clone();
var range2Clone = range2.clone();
var range3Clone = range3.clone();
var range4Clone = range4.clone();
expect(range1).to.eql(range1Clone);
expect(range1 === range1Clone).to.eql(false);
expect(range2).to.eql(range2Clone);
expect(range2 === range2Clone).to.eql(false);
expect(range3).to.eql(range3Clone);
expect(range3 === range3Clone).to.eql(false);
expect(range4).to.eql(range4Clone);
expect(range4 === range4Clone).to.eql(false);
});
});
================================================
FILE: test/state-machine/StateMachineTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../build/Ego");
describe("StateMachine", function(){
it("should initialize", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
expect(stateMachine instanceof Ego.State).to.eql(true);
expect(stateMachine._knowledge).to.eql(knowledge);
expect(stateMachine._statesByID).to.eql({});
expect(stateMachine._transitionsByStateID).to.eql({});
expect(stateMachine._entryState).to.eql(null);
expect(stateMachine._currentState).to.eql(null);
expect(stateMachine._stateChangedCallbackFunction).to.eql(null);
expect(stateMachine._name).to.eql("stateMachine1");
expect(stateMachine._id).to.have.length(36);
expect(stateMachine._parent).to.eql(null);
});
it("should add state", function(){
var knowledge = new Ego.Knowledge();
var stateMachine1 = new Ego.StateMachine("stateMachine1", knowledge);
var stateMachine2 = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("idle");
var id1 = state1.getID();
var id2 = state2.getID();
var obj = {};
var obj2 = {};
obj[id1] = state1;
obj2[id1] = [];
expect(stateMachine1.addState(state1)).to.eql(true);
expect(stateMachine1._statesByID).to.eql(obj);
expect(stateMachine1._transitionsByStateID).to.eql(obj2);
obj[id2] = state2;
obj2[id2] = [];
expect(stateMachine1.addState(state2)).to.eql(true);
expect(stateMachine1._statesByID).to.eql(obj);
expect(stateMachine1._transitionsByStateID).to.eql(obj2);
expect(stateMachine1.addState(state1)).to.eql(false);
expect(stateMachine1.addState(state2)).to.eql(false);
expect(stateMachine1._statesByID).to.eql(obj);
expect(stateMachine1._transitionsByStateID).to.eql(obj2);
expect(stateMachine2.addState(state1)).to.eql(false);
expect(stateMachine2.addState(state2)).to.eql(false);
expect(stateMachine2._statesByID).to.eql({});
expect(stateMachine2._transitionsByStateID).to.eql({});
});
it("should remove state", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state = new Ego.State("idle");
expect(stateMachine.removeState(state)).to.eql(false);
stateMachine.addState(state);
expect(stateMachine.removeState(state)).to.eql(true);
expect(stateMachine._statesByID).to.eql({});
expect(stateMachine._transitionsByStateID).to.eql({});
});
it("should not add state if the added state contains the state", function(){
var knowledge = new Ego.Knowledge();
var sm1 = new Ego.StateMachine("sm1", knowledge);
var sm2 = new Ego.StateMachine("sm2", knowledge);
var sm3 = new Ego.StateMachine("sm3", knowledge);
sm2.addState(sm1);
expect(sm1.addState(sm2)).to.eql(false);
sm1.addState(sm3);
expect(sm3.addState(sm2)).to.eql(false);
})
it("should check if has state", function(){
var knowledge = new Ego.Knowledge();
var sm1 = new Ego.StateMachine("sm1", knowledge);
var sm2 = new Ego.StateMachine("sm2", knowledge);
var state1 = new Ego.StateMachine("state1");
var state2 = new Ego.StateMachine("state2");
expect(sm1.hasState(sm1)).to.eql(true);
expect(sm1.hasState(state1)).to.eql(false);
expect(sm1.hasState(sm2)).to.eql(false);
sm1.addState(state1);
expect(sm1.hasState(state1)).to.eql(true);
sm1.addState(sm2);
expect(sm1.hasState(sm2)).to.eql(true);
sm2.addState(state2);
expect(sm2.hasState(state2)).to.eql(true);
expect(sm1.hasState(state2)).to.eql(true);
var sm3 = new Ego.StateMachine("sm3", knowledge);
var state3 = new Ego.State("state3");
sm3.addState(state3);
sm2.addState(sm3);
expect(sm1.hasState(state3)).to.eql(true);
});
it("should add transition", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("moving");
var state3 = new Ego.State("jumping");
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var transition2 = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var transition3 = new Ego.Transition(state1, state3, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
expect(stateMachine.addTransition(transition)).to.eql(false);
stateMachine.addState(state1);
var obj = {};
obj[state1.getID()] = [transition];
expect(stateMachine.addTransition(transition)).to.eql(true);
expect(stateMachine._transitionsByStateID).to.eql(obj);
expect(stateMachine.addTransition(transition)).to.eql(false);
expect(stateMachine._transitionsByStateID).to.eql(obj);
expect(stateMachine.addTransition(transition2)).to.eql(false);
expect(stateMachine._transitionsByStateID).to.eql(obj);
obj[state1.getID()].push(transition3);
expect(stateMachine.addTransition(transition3)).to.eql(true);
expect(stateMachine._transitionsByStateID).to.eql(obj);
});
it("should remove transition", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("moving");
var state3 = new Ego.State("jumping");
var transition1 = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var transition2 = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var transition3 = new Ego.Transition(state1, state3, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
stateMachine.addState(state1);
expect(stateMachine.removeTransition(transition1)).to.eql(false);
expect(stateMachine.removeTransition(transition2)).to.eql(false);
expect(stateMachine.removeTransition(transition3)).to.eql(false);
stateMachine.addTransition(transition1);
stateMachine.addTransition(transition3);
var obj = {};
obj[state1.getID()] = [transition3];
expect(stateMachine.removeTransition(transition1)).to.eql(true);
expect(stateMachine._transitionsByStateID).to.eql(obj);
stateMachine.addTransition(transition1);
obj = {};
obj[state1.getID()] = [transition1];
expect(stateMachine.removeTransition(transition3)).to.eql(true);
expect(stateMachine._transitionsByStateID).to.eql(obj);
obj = {};
obj[state1.getID()] = [];
expect(stateMachine.removeTransition(transition1)).to.eql(true);
expect(stateMachine._transitionsByStateID).to.eql(obj);
});
it("should set entry state", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("moving");
expect(stateMachine.setEntryState(state1)).to.eql(false);
expect(stateMachine._entryState).to.eql(null);
stateMachine.addState(state1);
expect(stateMachine.setEntryState(state1)).to.eql(true);
expect(stateMachine._entryState).to.eql(state1);
});
it("should unset entry state if the removed state is the entry state", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state = new Ego.State("idle");
stateMachine.addState(state);
stateMachine.setEntryState(state);
expect(stateMachine._entryState).to.eql(state);
stateMachine.removeState(state);
expect(stateMachine._entryState).to.eql(null);
});
it("should set state change callback function", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var fn = function(newState){
console.log("State changed: ", newState);
};
stateMachine.onStateChanged(fn);
expect(stateMachine._stateChangedCallbackFunction).to.eql(fn);
});
it("should throw an error if updating without an entry state", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var catched = null;
try{
stateMachine.update();
}catch (err){
catched = err;
}
expect(catched).not.to.eql(null);
expect(catched.message).to.eql("Entry state not set. Cannot update the StateMachine.");
});
it("should update", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", false);
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("moving");
stateMachine.addState(state1);
stateMachine.addState(state2);
stateMachine.addTransition(new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
stateMachine.setEntryState(state1);
expect(stateMachine._currentState).to.eql(null);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state1);
knowledge.updateBooleanInformation("isStuffHappening", true);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state2);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state2);
});
it("should invoke state change callback function", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("moving");
stateMachine.addState(state1);
stateMachine.addState(state2);
stateMachine.addTransition(new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
stateMachine.setEntryState(state1);
var param = null;
stateMachine.onStateChanged(function(newState){
param = newState;
});
stateMachine.update();
expect(param).to.eql(state2);
});
it("should reset", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("moving");
stateMachine.addState(state1);
stateMachine.addState(state2);
stateMachine.addTransition(new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
stateMachine.setEntryState(state1);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state2);
stateMachine.reset();
expect(stateMachine._currentState).to.eql(null);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state2);
stateMachine.reset();
knowledge.updateBooleanInformation("isStuffHappening", false);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state1);
});
it("should update recursively", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
knowledge.addBooleanInformation("shouldJump", true);
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("moving");
var state3 = new Ego.State("jumping");
stateMachine.addState(state1);
stateMachine.addState(state2);
stateMachine.addState(state3);
stateMachine.addTransition(new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
stateMachine.addTransition(new Ego.Transition(state2, state3, "shouldJump", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
stateMachine.setEntryState(state1);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state3);
stateMachine.reset();
knowledge.updateBooleanInformation("shouldJump", false);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state2);
});
it("should invoke state change callback function on recursive updates", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
knowledge.addBooleanInformation("shouldJump", true);
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state1 = new Ego.State("idle");
var state2 = new Ego.State("moving");
var state3 = new Ego.State("jumping");
stateMachine.addState(state1);
stateMachine.addState(state2);
stateMachine.addState(state3);
stateMachine.addTransition(new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
stateMachine.addTransition(new Ego.Transition(state2, state3, "shouldJump", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
stateMachine.setEntryState(state1);
var currentStates = [];
stateMachine.onStateChanged(function(newState){
currentStates.push(newState);
});
stateMachine.update();
expect(currentStates).to.eql([state1, state2, state3]);
});
it("should update hierarchically", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("trashSeen", false);
knowledge.addBooleanInformation("itemGot", false);
knowledge.addBooleanInformation("trashDisposed", false);
knowledge.addBooleanInformation("noPower", false);
knowledge.addBooleanInformation("recharged", false);
var decisionMethod = new Ego.IsTrue();
var stateMachine = new Ego.StateMachine("stateMachine", knowledge);
var stateMachine2 = new Ego.StateMachine("cleanUp", knowledge);
var state1 = new Ego.State("search");
var state2 = new Ego.State("headForTrash");
var state3 = new Ego.State("headForCompactor");
var state4 = new Ego.State("getPower");
stateMachine2.addState(state1);
stateMachine2.addState(state2);
stateMachine2.addState(state3);
stateMachine.addState(stateMachine2);
stateMachine.addState(state4);
var transition1 = new Ego.Transition(state1, state2, "trashSeen", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var transition2 = new Ego.Transition(state2, state3, "itemGot", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var transition3 = new Ego.Transition(state3, state1, "trashDisposed", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
expect(stateMachine2.addTransition(transition1)).to.eql(true);
expect(stateMachine2.addTransition(transition2)).to.eql(true);
expect(stateMachine2.addTransition(transition3)).to.eql(true);
var transition4 = new Ego.Transition(stateMachine2, state4, "noPower", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var transition5 = new Ego.Transition(state4, stateMachine2, "recharged", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
expect(stateMachine.addTransition(transition4)).to.eql(true);
expect(stateMachine.addTransition(transition5)).to.eql(true);
expect(stateMachine.setEntryState(stateMachine2)).to.eql(true);
expect(stateMachine2.setEntryState(state1)).to.eql(true);
expect(stateMachine._currentState).to.eql(null);
expect(stateMachine2._currentState).to.eql(null);
stateMachine.update();
expect(stateMachine._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state1);
knowledge.updateBooleanInformation("trashSeen", true);
stateMachine.update();
expect(stateMachine._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state2);
knowledge.updateBooleanInformation("trashSeen", false);
knowledge.updateBooleanInformation("itemGot", true);
stateMachine.update();
expect(stateMachine._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state3);
knowledge.updateBooleanInformation("itemGot", false);
knowledge.updateBooleanInformation("trashDisposed", true);
stateMachine.update();
expect(stateMachine._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state1);
knowledge.updateBooleanInformation("trashDisposed", false);
knowledge.updateBooleanInformation("trashSeen", true);
knowledge.updateBooleanInformation("itemGot", true);
stateMachine.update();
expect(stateMachine._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state3);
knowledge.updateBooleanInformation("noPower", true);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state4);
expect(stateMachine2._currentState).to.eql(null);
knowledge.updateBooleanInformation("trashSeen", true);
knowledge.updateBooleanInformation("itemGot", true);
knowledge.updateBooleanInformation("trashDisposed", false);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state4);
expect(stateMachine2._currentState).to.eql(null);
knowledge.updateBooleanInformation("noPower", false);
knowledge.updateBooleanInformation("recharged", true);
stateMachine.update();
expect(stateMachine._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state3);
});
it("should not invoke state change callback function on cross-hierarchy transitions", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("seenTrash", false);
var childStateMachine = new Ego.StateMachine("childStateMachine", knowledge);
var parentStateMachine = new Ego.StateMachine("parentStateMachine", knowledge);
var state1 = new Ego.State("search");
var state2 = new Ego.State("headForTrash");
var state3 = new Ego.State("getPower");
childStateMachine.addState(state1);
childStateMachine.addState(state2);
childStateMachine.setEntryState(state1);
parentStateMachine.addState(childStateMachine);
parentStateMachine.addState(state3);
parentStateMachine.setEntryState(childStateMachine);
childStateMachine.addTransition(new Ego.Transition(state1, state2, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
childStateMachine.addTransition(new Ego.Transition(state1, state3, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsFalse()));
var callCount = 0;
var lastState = null;
childStateMachine.onStateChanged(function(newState){
callCount ++;
lastState = newState;
});
parentStateMachine.update();
expect(callCount).to.eql(1);
expect(lastState).to.eql(state1);
});
it("should update the current node of the other state machine on cross-hierarchy transitions", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("seenTrash", false);
var childStateMachine = new Ego.StateMachine("childStateMachine", knowledge);
var parentStateMachine = new Ego.StateMachine("parentStateMachine", knowledge);
var state1 = new Ego.State("search");
var state2 = new Ego.State("headForTrash");
var state3 = new Ego.State("getPower");
childStateMachine.addState(state1);
childStateMachine.addState(state2);
childStateMachine.setEntryState(state1);
parentStateMachine.addState(childStateMachine);
parentStateMachine.addState(state3);
parentStateMachine.setEntryState(childStateMachine);
childStateMachine.addTransition(new Ego.Transition(state1, state2, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
childStateMachine.addTransition(new Ego.Transition(state1, state3, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsFalse()));
var states = [];
parentStateMachine.onStateChanged(function(newState){
states.push(newState);
});
parentStateMachine.update();
expect(parentStateMachine._currentState).to.eql(state3);
expect(states).to.eql([childStateMachine, state3]);
});
it("should reset on cross-hierarchy transitions", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("seenTrash", false);
var childStateMachine = new Ego.StateMachine("childStateMachine", knowledge);
var parentStateMachine = new Ego.StateMachine("parentStateMachine", knowledge);
var state1 = new Ego.State("search");
var state2 = new Ego.State("headForTrash");
var state3 = new Ego.State("getPower");
childStateMachine.addState(state1);
childStateMachine.addState(state2);
childStateMachine.setEntryState(state1);
parentStateMachine.addState(childStateMachine);
parentStateMachine.addState(state3);
parentStateMachine.setEntryState(childStateMachine);
childStateMachine.addTransition(new Ego.Transition(state1, state2, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
childStateMachine.addTransition(new Ego.Transition(state1, state3, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsFalse()));
var stateChanged = false;
childStateMachine.onStateChanged(function(state){
stateChanged = true;
});
parentStateMachine.update();
expect(stateChanged).to.eql(true);
expect(childStateMachine._currentState).to.eql(null);
});
it("should keep updating the switched state machine after cross-hierarchy transition", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("seenTrash", false);
knowledge.addBooleanInformation("isStuffHappening", true);
var childStateMachine = new Ego.StateMachine("childStateMachine", knowledge);
var parentStateMachine = new Ego.StateMachine("parentStateMachine", knowledge);
var state1 = new Ego.State("search");
var state2 = new Ego.State("headForTrash");
var state3 = new Ego.State("getPower");
var state4 = new Ego.State("doStuff");
childStateMachine.addState(state1);
childStateMachine.addState(state2);
childStateMachine.setEntryState(state1);
parentStateMachine.addState(childStateMachine);
parentStateMachine.addState(state3);
parentStateMachine.addState(state4);
parentStateMachine.setEntryState(childStateMachine);
childStateMachine.addTransition(new Ego.Transition(state1, state2, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
childStateMachine.addTransition(new Ego.Transition(state1, state3, "seenTrash", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsFalse()));
parentStateMachine.addTransition(new Ego.Transition(state3, state4, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue()));
parentStateMachine.update();
expect(parentStateMachine._currentState).to.eql(state4);
});
it("should throw an error if trying to remove the active state", function(){
var knowledge = new Ego.Knowledge();
var stateMachine = new Ego.StateMachine("stateMachine1", knowledge);
var state = new Ego.State("state1");
stateMachine.addState(state);
stateMachine.setEntryState(state);
stateMachine.update();
expect(stateMachine._currentState).to.eql(state);
var thrownError = null
try{
stateMachine.removeState(state);
}catch (err){
thrownError = err;
}
expect(thrownError).not.to.eql(null);
expect(thrownError.message).to.eql("Cannot remove the active state.");
});
it("should not crash if target node has no parent", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var stateMachine = new Ego.StateMachine("sm1", knowledge);
stateMachine.addState(state1);
stateMachine.setEntryState(state1);
stateMachine.addTransition(transition);
var stateChangeCount = 0;
stateMachine.onStateChanged(function(state){
stateChangeCount++;
});
stateMachine.update();
expect(stateChangeCount).to.eql(1);
expect(stateMachine._currentState).to.eql(null);
});
it("should clone", function(){
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", false);
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var stateMachine = new Ego.StateMachine("sm1", knowledge);
stateMachine.addState(state1);
stateMachine.addState(state2);
stateMachine.addTransition(transition);
stateMachine.setEntryState(state1);
var cloned = stateMachine.clone();
expect(stateMachine.getName()).to.eql(cloned.getName());
expect(stateMachine._knowledge === cloned._knowledge).to.eql(false);
cloned.update();
expect(cloned._currentState.getName()).to.eql("state1");
cloned._knowledge.updateBooleanInformation("isStuffHappening", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql("state2");
cloned = stateMachine.clone(knowledge);
expect(stateMachine._knowledge === cloned._knowledge).to.eql(true);
});
describe("integration", function(){
it("case#1", function(){
var isTrue = new Ego.IsTrue();
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("info1", false);
knowledge.addBooleanInformation("info2", false);
knowledge.addBooleanInformation("info3", false);
knowledge.addBooleanInformation("info4", false);
knowledge.addBooleanInformation("info5", false);
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var state3 = new Ego.State("state3");
var state4 = new Ego.State("state4");
var state5 = new Ego.State("state5");
var stateMachine1 = new Ego.StateMachine("stateMachine1", knowledge);
var stateMachine2 = new Ego.StateMachine("stateMachine2", knowledge);
var stateMachine3 = new Ego.StateMachine("stateMachine3", knowledge);
stateMachine1.addState(stateMachine2);
stateMachine1.addState(state3);
stateMachine1.addTransition(new Ego.Transition(stateMachine2, state3, "info2", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine1.setEntryState(stateMachine2);
stateMachine2.addState(state1);
stateMachine2.addState(state2);
stateMachine2.addTransition(new Ego.Transition(state1, state2, "info1", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine2.addTransition(new Ego.Transition(state2, state4, "info3", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine2.setEntryState(state1);
stateMachine3.addState(stateMachine1);
stateMachine3.addState(state4);
stateMachine3.addState(state5);
stateMachine3.addTransition(new Ego.Transition(state4, state5, "info4", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine3.addTransition(new Ego.Transition(state5, stateMachine1, "info5", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine3.setEntryState(stateMachine1);
stateMachine3.update();
expect(stateMachine3._currentState).to.eql(stateMachine1);
expect(stateMachine1._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state1);
knowledge.updateBooleanInformation("info1", true);
stateMachine3.update();
expect(stateMachine3._currentState).to.eql(stateMachine1);
expect(stateMachine1._currentState).to.eql(stateMachine2);
expect(stateMachine2._currentState).to.eql(state2);
knowledge.updateBooleanInformation("info3", true);
stateMachine3.update();
expect(stateMachine3._currentState).to.eql(state4);
expect(stateMachine1._currentState).to.eql(null);
expect(stateMachine2._currentState).to.eql(null);
knowledge.updateBooleanInformation("info4", true);
stateMachine3.update();
expect(stateMachine3._currentState).to.eql(state5);
expect(stateMachine1._currentState).to.eql(null);
expect(stateMachine2._currentState).to.eql(null);
knowledge.updateBooleanInformation("info5", true);
knowledge.updateBooleanInformation("info2", true);
stateMachine3.update();
expect(stateMachine3._currentState).to.eql(stateMachine1);
expect(stateMachine1._currentState).to.eql(state3);
expect(stateMachine2._currentState).to.eql(null);
});
it("case#2", function(){
var isTrue = new Ego.IsTrue();
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("info1", false);
knowledge.addBooleanInformation("info2", false);
knowledge.addBooleanInformation("info3", false);
knowledge.addBooleanInformation("info4", false);
knowledge.addBooleanInformation("info5", false);
knowledge.addBooleanInformation("info6", false);
knowledge.addBooleanInformation("info7", false);
knowledge.addBooleanInformation("info8", false);
knowledge.addBooleanInformation("info9", false);
knowledge.addBooleanInformation("info10", false);
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var state3 = new Ego.State("state3");
var state4 = new Ego.State("state4");
var state5 = new Ego.State("state5");
var state6 = new Ego.State("state6");
var state7 = new Ego.State("state7");
var sm1 = new Ego.StateMachine("sm1", knowledge);
var sm2 = new Ego.StateMachine("sm2", knowledge);
var sm3 = new Ego.StateMachine("sm3", knowledge);
var transition1 = new Ego.Transition(state1, state2, "info1", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition2 = new Ego.Transition(state2, state4, "info2", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition3 = new Ego.Transition(state2, state3, "info3", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition4 = new Ego.Transition(state3, sm2, "info4", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition5 = new Ego.Transition(state6, state7, "info5", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition6 = new Ego.Transition(state7, state4, "info6", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition7 = new Ego.Transition(state4, state5, "info7", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition8 = new Ego.Transition(state5, sm1, "info8", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition9 = new Ego.Transition(sm1, sm2, "info9", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition10 = new Ego.Transition(sm2, sm1, "info10", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
sm1.addState(state1);
sm1.addState(state2);
sm1.addState(state3);
sm1.addTransition(transition1);
sm1.addTransition(transition2);
sm1.addTransition(transition3);
sm1.addTransition(transition4);
sm1.setEntryState(state1);
sm2.addState(state6);
sm2.addState(state7);
sm2.addTransition(transition5);
sm2.addTransition(transition6);
sm2.setEntryState(state6);
sm3.addState(sm1);
sm3.addState(sm2);
sm3.addState(state4);
sm3.addState(state5);
sm3.addTransition(transition9);
sm3.addTransition(transition10);
sm3.addTransition(transition7);
sm3.addTransition(transition8);
sm3.setEntryState(sm1);
sm3.update();
expect(sm3._currentState).to.eql(sm1);
expect(sm1._currentState).to.eql(state1);
expect(sm2._currentState).to.eql(null);
knowledge.updateBooleanInformation("info9", true);
sm3.update();
expect(sm3._currentState).to.eql(sm2);
expect(sm1._currentState).to.eql(null);
expect(sm2._currentState).to.eql(state6);
knowledge.updateBooleanInformation("info9", false);
knowledge.updateBooleanInformation("info10", true);
sm3.update();
expect(sm3._currentState).to.eql(sm1);
expect(sm1._currentState).to.eql(state1);
expect(sm2._currentState).to.eql(null);
knowledge.updateBooleanInformation("info10", false);
knowledge.updateBooleanInformation("info9", true);
knowledge.updateBooleanInformation("info5", true);
knowledge.updateBooleanInformation("info6", true);
knowledge.updateBooleanInformation("info7", true);
sm3.update();
expect(sm3._currentState).to.eql(state5);
expect(sm1._currentState).to.eql(null);
expect(sm2._currentState).to.eql(null);
knowledge.updateBooleanInformation("info7", false);
knowledge.updateBooleanInformation("info8", true);
knowledge.updateBooleanInformation("info1", true);
knowledge.updateBooleanInformation("info2", true);
sm3.update();
expect(sm3._currentState).to.eql(state4);
expect(sm1._currentState).to.eql(null);
expect(sm2._currentState).to.eql(null);
knowledge.updateBooleanInformation("info1", false);
knowledge.updateBooleanInformation("info2", false);
knowledge.updateBooleanInformation("info3", false);
knowledge.updateBooleanInformation("info4", false);
knowledge.updateBooleanInformation("info5", true);
knowledge.updateBooleanInformation("info6", false);
knowledge.updateBooleanInformation("info7", true);
knowledge.updateBooleanInformation("info8", true);
knowledge.updateBooleanInformation("info9", true);
knowledge.updateBooleanInformation("info10", false);
sm3.update();
expect(sm3._currentState).to.eql(sm2);
expect(sm1._currentState).to.eql(null);
expect(sm2._currentState).to.eql(state7);
});
it("clone#1", function(){
var isTrue = new Ego.IsTrue();
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("info1", false);
knowledge.addBooleanInformation("info2", false);
knowledge.addBooleanInformation("info3", false);
knowledge.addBooleanInformation("info4", false);
knowledge.addBooleanInformation("info5", false);
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var state3 = new Ego.State("state3");
var state4 = new Ego.State("state4");
var state5 = new Ego.State("state5");
var stateMachine1 = new Ego.StateMachine("stateMachine1", knowledge);
var stateMachine2 = new Ego.StateMachine("stateMachine2", knowledge);
var stateMachine3 = new Ego.StateMachine("stateMachine3", knowledge);
stateMachine1.addState(stateMachine2);
stateMachine1.addState(state3);
stateMachine1.addTransition(new Ego.Transition(stateMachine2, state3, "info2", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine1.setEntryState(stateMachine2);
stateMachine2.addState(state1);
stateMachine2.addState(state2);
stateMachine2.addTransition(new Ego.Transition(state1, state2, "info1", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine2.addTransition(new Ego.Transition(state2, state4, "info3", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine2.setEntryState(state1);
stateMachine3.addState(stateMachine1);
stateMachine3.addState(state4);
stateMachine3.addState(state5);
stateMachine3.addTransition(new Ego.Transition(state4, state5, "info4", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine3.addTransition(new Ego.Transition(state5, stateMachine1, "info5", Ego.InformationTypes.TYPE_BOOLEAN, isTrue));
stateMachine3.setEntryState(stateMachine1);
var cloned = stateMachine3.clone(knowledge);
cloned.update();
expect(cloned._currentState.getName()).to.eql(stateMachine1.getName());
knowledge.updateBooleanInformation("info1", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(stateMachine1.getName());
knowledge.updateBooleanInformation("info3", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(state4.getName());
knowledge.updateBooleanInformation("info4", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(state5.getName());
knowledge.updateBooleanInformation("info5", true);
knowledge.updateBooleanInformation("info2", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(stateMachine1.getName());
});
it("clone#2", function(){
var isTrue = new Ego.IsTrue();
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("info1", false);
knowledge.addBooleanInformation("info2", false);
knowledge.addBooleanInformation("info3", false);
knowledge.addBooleanInformation("info4", false);
knowledge.addBooleanInformation("info5", false);
knowledge.addBooleanInformation("info6", false);
knowledge.addBooleanInformation("info7", false);
knowledge.addBooleanInformation("info8", false);
knowledge.addBooleanInformation("info9", false);
knowledge.addBooleanInformation("info10", false);
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var state3 = new Ego.State("state3");
var state4 = new Ego.State("state4");
var state5 = new Ego.State("state5");
var state6 = new Ego.State("state6");
var state7 = new Ego.State("state7");
var sm1 = new Ego.StateMachine("sm1", knowledge);
var sm2 = new Ego.StateMachine("sm2", knowledge);
var sm3 = new Ego.StateMachine("sm3", knowledge);
var transition1 = new Ego.Transition(state1, state2, "info1", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition2 = new Ego.Transition(state2, state4, "info2", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition3 = new Ego.Transition(state2, state3, "info3", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition4 = new Ego.Transition(state3, sm2, "info4", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition5 = new Ego.Transition(state6, state7, "info5", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition6 = new Ego.Transition(state7, state4, "info6", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition7 = new Ego.Transition(state4, state5, "info7", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition8 = new Ego.Transition(state5, sm1, "info8", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition9 = new Ego.Transition(sm1, sm2, "info9", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
var transition10 = new Ego.Transition(sm2, sm1, "info10", Ego.InformationTypes.TYPE_BOOLEAN, isTrue);
sm1.addState(state1);
sm1.addState(state2);
sm1.addState(state3);
sm1.addTransition(transition1);
sm1.addTransition(transition2);
sm1.addTransition(transition3);
sm1.addTransition(transition4);
sm1.setEntryState(state1);
sm2.addState(state6);
sm2.addState(state7);
sm2.addTransition(transition5);
sm2.addTransition(transition6);
sm2.setEntryState(state6);
sm3.addState(sm1);
sm3.addState(sm2);
sm3.addState(state4);
sm3.addState(state5);
sm3.addTransition(transition9);
sm3.addTransition(transition10);
sm3.addTransition(transition7);
sm3.addTransition(transition8);
sm3.setEntryState(sm1);
var cloned = sm3.clone(knowledge);
cloned.update();
expect(cloned._currentState.getName()).to.eql(sm1.getName());
knowledge.updateBooleanInformation("info9", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(sm2.getName());
knowledge.updateBooleanInformation("info9", false);
knowledge.updateBooleanInformation("info10", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(sm1.getName());
knowledge.updateBooleanInformation("info10", false);
knowledge.updateBooleanInformation("info9", true);
knowledge.updateBooleanInformation("info5", true);
knowledge.updateBooleanInformation("info6", true);
knowledge.updateBooleanInformation("info7", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(state5.getName());
knowledge.updateBooleanInformation("info7", false);
knowledge.updateBooleanInformation("info8", true);
knowledge.updateBooleanInformation("info1", true);
knowledge.updateBooleanInformation("info2", true);
cloned.update();
expect(cloned._currentState.getName()).to.eql(state4.getName());
knowledge.updateBooleanInformation("info1", false);
knowledge.updateBooleanInformation("info2", false);
knowledge.updateBooleanInformation("info3", false);
knowledge.updateBooleanInformation("info4", false);
knowledge.updateBooleanInformation("info5", true);
knowledge.updateBooleanInformation("info6", false);
knowledge.updateBooleanInformation("info7", true);
knowledge.updateBooleanInformation("info8", true);
knowledge.updateBooleanInformation("info9", true);
knowledge.updateBooleanInformation("info10", false);
cloned.update();
expect(cloned._currentState.getName()).to.eql(sm2.getName());
});
});
});
================================================
FILE: test/state-machine/StateTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../build/Ego");
describe("State", function(){
it("should initialize", function(){
var state = new Ego.State("idle");
expect(state._name).to.eql("idle");
expect(state._id).to.have.length(36);
expect(state._parent).to.eql(null);
});
it("should set parent", function(){
var state = new Ego.State("idle");
var parent = new Ego.State("parent");
expect(state.setParent(parent)).to.eql(true);
expect(state._parent).to.eql(parent);
var parent2 = new Ego.State("parent2");
expect(state.setParent(parent2)).to.eql(false);
expect(state._parent).to.eql(parent);
});
it("should get parent", function(){
var state = new Ego.State("idle");
var parent = new Ego.State("parent");
state.setParent(parent);
expect(state.getParent()).to.eql(parent);
});
it("should removeParent", function(){
var state = new Ego.State("idle");
var parent = new Ego.State("parent");
state.setParent(parent);
state.removeParent();
expect(state.getParent()).to.eql(null);
});
it("should get ID", function(){
var state = new Ego.State("idle");
expect(state.getID()).to.eql(state._id);
expect(state.getID()).to.have.length(36);
});
it("should get name", function(){
var state = new Ego.State("testState");
expect(state.getName()).to.eql("testState");
});
it("should clone", function(){
var state = new Ego.State("state1");
var cloned = state.clone();
expect(state.getName()).to.eql(cloned.getName());
expect(state === cloned).to.eql(false);
})
});
================================================
FILE: test/state-machine/TransitionTest.js
================================================
var expect = require('expect.js');
var Ego = require("../../build/Ego");
describe("Transition", function(){
it("should initialize", function(){
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var decisionMethod = new Ego.IsTrue();
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
expect(transition instanceof Ego.Decision).to.eql(true);
expect(transition._informationName).to.eql("isStuffHappening");
expect(transition._informationType).to.eql(Ego.InformationTypes.TYPE_BOOLEAN);
expect(transition._decisionMethod).to.eql(decisionMethod);
expect(transition._yesNode).to.eql(state2);
expect(transition._noNode).to.eql(state1);
});
it("should get source node", function(){
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var decisionMethod = new Ego.IsTrue();
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
expect(transition.getSourceNode()).to.eql(state1);
});
it("should get target node", function(){
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var decisionMethod = new Ego.IsTrue();
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
expect(transition.getTargetNode()).to.eql(state2);
});
it("should check if possible", function(){
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var decisionMethod = new Ego.IsTrue();
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, decisionMethod);
var knowledge = new Ego.Knowledge();
knowledge.addBooleanInformation("isStuffHappening", true);
expect(transition.isPossible(knowledge)).to.eql(true);
knowledge.updateBooleanInformation("isStuffHappening", false);
expect(transition.isPossible(knowledge)).to.eql(false);
});
it("should clone", function(){
var state1 = new Ego.State("state1");
var state2 = new Ego.State("state2");
var transition = new Ego.Transition(state1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var cloned = transition.clone();
expect(cloned instanceof Ego.Transition).to.eql(true);
expect(cloned._informationName).to.eql(transition._informationName);
expect(cloned._informationType).to.eql(transition._informationType);
expect(cloned._decisionMethod).to.eql(transition._decisionMethod);
expect(cloned.getYesNode().getName()).to.eql(transition.getYesNode().getName());
expect(cloned.getNoNode().getName()).to.eql(transition.getNoNode().getName());
expect(cloned.getYesNode() === transition.getYesNode()).to.eql(false);
expect(cloned.getNoNode() === transition.getNoNode()).to.eql(false);
cloned = transition.clone(state1);
expect(cloned._informationName).to.eql(transition._informationName);
expect(cloned._informationType).to.eql(transition._informationType);
expect(cloned._decisionMethod).to.eql(transition._decisionMethod);
expect(cloned.getYesNode().getName()).to.eql(transition.getYesNode().getName());
expect(cloned.getNoNode().getName()).to.eql(transition.getNoNode().getName());
expect(cloned.getYesNode() === transition.getYesNode()).to.eql(false);
expect(cloned.getNoNode() === transition.getNoNode()).to.eql(true);
cloned = transition.clone(null, state2);
expect(cloned._informationName).to.eql(transition._informationName);
expect(cloned._informationType).to.eql(transition._informationType);
expect(cloned._decisionMethod).to.eql(transition._decisionMethod);
expect(cloned.getYesNode().getName()).to.eql(transition.getYesNode().getName());
expect(cloned.getNoNode().getName()).to.eql(transition.getNoNode().getName());
expect(cloned.getYesNode() === transition.getYesNode()).to.eql(true);
expect(cloned.getNoNode() === transition.getNoNode()).to.eql(false);
cloned = transition.clone(state1, state2);
expect(cloned._informationName).to.eql(transition._informationName);
expect(cloned._informationType).to.eql(transition._informationType);
expect(cloned._decisionMethod).to.eql(transition._decisionMethod);
expect(cloned.getYesNode().getName()).to.eql(transition.getYesNode().getName());
expect(cloned.getNoNode().getName()).to.eql(transition.getNoNode().getName());
expect(cloned.getYesNode() === transition.getYesNode()).to.eql(true);
expect(cloned.getNoNode() === transition.getNoNode()).to.eql(true);
var sm1 = new Ego.StateMachine("sm1", new Ego.Knowledge())
var tmpState = new Ego.State("tmpState");
sm1.addState(tmpState);
sm1.setEntryState(tmpState)
var transition2 = new Ego.Transition(sm1, state2, "isStuffHappening", Ego.InformationTypes.TYPE_BOOLEAN, new Ego.IsTrue());
var newKnowledge = new Ego.Knowledge();
var obj = {};
cloned = transition2.clone(null, null, newKnowledge, obj);
expect(cloned.getSourceNode()._knowledge === newKnowledge).to.eql(true);
expect(Object.keys(obj).length).to.eql(1);
expect(cloned.getSourceNode()._entryState.getName()).to.eql("tmpState");
});
});
gitextract_frxhfo50/
├── .babelrc
├── .gitignore
├── LICENSE
├── README.md
├── js/
│ ├── Ego.js
│ ├── decision-tree/
│ │ ├── Decision.js
│ │ ├── DecisionTree.js
│ │ ├── Knowledge.js
│ │ ├── decision-methods/
│ │ │ ├── DecisionMethod.js
│ │ │ ├── IsFalse.js
│ │ │ ├── IsInRange.js
│ │ │ └── IsTrue.js
│ │ └── math/
│ │ └── Range.js
│ ├── state-machine/
│ │ ├── State.js
│ │ ├── StateMachine.js
│ │ └── Transition.js
│ └── util/
│ └── UUIDV4.js
├── package.json
├── rollup.config.js
└── test/
├── decision-tree/
│ ├── DecisionTest.js
│ ├── DecisionTreeTest.js
│ ├── KnowledgeTest.js
│ ├── decision-methods/
│ │ ├── DecisionMethodTest.js
│ │ ├── IsFalseTest.js
│ │ ├── IsInRangeTest.js
│ │ └── IsTrueTest.js
│ └── math/
│ └── RangeTest.js
└── state-machine/
├── StateMachineTest.js
├── StateTest.js
└── TransitionTest.js
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (126K chars).
[
{
"path": ".babelrc",
"chars": 30,
"preview": "{\n \"presets\": [ \"es2015\" ]\n}\n"
},
{
"path": ".gitignore",
"chars": 29,
"preview": "build\nnode_modules\n.DS_Store\n"
},
{
"path": "LICENSE",
"chars": 1068,
"preview": "MIT License\n\nCopyright (c) 2020 Oğuz Eroğlu\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "README.md",
"chars": 17096,
"preview": "# Ego\n\nEgo is a lightweight decision making library for game AI. It provides decision trees and state machines (hierarch"
},
{
"path": "js/Ego.js",
"chars": 692,
"preview": "export { Knowledge } from \"./decision-tree/Knowledge\";\nexport { DecisionMethod } from \"./decision-tree/decision-methods/"
},
{
"path": "js/decision-tree/Decision.js",
"chars": 2028,
"preview": "var InformationTypes = {\n TYPE_BOOLEAN: \"TYPE_BOOLEAN\",\n TYPE_NUMERICAL: \"TYPE_NUMERICAL\",\n TYPE_VECTOR: \"TYPE_VECTOR"
},
{
"path": "js/decision-tree/DecisionTree.js",
"chars": 564,
"preview": "import { Decision } from \"./Decision\";\n\nvar DecisionTree = function(rootNode){\n if (!(rootNode instanceof Decision)){\n "
},
{
"path": "js/decision-tree/Knowledge.js",
"chars": 4141,
"preview": "var Knowledge = function(){\n this._booleanMap = {};\n this._numericalMap = {};\n this._vectorMap = {};\n}\n\nKnowledge.pro"
},
{
"path": "js/decision-tree/decision-methods/DecisionMethod.js",
"chars": 468,
"preview": "var DecisionMethod = function(parameter){\n this._parameter = parameter;\n}\n\nDecisionMethod.prototype.updateParameter = f"
},
{
"path": "js/decision-tree/decision-methods/IsFalse.js",
"chars": 380,
"preview": "import { DecisionMethod } from \"./DecisionMethod\";\n\nvar IsFalse = function(){\n DecisionMethod.call(this, null);\n}\n\nIsFa"
},
{
"path": "js/decision-tree/decision-methods/IsInRange.js",
"chars": 506,
"preview": "import { DecisionMethod } from \"./DecisionMethod\";\n\nvar IsInRange = function(range){\n DecisionMethod.call(this, range);"
},
{
"path": "js/decision-tree/decision-methods/IsTrue.js",
"chars": 373,
"preview": "import { DecisionMethod } from \"./DecisionMethod\";\n\nvar IsTrue = function(){\n DecisionMethod.call(this, null);\n}\n\nIsTru"
},
{
"path": "js/decision-tree/math/Range.js",
"chars": 1585,
"preview": "var Range = function(lowerBound, upperBound){\n this._isLowerBoundInclusive = true;\n this._isUpperBoundInclusive = true"
},
{
"path": "js/state-machine/State.js",
"chars": 619,
"preview": "import uuidv4 from \"../util/UUIDV4\";\n\nvar State = function(name){\n this._name = name;\n this._id = uuidv4();\n\n this._p"
},
{
"path": "js/state-machine/StateMachine.js",
"chars": 6137,
"preview": "import { State } from \"./State\";\n\nvar StateMachine = function(name, knowledge){\n State.call(this, name);\n\n this._knowl"
},
{
"path": "js/state-machine/Transition.js",
"chars": 1352,
"preview": "import { Decision } from \"../decision-tree/Decision\";\n\nvar Transition = function(source, target, infoName, infoType, dec"
},
{
"path": "js/util/UUIDV4.js",
"chars": 236,
"preview": "var uuidv4 = function(){\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math"
},
{
"path": "package.json",
"chars": 1237,
"preview": "{\n \"name\": \"@oguz.eroglu/ego-js\",\n \"version\": \"1.1.0\",\n \"description\": \"A lightweight decision making library for gam"
},
{
"path": "rollup.config.js",
"chars": 686,
"preview": "import babel from 'rollup-plugin-babel';\nimport babelrc from 'babelrc-rollup';\nimport istanbul from 'rollup-plugin-istan"
},
{
"path": "test/decision-tree/DecisionTest.js",
"chars": 6442,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../build/Ego\");\n\ndescribe(\"Decision\", function(){\n\n it(\"should"
},
{
"path": "test/decision-tree/DecisionTreeTest.js",
"chars": 10647,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../build/Ego\");\n\ndescribe(\"DecisionTree\", function(){\n\n it(\"sh"
},
{
"path": "test/decision-tree/KnowledgeTest.js",
"chars": 6604,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../build/Ego\");\n\ndescribe(\"Knowledge\", function(){\n\n it(\"shoul"
},
{
"path": "test/decision-tree/decision-methods/DecisionMethodTest.js",
"chars": 1160,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../../build/Ego\");\n\ndescribe(\"DecisionMethod\", function(){\n\n i"
},
{
"path": "test/decision-tree/decision-methods/IsFalseTest.js",
"chars": 767,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../../build/Ego\");\n\ndescribe(\"IsFalse\", function(){\n\n it(\"shou"
},
{
"path": "test/decision-tree/decision-methods/IsInRangeTest.js",
"chars": 1437,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../../build/Ego\");\n\ndescribe(\"IsInRange\", function(){\n\n it(\"sh"
},
{
"path": "test/decision-tree/decision-methods/IsTrueTest.js",
"chars": 751,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../../build/Ego\");\n\ndescribe(\"IsTrue\", function(){\n\n it(\"shoul"
},
{
"path": "test/decision-tree/math/RangeTest.js",
"chars": 3340,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../../build/Ego\");\n\ndescribe(\"Range\", function(){\n\n it(\"should"
},
{
"path": "test/state-machine/StateMachineTest.js",
"chars": 41767,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../build/Ego\");\n\ndescribe(\"StateMachine\", function(){\n\n it(\"sh"
},
{
"path": "test/state-machine/StateTest.js",
"chars": 1633,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../build/Ego\");\n\ndescribe(\"State\", function(){\n\n it(\"should in"
},
{
"path": "test/state-machine/TransitionTest.js",
"chars": 5363,
"preview": "var expect = require('expect.js');\nvar Ego = require(\"../../build/Ego\");\n\ndescribe(\"Transition\", function(){\n\n it(\"shou"
}
]
About this extraction
This page contains the full source code of the oguzeroglu/Ego GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (116.3 KB), approximately 28.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.