├── examples
└── boid
│ ├── server.bat
│ ├── js
│ ├── main.js
│ ├── Scene.js
│ ├── Agent.js
│ └── states.js
│ ├── game.html
│ ├── index.html
│ └── libs
│ └── statejs-0.1.0dev.js
├── docs
└── statejs-0.1.0.zip
├── bower.json
├── .gitignore
├── package.json
├── LICENSE
├── README.md
├── tests
├── tests
│ ├── Blackboard.js
│ ├── FSM.js
│ ├── Utility.js
│ └── Subsumption.js
├── tests.html
├── mocha.css
└── sinon-chai.js
├── libs
├── statejs-0.1.0.min.js
└── statejs-0.1.0.js
└── src
├── State.js
├── statejs.js
├── FSM.js
├── Utility.js
├── Subsumption.js
└── Blackboard.js
/examples/boid/server.bat:
--------------------------------------------------------------------------------
1 | python -m SimpleHTTPServer 8000
--------------------------------------------------------------------------------
/docs/statejs-0.1.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/renatopp/statejs/HEAD/docs/statejs-0.1.0.zip
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "statejs",
3 | "version": "0.1.0",
4 | "homepage": "https://github.com/renatopp/statejs",
5 | "authors": [
6 | {"name":"Renato Pereira", "email":"renato.ppontes@gmail.com", "homepage":"http://guineashots.com"},
7 | ],
8 | "description": "Behavior tree library",
9 | "license": "MIT",
10 | "keywords": [
11 | "state",
12 | "state machine",
13 | "finitie state machine",
14 | "subsumption",
15 | "utility",
16 | "ai",
17 | "artificial intelligence",
18 | "statejs",
19 | ],
20 | "main": [ "libs/statejs-0.1.0.min.js" ],
21 | "ignore": [
22 | "node_modules",
23 | "bower_components",
24 | "build",
25 | "docs",
26 | "src",
27 | "tests",
28 | "tools",
29 | "examples"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Commenting this out is preferred by some people, see
24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
25 | node_modules
26 |
27 | # Users Environment Variables
28 | .lock-wscript
29 |
30 | # IDE configurations
31 | .idea
32 |
33 | # --------------
34 | docs/statejs
35 |
--------------------------------------------------------------------------------
/examples/boid/js/main.js:
--------------------------------------------------------------------------------
1 | var game = {
2 | canvas : null,
3 | stage : null,
4 | scene : null,
5 | machine : null,
6 | fdelta : 0
7 | };
8 | var settings = {
9 | SHEEP_ROTATION_SPEED: 1,
10 | SHEEP_MOVE_SPEED: 1,
11 | SHEEP_OBEY_DISTANCE: 100,
12 | SHEEP_NEIGHBOR_DISTANCE: 100,
13 | SHEEP_MIN_SAFE_DISTANCE: 40,
14 | SHEEP_MAX_VELOCITY: 100,
15 | SHEEP_VELOCITY_DECAY: 0.999,
16 | SHEEP_FORGET_TIME: 1000,
17 | }
18 |
19 |
20 | function main() {
21 | game.canvas = document.getElementById('game');
22 | game.stage = new createjs.Stage(game.canvas);
23 | game.machine = new statejs.FSM()
24 | .add('idle', new IdleState())
25 | .add('obey', new ObeyState())
26 | .add('stopping', new StoppingState())
27 | game.scene = new MainScene();
28 |
29 | createjs.Ticker.framerate = 60;
30 | }
31 | main();
--------------------------------------------------------------------------------
/examples/boid/game.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "statejs",
3 | "version": "0.1.0",
4 | "description": "A behavior tree library for JavaScript",
5 | "main": "libs/statejs-0.1.0.min.js",
6 | "directories": {
7 | "doc" : "docs",
8 | "example" : "examples",
9 | "test" : "tests"
10 | },
11 | "scripts": {
12 | "test": "echo \"Error: no test specified\" && exit 1"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git://github.com/renatopp/statejs.git"
17 | },
18 | "keywords": [
19 | "state",
20 | "state machine",
21 | "finitie state machine",
22 | "subsumption",
23 | "utility",
24 | "ai",
25 | "artificial intelligence",
26 | "statejs",
27 | ],
28 | "author": "Renato Pereira (http://guineashots.com/)",
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/renatopp/statejs/issues"
32 | },
33 | "homepage": "http://statejs.guineashots.com"
34 | }
35 |
--------------------------------------------------------------------------------
/examples/boid/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Renato de Pontes Pereira
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/examples/boid/js/Scene.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | function MainScene() {
4 | this.Container_constructor();
5 |
6 | this.agents = [];
7 | for (var i=0; i<10; i++) {
8 | var agent = new Agent(this);
9 | this.agents.push(agent);
10 | this.addChild(agent);
11 | }
12 |
13 | game.stage.addChild(this);
14 | createjs.Ticker.on('tick', this.update, this);
15 | }
16 | var p = createjs.extend(MainScene, createjs.Container);
17 |
18 | p.updateMovement = function(e) {
19 | var fdelta = e.delta/1000.;
20 |
21 | for (var i=0; i 5) {
26 | var x = agent.x;
27 | var y = agent.y;
28 | agent.x += settings.SHEEP_MOVE_SPEED*agent.dx*fdelta;
29 | agent.y += settings.SHEEP_MOVE_SPEED*agent.dy*fdelta;
30 | }
31 | }
32 | }
33 |
34 | p.update = function(e) {
35 | game.fdelta = e.delta/1000.;
36 |
37 | for (var i=0; i
30 |
31 | It is very recommended that you take a look into the API where you will find
32 | examples and descriptions of features in the library:
33 |
34 | - http://docs.guineashots.com/statejs
35 |
36 |
37 | ------------------
38 | Within the Library
39 | ------------------
40 |
41 | - Finite State Machine
42 | - Utility based
43 | - SubSumption architecture
44 |
45 |
46 | -----------------
47 | Looking for More?
48 | -----------------
49 |
50 | Take a look into Behavior3JS:
51 |
52 | - http://behavior3js.guineashots.com
53 |
--------------------------------------------------------------------------------
/tests/tests/Blackboard.js:
--------------------------------------------------------------------------------
1 | /* BLACKBOARD ============================================================== */
2 | suite('Blackboard', function() {
3 | test('Basic Read & Write operations', function() {
4 | var blackboard = new statejs.Blackboard();
5 |
6 | blackboard.set('var1', 'this is some value');
7 | blackboard.set('var2', 999888);
8 |
9 | assert.equal(blackboard.get('var1'), 'this is some value');
10 | assert.equal(blackboard.get('var2'), 999888);
11 | assert.equal(blackboard.get('var3'), undefined);
12 | });
13 |
14 | test('Tree memory initialization', function() {
15 | var blackboard = new statejs.Blackboard();
16 |
17 | blackboard.set('var1', 'value', 'tree1');
18 |
19 | assert.isNotUndefined(blackboard.get('var1', 'tree1'));
20 | assert.isNotUndefined(blackboard.get('stateMemory', 'tree1'));
21 | });
22 |
23 | test('Read & Write operations within Tree Scope', function() {
24 | var blackboard = new statejs.Blackboard();
25 |
26 | blackboard.set('var1', 'this is some value', 'tree 1');
27 | blackboard.set('var2', 999888, 'tree 2');
28 |
29 | assert.equal(blackboard.get('var1', 'tree 1'), 'this is some value');
30 | assert.equal(blackboard.get('var2', 'tree 2'), 999888);
31 |
32 | assert.equal(blackboard.get('var1', 'tree 2'), undefined);
33 | assert.equal(blackboard.get('var2', 'tree 1'), undefined);
34 | });
35 |
36 | test('Read & Write operations within Tree and Node Scopes', function() {
37 | var blackboard = new statejs.Blackboard();
38 |
39 | blackboard.set('var1', 'value 1', 'tree 1');
40 | blackboard.set('var2', 'value 2', 'tree 1', 'state 1');
41 | blackboard.set('var3', 'value 3', 'tree 1', 'state 2');
42 | blackboard.set('var4', 999888, 'tree 2');
43 |
44 | assert.equal(blackboard.get('var2', 'tree 1', 'state 1'), 'value 2');
45 | assert.equal(blackboard.get('var3', 'tree 1', 'state 2'), 'value 3');
46 | assert.equal(blackboard.get('var2', 'tree 1', 'state 2'), undefined);
47 | assert.equal(blackboard.get('var3', 'tree 1', 'state 1'), undefined);
48 | assert.equal(blackboard.get('var2', 'tree 1'), undefined);
49 | assert.equal(blackboard.get('var1', 'tree 1', 'state 1'), undefined);
50 |
51 | assert.equal(blackboard.get('var2', 'tree 2', 'state 1'), undefined);
52 | });
53 | });
54 | /* ========================================================================= */
55 |
--------------------------------------------------------------------------------
/tests/tests.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mocha Tests
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/tests/tests/FSM.js:
--------------------------------------------------------------------------------
1 | suite('Finite State Machine', function() {
2 | test('Add states', function() {
3 | var machine = new statejs.FSM();
4 |
5 | var state_a = StateStub();
6 | var state_b = StateStub();
7 | machine.add('a', state_a);
8 | machine.add('b', state_b);
9 |
10 | assert.equal(state_a.machine, machine);
11 | assert.equal(state_b.machine, machine);
12 | });
13 |
14 | test('Add states (Error)', function() {
15 | var machine = new statejs.FSM();
16 |
17 | var state_a = StateStub();
18 | var state_b = StateStub();
19 | machine.add('a', state_a);
20 |
21 | assert.throw(function() {machine.add('a', state_b)}, Error);
22 | });
23 |
24 |
25 | test('List states', function() {
26 | var machine = new statejs.FSM();
27 |
28 | var state_a = StateStub();
29 | var state_b = StateStub();
30 | machine.add('a', state_a);
31 | machine.add('b', state_b);
32 |
33 | var states = machine.list();
34 | assert.equal(states.length, 2);
35 | assert.notEqual(states.indexOf('a'), -1);
36 | assert.notEqual(states.indexOf('b'), -1);
37 | });
38 |
39 | test('Get states', function() {
40 | var machine = new statejs.FSM();
41 |
42 | var state_a = StateStub();
43 | var state_b = StateStub();
44 | machine.add('a', state_a);
45 | machine.add('b', state_b);
46 |
47 | assert.equal(machine.get('a'), state_a);
48 | assert.equal(machine.get('b'), state_b);
49 | });
50 |
51 | test('Current state name', function() {
52 | var machine = new statejs.FSM();
53 | machine.id = '1';
54 |
55 | var memory = BlackboardStub();
56 | assert.notEqual(machine.name(memory), 'a');
57 | memory.get.withArgs('name', '1').returns('a');
58 | assert.equal(machine.name(memory), 'a');
59 | });
60 |
61 | test('State transition', function() {
62 | var machine = new statejs.FSM();
63 | machine.id = '1';
64 |
65 | var state_a = StateStub();
66 | var state_b = StateStub();
67 | machine.add('a', state_a);
68 | machine.add('b', state_b);
69 |
70 | var target = null;
71 | var memory = BlackboardStub();
72 |
73 | // first transition
74 | machine.to('a', target, memory);
75 | assert.isTrue(memory.set.withArgs('name', 'a', '1').calledOnce);
76 | assert.isTrue(state_a.enter.withArgs(target, memory).calledOnce);
77 |
78 | // second transition
79 | memory.get.withArgs('name', '1').returns('a');
80 |
81 | machine.to('b', target, memory);
82 | assert.isTrue(memory.set.withArgs('name', 'b', '1').calledOnce);
83 | assert.isTrue(state_a.exit.withArgs(target, memory).calledOnce);
84 | assert.isTrue(state_b.enter.withArgs(target, memory).calledOnce);
85 | });
86 |
87 | test('Ticking state', function() {
88 | var machine = new statejs.FSM();
89 | machine.id = '1';
90 |
91 | var state_a = StateStub();
92 | var state_b = StateStub();
93 | machine.add('a', state_a);
94 | machine.add('b', state_b);
95 |
96 | var target = null;
97 | var memory = BlackboardStub();
98 |
99 | // ticking
100 | memory.get.withArgs('name', '1').returns('a');
101 | machine.tick(target, memory);
102 |
103 | assert.isTrue(state_a.tick.withArgs(target, memory).calledOnce);
104 | assert.isFalse(state_b.tick.withArgs(target, memory).calledOnce);
105 | });
106 | });
--------------------------------------------------------------------------------
/libs/statejs-0.1.0.min.js:
--------------------------------------------------------------------------------
1 | /*! StateJS 2015-04-02 */
2 | this.statejs=this.statejs||{},function(){"use strict";statejs.createUUID=function(){for(var a=[],b="0123456789abcdef",c=0;36>c;c++)a[c]=b.substr(Math.floor(16*Math.random()),1);a[14]="4",a[19]=b.substr(3&a[19]|8,1),a[8]=a[13]=a[18]=a[23]="-";var d=a.join("");return d},statejs.Class=function(a){var b=function(a){this.initialize(a)};return a&&(b.prototype=Object.create(a.prototype),b.prototype.constructor=b),b.prototype.initialize||(b.prototype.initialize=function(){}),b}}(),this.statejs=this.statejs||{},function(){"use strict";var a=statejs.Class(),b=a.prototype;b.initialize=function(){this._baseMemory={},this._machineMemory={}},b._getMachineMemory=function(a){return this._machineMemory[a]||(this._machineMemory[a]={stateMemory:{}}),this._machineMemory[a]},b._getStateMemory=function(a,b){var c=a.stateMemory;return c[b]||(c[b]={}),c[b]},b._getMemory=function(a,b){var c=this._baseMemory;return a&&(c=this._getMachineMemory(a),b&&(c=this._getStateMemory(c,b))),c},b.set=function(a,b,c,d){var e=this._getMemory(c,d);e[a]=b},b.get=function(a,b,c){var d=this._getMemory(b,c);return d[a]},statejs.Blackboard=a}(),this.statejs=this.statejs||{},function(){"use strict";var a=statejs.Class(),b=a.prototype;this.id=null,this.machine=null,b.initialize=function(){this.id=statejs.createUUID(),this.machine=null},b.enter=function(){},b.potential=function(){console&&console.log&&console.log("Warning: potential not implemented.")},b.tick=function(){},b.exit=function(){}}(),this.statejs=this.statejs||{},function(){"use strict";var a=statejs.Class(),b=a.prototype;this.id=null,b.initialize=function(){this.id=statejs.createUUID(),this._states={}},b.add=function(a,b){if("undefined"!=typeof this._states[a])throw new Error('State "'+a+'" already on the FSM.');return this._states[a]=b,b.machine=this,this},b.get=function(a){return this._states[a]},b.list=function(){var a=[];for(var b in this._states)a.push(b);return a},b.name=function(a){return a.get("name",this.id)},b.to=function(a,b,c){if("undefined"==typeof this._states[a])throw new Error('State "'+a+'" does not exist.');var d=c.get("name",this.id),e=this.get(d);e&&e.exit(b,c);var f=this._states[a];return c.set("name",a,this.id),f.enter(b,c),this},b.tick=function(a,b){var c=b.get("name",this.id),d=this.get(c);d&&d.tick(a,b)},statejs.FSM=a}(),this.statejs=this.statejs||{},function(){"use strict";var a=statejs.Class(),b=a.prototype;this.id=null,b.initialize=function(){this.id=statejs.createUUID(),this._states=[]},b.add=function(a,b){for(var c=0;cd&&(d=g,e=c)}e&&(this._to(e.name,a,b),e.state.tick(a,b))},b._to=function(a,b,c){if(null===a)return void c.set("name",null,this.id);if(a!==this.name){var d=this.get(a);if("undefined"==typeof d)throw new Error('State "'+a+'" does not exist.');var e=c.get("name",this.id),f=this.get(e);return f&&f.exit(b,c),c.set("name",a,this.id),d.enter(b,c),this}},statejs.Utility=a}();
--------------------------------------------------------------------------------
/src/State.js:
--------------------------------------------------------------------------------
1 | /**
2 | * State
3 | *
4 | * Copyright (c) 2015 Renato de Pontes Pereira.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to
8 | * deal in the Software without restriction, including without limitation the
9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | * sell copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 | * IN THE SOFTWARE.
23 | **/
24 |
25 | /**
26 | * @module StateJS
27 | **/
28 |
29 | // namespace:
30 | this.statejs = this.statejs || {};
31 |
32 | (function() {
33 | "use strict";
34 |
35 | /**
36 | * The State class represents generic states that works for all machines.
37 | * Some machines require the implementation of specific methods in this
38 | * class. Please consult the documentation of the machine you want to use and
39 | * the description of each method here.
40 | *
41 | * To extend the State class you can use the `statejs.Class` function:
42 | *
43 | * var MyState = statejs.Class(statejs.State);
44 | *
45 | * MyState.prototype.tick = function(target, memory) {
46 | * console.log('My Implementation')
47 | * }
48 | *
49 | * @class State
50 | **/
51 | var State = statejs.Class();
52 | var p = State.prototype;
53 |
54 | /**
55 | * State unique ID.
56 | *
57 | * @property id
58 | * @type {String}
59 | * @readonly
60 | **/
61 | this.id = null;
62 |
63 | /**
64 | * The reference to the machine in which this state was added.
65 | *
66 | * @property machine
67 | * @type {Object}
68 | * @readonly
69 | **/
70 | this.machine = null;
71 |
72 | /**
73 | * Initialization method.
74 | *
75 | * @method initialize
76 | * @constructor
77 | */
78 | p.initialize = function() {
79 | this.id = statejs.createUUID();
80 | this.machine = null;
81 | }
82 |
83 | /**
84 | * Enter method, override this to use. It is called when the machine assume
85 | * this state as the current one
86 | *
87 | * @method enter
88 | * @param {Object} target A target object, commonly an agent.
89 | * @param {Object} memory A blackboard object.
90 | */
91 | p.enter = function(target, memory) {}
92 |
93 | /**
94 | * The potential method is used to some machines for different things.
95 | * Consult the machine documentation to know how to implement this.
96 | *
97 | * In general, this method is used by machines to verify if the state is able
98 | * to execute at a given moment or not.
99 | *
100 | * @method potential
101 | * @param {Object} target A target object, commonly an agent.
102 | * @param {Object} memory A blackboard object.
103 | */
104 | p.potential = function(target, memory) {
105 | if (console && console.log) {
106 | console.log('Warning: potential not implemented.');
107 | }
108 | }
109 |
110 | /**
111 | * Tick method is called every time a machine is asked to update. Depending
112 | * on the machine, the State tick may only be called if it is the current
113 | * executing state. Consult the machine documentation to known more.
114 | *
115 | * @method tick
116 | * @param {Object} target A target object, commonly an agent.
117 | * @param {Object} memory A blackboard object.
118 | */
119 | p.tick = function(target, memory) {}
120 |
121 | /**
122 | * Exit method is called when the state is replaced by another on the
123 | * machine.
124 | *
125 | * @method exit
126 | * @param {Object} target A target object, commonly an agent.
127 | * @param {Object} memory A blackboard object.
128 | */
129 | p.exit = function(target, memory) {}
130 | })();
--------------------------------------------------------------------------------
/tests/tests/Utility.js:
--------------------------------------------------------------------------------
1 | suite('Utility', function() {
2 | test('Add states', function() {
3 | var machine = new statejs.Utility();
4 |
5 | var state_a = StateStub();
6 | var state_b = StateStub();
7 | machine.add('a', state_a);
8 | machine.add('b', state_b);
9 |
10 | assert.equal(state_a.machine, machine);
11 | assert.equal(state_b.machine, machine);
12 | });
13 |
14 | test('Add states (Error)', function() {
15 | var machine = new statejs.Utility();
16 |
17 | var state_a = StateStub();
18 | var state_b = StateStub();
19 | machine.add('a', state_a);
20 |
21 | assert.throw(function() {machine.add('a', state_b)}, Error);
22 | });
23 |
24 | test('List states', function() {
25 | var machine = new statejs.Utility();
26 |
27 | var state_a = StateStub();
28 | var state_b = StateStub();
29 | machine.add('a', state_a);
30 | machine.add('b', state_b);
31 |
32 | var states = machine.list();
33 | assert.equal(states.length, 2);
34 | assert.notEqual(states.indexOf('a'), -1);
35 | assert.notEqual(states.indexOf('b'), -1);
36 | });
37 |
38 | test('Get states', function() {
39 | var machine = new statejs.Utility();
40 |
41 | var state_a = StateStub();
42 | var state_b = StateStub();
43 | machine.add('a', state_a);
44 | machine.add('b', state_b);
45 |
46 | assert.equal(machine.get('a'), state_a);
47 | assert.equal(machine.get('b'), state_b);
48 | });
49 |
50 | test('Checking potential state', function() {
51 | var machine = new statejs.Utility();
52 | machine.id = '1';
53 |
54 | var state_a = StateStub();
55 | var state_b = StateStub();
56 | machine.add('a', state_a);
57 | machine.add('b', state_b);
58 |
59 | var target = null;
60 | var memory = BlackboardStub();
61 |
62 | // ticking
63 | machine.tick(target, memory);
64 | assert.isTrue(state_a.potential.withArgs(target, memory).calledOnce);
65 | assert.isTrue(state_b.potential.withArgs(target, memory).calledOnce);
66 | });
67 |
68 | test('Current state name', function() {
69 | var machine = new statejs.Utility();
70 | machine.id = '1';
71 |
72 | var memory = BlackboardStub();
73 |
74 | assert.notEqual(machine.name(memory), 'a');
75 | memory.get.withArgs('name', '1').returns('a');
76 | assert.equal(machine.name(memory), 'a');
77 | });
78 |
79 | test('State transition', function() {
80 | var machine = new statejs.Utility();
81 | machine.id = '1';
82 |
83 | var state_a = StateStub();
84 | var state_b = StateStub();
85 | machine.add('a', state_a);
86 | machine.add('b', state_b);
87 |
88 | var target = null;
89 | var memory = BlackboardStub();
90 |
91 | // ticking
92 | state_a.potential.returns(15);
93 | state_b.potential.returns(10);
94 | machine.tick(target, memory);
95 | assert.isTrue(memory.set.withArgs('name', 'a', '1').calledOnce);
96 | assert.isTrue(state_a.potential.withArgs(target, memory).calledOnce);
97 | assert.isTrue(state_a.enter.withArgs(target, memory).calledOnce);
98 |
99 | assert.isTrue(state_b.potential.withArgs(target, memory).calledOnce);
100 | assert.isFalse(state_b.enter.withArgs(target, memory).calledOnce);
101 | });
102 |
103 | test('State transition (cont)', function() {
104 | var machine = new statejs.Utility();
105 | machine.id = '1';
106 |
107 | var state_a = StateStub();
108 | var state_b = StateStub();
109 | machine.add('a', state_a);
110 | machine.add('b', state_b);
111 |
112 | var target = null;
113 | var memory = BlackboardStub();
114 | // ticking
115 | state_a.potential.returns(15);
116 | state_b.potential.returns(25);
117 | machine.tick(target, memory);
118 | assert.isTrue(memory.set.withArgs('name', 'b', '1').calledOnce);
119 | assert.isTrue(state_a.potential.withArgs(target, memory).calledOnce);
120 | assert.isFalse(state_a.enter.withArgs(target, memory).calledOnce);
121 |
122 | assert.isTrue(state_b.potential.withArgs(target, memory).calledOnce);
123 | assert.isTrue(state_b.enter.withArgs(target, memory).calledOnce);
124 | });
125 |
126 | test('Ticking current state', function() {
127 | var machine = new statejs.Utility();
128 | machine.id = '1';
129 |
130 | var state_a = StateStub();
131 | var state_b = StateStub();
132 | machine.add('a', state_a);
133 | machine.add('b', state_b);
134 |
135 | var target = null;
136 | var memory = BlackboardStub();
137 | // ticking
138 | state_a.potential.returns(false);
139 | state_b.potential.returns(true);
140 | machine.tick(target, memory);
141 |
142 | assert.isFalse(state_a.tick.withArgs(target, memory).calledOnce);
143 | assert.isTrue(state_b.tick.withArgs(target, memory).calledOnce);
144 | });
145 | });
--------------------------------------------------------------------------------
/src/statejs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * statejs
3 | *
4 | * Copyright (c) 2015 Renato de Pontes Pereira.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to
8 | * deal in the Software without restriction, including without limitation the
9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | * sell copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 | * IN THE SOFTWARE.
23 | */
24 | this.statejs = this.statejs || {};
25 |
26 | /**
27 | * StateJS
28 | * =======
29 | *
30 | * * * *
31 | *
32 | * **StateJS** is a Javascript library that provides state-based models such
33 | * as Finite State Machines and the Subsumption architecture. It aims speed,
34 | * low memory consuption and multi-agent control. Check out some features
35 | * of StateJS:
36 | *
37 | * - StateJS provides **Finite State Machines**, **Subsumption Architecture**
38 | * and **Utility Functions**;
39 | * - **Extensible and Flexible**, create new Machines or your own version of
40 | * FSM or Utilities;
41 | * - **Optimized to control multiple agents**, you can use a single machine
42 | * instance to handle hundreds of agents;
43 | * - **Completely free**, StateJS is published under the MIT License, which
44 | * means that you can use it for your open source and commercial projects;
45 | * - **Lightweight**, only 5KB!
46 | *
47 | * Visit http://statejs.guineashots.com to know more!
48 | *
49 | * @module StateJS
50 | * @main StateJS
51 | */
52 | (function() {
53 | "use strict";
54 |
55 | /**
56 | * List of internal and helper functions in StateJS.
57 | *
58 | * @class Utils
59 | **/
60 |
61 | /**
62 | * This function is used to create unique IDs for machines and states.
63 | *
64 | * (consult http://www.ietf.org/rfc/rfc4122.txt).
65 | *
66 | * @method createUUID
67 | * @return {String} A unique ID.
68 | */
69 | statejs.createUUID = function() {
70 | var s = [];
71 | var hexDigits = "0123456789abcdef";
72 | for (var i = 0; i < 36; i++) {
73 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
74 | }
75 | // bits 12-15 of the time_hi_and_version field to 0010
76 | s[14] = "4";
77 |
78 | // bits 6-7 of the clock_seq_hi_and_reserved to 01
79 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
80 |
81 | s[8] = s[13] = s[18] = s[23] = "-";
82 |
83 | var uuid = s.join("");
84 | return uuid;
85 | }
86 |
87 | /**
88 | * Class is a meta-factory function to create classes in JavaScript. It is a
89 | * shortcut for the CreateJS syntax style. By default, the class created by
90 | * this function have an initialize function (the constructor). Optionally, you
91 | * can specify the inheritance by passing another class as parameter.
92 | *
93 | * By default, all classes created using this function, may receives only a
94 | * settings parameter as argument. This pattern is commonly used by jQuery and
95 | * its plugins.
96 | *
97 | * Usage
98 | * -----
99 | *
100 | * // Creating a simple class
101 | * var BaseClass = statejs.Class();
102 | *
103 | * // Using inheritance
104 | * var ChildClass = statejs.Class(BaseClass);
105 | *
106 | * // Defining the constructor
107 | * ChildClass.prototype.initialize = function(settings) { ... }
108 | *
109 | * @method Class
110 | * @param {Object} baseClass The super class.
111 | * @return {Object} A new class.
112 | */
113 | statejs.Class = function(baseClass) {
114 | // create a new class
115 | var cls = function(params) {
116 | this.initialize(params);
117 | };
118 |
119 | // if base class is provided, inherit
120 | if (baseClass) {
121 | cls.prototype = Object.create(baseClass.prototype);
122 | cls.prototype.constructor = cls;
123 | }
124 |
125 | // create initialize if does not exist on baseClass
126 | if(!cls.prototype.initialize) {
127 | cls.prototype.initialize = function() {};
128 | }
129 |
130 | return cls;
131 | }
132 |
133 | })();
134 |
--------------------------------------------------------------------------------
/tests/mocha.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | body {
4 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
5 | padding: 0px 50px;
6 | }
7 |
8 | #mocha ul, #mocha li {
9 | margin: 0;
10 | padding: 0;
11 | }
12 |
13 | #mocha ul {
14 | list-style: none;
15 | }
16 |
17 | #mocha h1, #mocha h2 {
18 | margin: 0;
19 | }
20 |
21 | #mocha h1 {
22 | margin-top: 15px;
23 | font-size: 1em;
24 | font-weight: 200;
25 | }
26 |
27 | #mocha h1 a {
28 | text-decoration: none;
29 | color: inherit;
30 | }
31 |
32 | #mocha h1 a:hover {
33 | text-decoration: underline;
34 | }
35 |
36 | #mocha .suite .suite h1 {
37 | margin-top: 0;
38 | font-size: .8em;
39 | }
40 |
41 | .hidden {
42 | display: none;
43 | }
44 |
45 | #mocha h2 {
46 | font-size: 12px;
47 | font-weight: normal;
48 | cursor: pointer;
49 | }
50 |
51 | #mocha .suite {
52 | margin-left: 15px;
53 | }
54 |
55 | #mocha .test {
56 | margin-left: 15px;
57 | overflow: hidden;
58 | }
59 |
60 | #mocha .test.pending:hover h2::after {
61 | content: '(pending)';
62 | font-family: arial;
63 | }
64 |
65 | #mocha .test.pass.medium .duration {
66 | background: #C09853;
67 | }
68 |
69 | #mocha .test.pass.slow .duration {
70 | background: #B94A48;
71 | }
72 |
73 | #mocha .test.pass::before {
74 | content: '✓';
75 | font-size: 12px;
76 | display: block;
77 | float: left;
78 | margin-right: 5px;
79 | color: #00d6b2;
80 | }
81 |
82 | #mocha .test.pass .duration {
83 | font-size: 9px;
84 | margin-left: 5px;
85 | padding: 2px 5px;
86 | color: white;
87 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
88 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
89 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
90 | -webkit-border-radius: 5px;
91 | -moz-border-radius: 5px;
92 | -ms-border-radius: 5px;
93 | -o-border-radius: 5px;
94 | border-radius: 5px;
95 | }
96 |
97 | #mocha .test.pass.fast .duration {
98 | display: none;
99 | }
100 |
101 | #mocha .test.pending {
102 | color: #0b97c4;
103 | }
104 |
105 | #mocha .test.pending::before {
106 | content: '◦';
107 | color: #0b97c4;
108 | }
109 |
110 | #mocha .test.fail {
111 | color: #c00;
112 | }
113 |
114 | #mocha .test.fail pre {
115 | color: black;
116 | }
117 |
118 | #mocha .test.fail::before {
119 | content: '✖';
120 | font-size: 12px;
121 | display: block;
122 | float: left;
123 | margin-right: 5px;
124 | color: #c00;
125 | }
126 |
127 | #mocha .test pre.error {
128 | color: #c00;
129 | max-height: 300px;
130 | overflow: auto;
131 | }
132 |
133 | #mocha .test pre {
134 | display: block;
135 | float: left;
136 | clear: left;
137 | font: 12px/1.5 monaco, monospace;
138 | margin: 5px;
139 | padding: 15px;
140 | border: 1px solid #eee;
141 | border-bottom-color: #ddd;
142 | -webkit-border-radius: 3px;
143 | -webkit-box-shadow: 0 1px 3px #eee;
144 | -moz-border-radius: 3px;
145 | -moz-box-shadow: 0 1px 3px #eee;
146 | }
147 |
148 | #mocha .test h2 {
149 | position: relative;
150 | }
151 |
152 | #mocha .test a.replay {
153 | position: absolute;
154 | top: 3px;
155 | right: 0;
156 | text-decoration: none;
157 | vertical-align: middle;
158 | display: block;
159 | width: 15px;
160 | height: 15px;
161 | line-height: 15px;
162 | text-align: center;
163 | background: #eee;
164 | font-size: 15px;
165 | -moz-border-radius: 15px;
166 | border-radius: 15px;
167 | -webkit-transition: opacity 200ms;
168 | -moz-transition: opacity 200ms;
169 | transition: opacity 200ms;
170 | opacity: 0.3;
171 | color: #888;
172 | }
173 |
174 | #mocha .test:hover a.replay {
175 | opacity: 1;
176 | }
177 |
178 | #mocha-report.pass .test.fail {
179 | display: none;
180 | }
181 |
182 | #mocha-report.fail .test.pass {
183 | display: none;
184 | }
185 |
186 | #mocha-error {
187 | color: #c00;
188 | font-size: 1.5 em;
189 | font-weight: 100;
190 | letter-spacing: 1px;
191 | }
192 |
193 | #mocha-stats {
194 | position: fixed;
195 | top: 15px;
196 | left: 10px;
197 | background: #D2D2D2;
198 | border: 1px solid #999;
199 | text-align: center;
200 | padding: 5px 0px;
201 | width: 55px;
202 | font-size: 12px;
203 | margin: 0;
204 | color: #333;
205 | }
206 |
207 | #mocha-stats .progress {
208 | /*float: lef;*/
209 | /*padding-top: 15;*/
210 | }
211 |
212 | #mocha-stats em {
213 | color: #000;
214 | font-weight: bold;
215 | }
216 |
217 | #mocha-stats a {
218 | text-decoration: none;
219 | color: inherit;
220 | }
221 |
222 | #mocha-stats a:hover {
223 | border-bottom: 1px solid #333;
224 | }
225 |
226 | #mocha-stats li {
227 | display: inline-block;
228 | margin: 0 0px;
229 | list-style: none;
230 | padding: 5px;
231 | }
232 | #mocha-stats li:first-child {
233 | padding-top: 10px;
234 | }
235 | #mocha-stats li:last-child {
236 | padding-bottom: 10px;
237 | }
238 |
239 | code .comment { color: #ddd }
240 | code .init { color: #2F6FAD }
241 | code .string { color: #5890AD }
242 | code .keyword { color: #8A6343 }
243 | code .number { color: #2F6FAD }
244 |
--------------------------------------------------------------------------------
/src/FSM.js:
--------------------------------------------------------------------------------
1 | /**
2 | * State
3 | *
4 | * Copyright (c) 2015 Renato de Pontes Pereira.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to
8 | * deal in the Software without restriction, including without limitation the
9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 | * sell copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 | * IN THE SOFTWARE.
23 | **/
24 |
25 | /**
26 | * @module StateJS
27 | **/
28 |
29 | // namespace:
30 | this.statejs = this.statejs || {};
31 |
32 | (function() {
33 | "use strict";
34 |
35 | /**
36 | * A Finite State Machine implementation.
37 | *
38 | * The FSM does not use the `potential` method in the state, and it only call
39 | * the `tick` method of the current state.
40 | *
41 | * @class FSM
42 | */
43 | var FSM = statejs.Class();
44 | var p = FSM.prototype;
45 |
46 | /**
47 | * Machine unique ID.
48 | *
49 | * @property id
50 | * @type {String}
51 | * @readonly
52 | **/
53 | this.id = null;
54 |
55 | /**
56 | * Initialization method.
57 | *
58 | * @method initialize
59 | * @constructor
60 | */
61 | p.initialize = function() {
62 | this.id = statejs.createUUID();
63 | this._states = {};
64 | }
65 |
66 | /**
67 | * Adds a new state to the FSM. The state is identified by a name, which must
68 | * be unique.
69 | *
70 | * @method add
71 | * @param {String} name The unique name that identifies the state.
72 | * @param {State} state The state object.
73 | */
74 | p.add = function(name, state) {
75 | if (typeof this._states[name] !== 'undefined') {
76 | throw new Error('State "'+name+'" already on the FSM.');
77 | }
78 |
79 | this._states[name] = state;
80 | state.machine = this;
81 |
82 | return this;
83 | }
84 |
85 | /**
86 | * Returns a registered state instance by name.
87 | *
88 | * @method get
89 | * @param {String} name The state name.
90 | * @return {State} The state.
91 | */
92 | p.get = function(name) {
93 | return this._states[name];
94 | }
95 |
96 | /**
97 | * Returns a list of all state names registered in this machine.
98 | *
99 | * @method list
100 | * @return {Array} An array of state names.
101 | */
102 | p.list = function() {
103 | var result = [];
104 | for (var name in this._states) {
105 | result.push(name);
106 | }
107 |
108 | return result;
109 | }
110 |
111 | /**
112 | * Return the name of the current state. Requires a blackboard instance.
113 | *
114 | * @method name
115 | * @param {statejs.Blackboard} memory A Blackboard instance.
116 | * @return {String} the name of the current state or `null` if none.
117 | */
118 | p.name = function(memory) {
119 | return memory.get('name', this.id);
120 | }
121 |
122 | /**
123 | * Change the machine to the new state.
124 | *
125 | * @method to
126 | * @param {String} name The state name.
127 | * @param {Object} target A target object.
128 | * @param {statejs.Blackboard} memory A Blackboard instance.
129 | */
130 | p.to = function(name, target, memory) {
131 | if (typeof this._states[name] === 'undefined') {
132 | throw new Error('State "'+name+'" does not exist.');
133 | }
134 |
135 | // exit current state
136 | var fromStateName = memory.get('name', this.id);
137 | var fromState = this.get(fromStateName);
138 | if (fromState) {
139 | fromState.exit(target, memory);
140 | }
141 |
142 | // change to the next state
143 | var state = this._states[name];
144 | memory.set('name', name, this.id);
145 | state.enter(target, memory);
146 |
147 | return this;
148 | }
149 |
150 | /**
151 | * Propagates the update to current state.
152 | *
153 | * @method tick
154 | * @param {Object} target A target object.
155 | * @param {statejs.Blackboard} memory A Blackboard instance.
156 | */
157 | p.tick = function(target, memory) {
158 | var stateName = memory.get('name', this.id);
159 | var state = this.get(stateName);
160 | if (state) {
161 | state.tick(target, memory);
162 | }
163 | }
164 |
165 | statejs.FSM = FSM;
166 | })();
--------------------------------------------------------------------------------
/examples/boid/js/states.js:
--------------------------------------------------------------------------------
1 | var dist = function(x1, y1, x2, y2) {
2 | return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
3 | }
4 | var norm2d = function(x, y) {
5 | return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
6 | }
7 |
8 | function _avoidMouse(target, neighbors) {
9 | var px = 0;
10 | var py = 0;
11 | var mx = game.stage.mouseX;
12 | var my = game.stage.mouseY;
13 |
14 | if (dist(target.x, target.y, mx, my) < settings.SHEEP_OBEY_DISTANCE) {
15 | var px = (mx - target.x);
16 | var py = (my - target.y);
17 | }
18 |
19 | return [-px, -py];
20 | }
21 | function _centering(target, neighbors) {
22 | var px = 0;
23 | var py = 0;
24 |
25 | if (neighbors.length) {
26 | for (var i=0; i settings.SHEEP_MAX_VELOCITY) {
87 | target.dx = (target.dx/norm)*settings.SHEEP_MAX_VELOCITY;
88 | target.dy = (target.dy/norm)*settings.SHEEP_MAX_VELOCITY;
89 | }
90 | }
91 |
92 | var IdleState = statejs.Class(statejs.State);
93 | IdleState.prototype.enter = function(target, memory) {}
94 | IdleState.prototype.exit = function(target, memory) {}
95 | IdleState.prototype.tick = function(target, memory) {
96 | var mx = game.stage.mouseX;
97 | var my = game.stage.mouseY;
98 |
99 | if (dist(target.x, target.y, mx, my) < settings.SHEEP_OBEY_DISTANCE) {
100 | this.machine.to('obey', target, memory);
101 | }
102 |
103 | flock(target, memory.get('neighbors'), [0.0, 0.0, 0.3, 0.1])
104 | }
105 |
106 | var ObeyState = statejs.Class(statejs.State);
107 | ObeyState.prototype.enter = function(target, memory) {}
108 | ObeyState.prototype.exit = function(target, memory) {
109 | memory.set('starttime', false, this.machine.id, this.id)
110 | }
111 | ObeyState.prototype.tick = function(target, memory) {
112 | var mx = game.stage.mouseX;
113 | var my = game.stage.mouseY;
114 |
115 | if (dist(target.x, target.y, mx, my) > settings.SHEEP_OBEY_DISTANCE) {
116 | var starttime = memory.get('starttime', this.machine.id, this.id);
117 | var curtime = new Date().getTime();
118 | if (!starttime) {
119 | memory.set('starttime', curtime, this.machine.id, this.id)
120 | starttime = curtime;
121 | }
122 | // console.log(curtime, starttime, curtime-starttime, settings.SHEEP_FORGET_TIME)
123 | if (curtime-starttime > settings.SHEEP_FORGET_TIME) {
124 | this.machine.to('stopping', target, memory);
125 | }
126 | } else {
127 | memory.set('starttime', false, this.machine.id, this.id)
128 | }
129 |
130 | flock(target, memory.get('neighbors'), [1.0, 0.6, 1.0, 1.0])
131 | }
132 |
133 | var StoppingState = statejs.Class(statejs.State);
134 | StoppingState.prototype.enter = function(target, memory) {
135 | memory.set('starttime', new Date().getTime(), this.machine.id, this.id);
136 | }
137 | StoppingState.prototype.exit = function(target, memory) {}
138 | StoppingState.prototype.tick = function(target, memory) {
139 | var mx = game.stage.mouseX;
140 | var my = game.stage.mouseY;
141 |
142 | if (dist(target.x, target.y, mx, my) < settings.SHEEP_OBEY_DISTANCE) {
143 | this.machine.to('obey', target, memory);
144 | }
145 |
146 | var starttime = memory.get('starttime', this.machine.id, this.id);
147 | var curtime = new Date().getTime();
148 | if (curtime - starttime > 3000) {
149 | this.machine.to('idle', target, memory);
150 | }
151 |
152 | flock(target, memory.get('neighbors'), [0.0, 0.1, 1.0, 0.4])
153 | }
154 |
--------------------------------------------------------------------------------
/tests/tests/Subsumption.js:
--------------------------------------------------------------------------------
1 | suite('Subsumption', function() {
2 | test('Add states', function() {
3 | var machine = new statejs.Subsumption();
4 |
5 | var state_a = StateStub();
6 | var state_b = StateStub();
7 | machine.add('a', state_a);
8 | machine.add('b', state_b);
9 |
10 | assert.equal(state_a.machine, machine);
11 | assert.equal(state_b.machine, machine);
12 | });
13 |
14 | test('Add states (Error)', function() {
15 | var machine = new statejs.Subsumption();
16 |
17 | var state_a = StateStub();
18 | var state_b = StateStub();
19 | machine.add('a', state_a);
20 |
21 | assert.throw(function() {machine.add('a', state_b)}, Error);
22 | });
23 |
24 | test('List states', function() {
25 | var machine = new statejs.Subsumption();
26 |
27 | var state_a = StateStub();
28 | var state_b = StateStub();
29 | machine.add('a', state_a);
30 | machine.add('b', state_b);
31 |
32 | var states = machine.list();
33 | assert.equal(states.length, 2);
34 | assert.notEqual(states.indexOf('a'), -1);
35 | assert.notEqual(states.indexOf('b'), -1);
36 | });
37 |
38 | test('Get states', function() {
39 | var machine = new statejs.Subsumption();
40 |
41 | var state_a = StateStub();
42 | var state_b = StateStub();
43 | machine.add('a', state_a);
44 | machine.add('b', state_b);
45 |
46 | assert.equal(machine.get('a'), state_a);
47 | assert.equal(machine.get('b'), state_b);
48 | });
49 |
50 | test('Checking potential state', function() {
51 | var machine = new statejs.Subsumption();
52 | machine.id = '1';
53 |
54 | var state_a = StateStub();
55 | var state_b = StateStub();
56 | machine.add('a', state_a);
57 | machine.add('b', state_b);
58 |
59 | var target = null;
60 | var memory = BlackboardStub();
61 |
62 | // ticking
63 | machine.tick(target, memory);
64 | assert.isTrue(state_a.potential.withArgs(target, memory).calledOnce);
65 | assert.isTrue(state_b.potential.withArgs(target, memory).calledOnce);
66 | });
67 |
68 | test('Current state name', function() {
69 | var machine = new statejs.Subsumption();
70 | machine.id = '1';
71 |
72 | var memory = BlackboardStub();
73 |
74 | assert.notEqual(machine.name(memory), 'a');
75 | memory.get.withArgs('name', '1').returns('a');
76 | assert.equal(machine.name(memory), 'a');
77 | });
78 |
79 | test('State transition', function() {
80 | var machine = new statejs.Subsumption();
81 | machine.id = '1';
82 |
83 | var state_a = StateStub();
84 | var state_b = StateStub();
85 | machine.add('a', state_a);
86 | machine.add('b', state_b);
87 |
88 | var target = null;
89 | var memory = BlackboardStub();
90 |
91 | // ticking
92 | state_a.potential.returns(true);
93 | machine.tick(target, memory);
94 | assert.isTrue(memory.set.withArgs('name', 'a', '1').calledOnce);
95 | assert.isTrue(state_a.potential.withArgs(target, memory).calledOnce);
96 | assert.isTrue(state_a.enter.withArgs(target, memory).calledOnce);
97 |
98 | assert.isFalse(state_b.potential.withArgs(target, memory).calledOnce);
99 | assert.isFalse(state_b.enter.withArgs(target, memory).calledOnce);
100 | });
101 |
102 | test('State transition (cont)', function() {
103 | var machine = new statejs.Subsumption();
104 | machine.id = '1';
105 |
106 | var state_a = StateStub();
107 | var state_b = StateStub();
108 | machine.add('a', state_a);
109 | machine.add('b', state_b);
110 |
111 | var target = null;
112 | var memory = BlackboardStub();
113 | // ticking
114 | state_a.potential.returns(false);
115 | state_b.potential.returns(true);
116 | machine.tick(target, memory);
117 | assert.isTrue(memory.set.withArgs('name', 'b', '1').calledOnce);
118 | assert.isTrue(state_a.potential.withArgs(target, memory).calledOnce);
119 | assert.isFalse(state_a.enter.withArgs(target, memory).calledOnce);
120 |
121 | assert.isTrue(state_b.potential.withArgs(target, memory).calledOnce);
122 | assert.isTrue(state_b.enter.withArgs(target, memory).calledOnce);
123 | });
124 |
125 | test('Transition to null', function() {
126 | var machine = new statejs.Subsumption();
127 | machine.id = '1';
128 |
129 | var state_a = StateStub();
130 | var state_b = StateStub();
131 | machine.add('a', state_a);
132 | machine.add('b', state_b);
133 |
134 | var target = null;
135 | var memory = BlackboardStub();
136 | // ticking
137 | state_a.potential.returns(false);
138 | state_b.potential.returns(false);
139 | machine.tick(target, memory);
140 |
141 | assert.isTrue(memory.set.withArgs('name', null, '1').calledOnce);
142 | assert.isFalse(state_a.enter.withArgs(target, memory).calledOnce);
143 | assert.isFalse(state_b.enter.withArgs(target, memory).calledOnce);
144 | });
145 |
146 |
147 |
148 | test('Ticking current state', function() {
149 | var machine = new statejs.Subsumption();
150 | machine.id = '1';
151 |
152 | var state_a = StateStub();
153 | var state_b = StateStub();
154 | machine.add('a', state_a);
155 | machine.add('b', state_b);
156 |
157 | var target = null;
158 | var memory = BlackboardStub();
159 | // ticking
160 | state_a.potential.returns(false);
161 | state_b.potential.returns(true);
162 | machine.tick(target, memory);
163 |
164 | assert.isFalse(state_a.tick.withArgs(target, memory).calledOnce);
165 | assert.isTrue(state_b.tick.withArgs(target, memory).calledOnce);
166 | });
167 | });
--------------------------------------------------------------------------------
/tests/sinon-chai.js:
--------------------------------------------------------------------------------
1 | (function (sinonChai) {
2 | "use strict";
3 |
4 | // Module systems magic dance.
5 |
6 | if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
7 | // NodeJS
8 | module.exports = sinonChai;
9 | } else if (typeof define === "function" && define.amd) {
10 | // AMD
11 | define(function () {
12 | return sinonChai;
13 | });
14 | } else {
15 | // Other environment (usually