Skip to content

Commit

Permalink
clone API for state, state machine, and transition
Browse files Browse the repository at this point in the history
  • Loading branch information
oguzeroglu committed Sep 24, 2020
1 parent 32d3aa7 commit 62fcd89
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 0 deletions.
4 changes: 4 additions & 0 deletions js/state-machine/State.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ var State = function(name){
this._parent = null;
}

State.prototype.clone = function(){
return new State(this.getName());
}

State.prototype.setParent = function(parent){

if (this._parent){
Expand Down
33 changes: 33 additions & 0 deletions js/state-machine/StateMachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,39 @@ var StateMachine = function(name, knowledge){

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;
Expand Down
19 changes: 19 additions & 0 deletions js/state-machine/Transition.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@ var Transition = function(source, target, infoName, infoType, decisionMethod){
}
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);

Expand Down
214 changes: 214 additions & 0 deletions test/state-machine/StateMachineTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,40 @@ describe("StateMachine", function(){
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(){
Expand Down Expand Up @@ -926,5 +960,185 @@ describe("StateMachine", function(){
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());
});
});
});
9 changes: 9 additions & 0 deletions test/state-machine/StateTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,13 @@ describe("State", function(){

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);
})
});
62 changes: 62 additions & 0 deletions test/state-machine/TransitionTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,66 @@ describe("Transition", function(){

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");
});
});

0 comments on commit 62fcd89

Please sign in to comment.