Skip to content

Commit

Permalink
feat(jsbattle-engine): add custom finish conditions for battles
Browse files Browse the repository at this point in the history
  • Loading branch information
jamro committed Dec 9, 2019
1 parent 7c2d1dd commit 441d69a
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 16 deletions.
12 changes: 12 additions & 0 deletions packages/jsbattle-docs/docs/dev_guide/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ and refreshing the renderer.
* [.setRngSeed(seed)](#Simulation+setRngSeed)
* [.getRngSeed()](#Simulation+getRngSeed)
* [.getRandom()](#Simulation+getRandom)
* [.setFinishCondition(callback)](#Simulation+setFinishCondition)
* [.init(width, height)](#Simulation+init)
* [.start()](#Simulation+start)
* [.addTank(aiDefinition)](#Simulation+addTank)
Expand Down Expand Up @@ -378,6 +379,17 @@ some RNG calls could be unseeded.
### simulation.getRandom() ⇒
**Kind**: instance method of [<code>Simulation</code>](#Simulation)
**Returns**: random number from seeded rng
<a name="Simulation+setFinishCondition"></a>

### simulation.setFinishCondition(callback)
set custom condition of battle finish. Once provided callbacl return true, the simulation will be over

**Kind**: instance method of [<code>Simulation</code>](#Simulation)

| Param | Type | Description |
| --- | --- | --- |
| callback | <code>function</code> | callback determining end of the battle. It takes one argument (simulation object) and return true (stop simulation) or false (continue simulation) |

<a name="Simulation+init"></a>

### simulation.init(width, height)
Expand Down
12 changes: 12 additions & 0 deletions packages/jsbattle-engine/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ and refreshing the renderer.
* [.setRngSeed(seed)](#Simulation+setRngSeed)
* [.getRngSeed()](#Simulation+getRngSeed)
* [.getRandom()](#Simulation+getRandom)
* [.setFinishCondition(callback)](#Simulation+setFinishCondition)
* [.init(width, height)](#Simulation+init)
* [.start()](#Simulation+start)
* [.addTank(aiDefinition)](#Simulation+addTank)
Expand Down Expand Up @@ -378,6 +379,17 @@ some RNG calls could be unseeded.
### simulation.getRandom() ⇒
**Kind**: instance method of [<code>Simulation</code>](#Simulation)
**Returns**: random number from seeded rng
<a name="Simulation+setFinishCondition"></a>

### simulation.setFinishCondition(callback)
set custom condition of battle finish. Once provided callbacl return true, the simulation will be over

**Kind**: instance method of [<code>Simulation</code>](#Simulation)

| Param | Type | Description |
| --- | --- | --- |
| callback | <code>function</code> | callback determining end of the battle. It takes one argument (simulation object) and return true (stop simulation) or false (continue simulation) |

<a name="Simulation+init"></a>

### simulation.init(width, height)
Expand Down
33 changes: 21 additions & 12 deletions packages/jsbattle-engine/src/engine/Simulation.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AiWrapper from "./AiWrapper.js";
import PerformanceMonitor from "./PerformanceMonitor.js";
import UltimateBattleDescriptor from "./UltimateBattleDescriptor.js";
import seedrandom from "seedrandom";
import finishCondition from "./finishCondition.js";

/**
* Battle simulation component. Process the simulation updating all related objects
Expand Down Expand Up @@ -47,6 +48,8 @@ class Simulation {
this._onStartCallback = [];
this._timeElapsed = 0;
this._timeLimit = 30000;
this._finishCondition = finishCondition;
this._customFinishCondition = false;
this._eventStore = new EventStore();
this._nextTankId = 1;
this._nextBulletId = 1;
Expand Down Expand Up @@ -90,6 +93,15 @@ class Simulation {
return this._rng();
}

/**
* set custom condition of battle finish. Once provided callbacl return true, the simulation will be over
* @param {Function} callback - callback determining end of the battle. It takes one argument (simulation object) and return true (stop simulation) or false (continue simulation)
*/
setFinishCondition(callback) {
this._customFinishCondition = true;
this._finishCondition = callback;
}

/**
* Initialize the battle field. Must be called before any other calls
* to simulation object
Expand Down Expand Up @@ -198,7 +210,7 @@ class Simulation {
clearTimeout(self._simulationTimeout);
self._simulationTimeout = null;
}
if(self._getTeamsLeft() <= 1 || self._timeElapsed == self._timeLimit) {
if((self._timeLimit > 0 && self._timeElapsed == self._timeLimit) || self._finishCondition(self)) {
self.stop();
self._updateModel();
self._updateView();
Expand All @@ -209,7 +221,10 @@ class Simulation {
let dt = self._simulationStepDuration/self._speedMultiplier - processingTime;
dt = Math.round(dt);
for(i=0; i < self._onSimulationStepCallback.length; i++) self._onSimulationStepCallback[i]();
self._timeElapsed = Math.min(self._timeElapsed + self._simulationStepDuration, self._timeLimit);
self._timeElapsed += self._simulationStepDuration;
if(self._timeLimit > 0) {
self._timeElapsed = Math.min(self._timeElapsed, self._timeLimit);
}
if(dt > 0) {
self._callStackCount=0;
self._simulationTimeout = setTimeout(self._simulationStep.bind(self), dt);
Expand Down Expand Up @@ -255,7 +270,7 @@ class Simulation {
tank.moveTo(startSlot.x, startSlot.y);
this._tankList.push(tank);
this._allTankList.push(tank);
if(this._allTankList.length > 2) {
if(this._timeLimit > 0 && this._allTankList.length > 2) {
this._timeLimit += 2000;
}

Expand Down Expand Up @@ -387,6 +402,9 @@ class Simulation {
* @return UltimateBattleDescriptor object
*/
createUltimateBattleDescriptor() {
if(this._customFinishCondition) {
throw new Error('Cannot create UBD for battles with custom battle finish condition!');
}
return this._ultimateBattleDescriptor.clone();
}

Expand Down Expand Up @@ -553,15 +571,6 @@ class Simulation {
this._eventStore.clear();
}

_getTeamsLeft() {
let teamsLeft = 0;
for(let i in this._teamMap) {
if(!this._teamMap[i].isAlive) continue;
teamsLeft++;
}
return teamsLeft;
}

_createAiWrapper(tank, aiDefinition) {
return new AiWrapper(tank, aiDefinition);
}
Expand Down
12 changes: 12 additions & 0 deletions packages/jsbattle-engine/src/engine/finishCondition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

function finishCondition(simulaiton) {
let teamsLeft = 0;
for(let i in simulaiton._teamMap) {
if(!simulaiton._teamMap[i].isAlive) continue;
teamsLeft++;
}
return (teamsLeft <= 1);
}

export default finishCondition;
6 changes: 2 additions & 4 deletions packages/jsbattle-engine/src/examples/livecode.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
simulation = null;
}
simulation = JsBattle.createSimulation(renderer);
simulation.setFinishCondition(() => false);
simulation.timeLimit = 0;
simulation.init(900, 600);
simulation.onFinish(() => reload());
simulation.onError((err) => {
Expand All @@ -84,10 +86,6 @@
})
var ai;

ai = JsBattle.createAiDefinition();
ai.fromFile('dummy');
simulation.addTank(ai);

ai = JsBattle.createAiDefinition();
ai.fromCode('my-tank', editor.value);
simulation.addTank(ai);
Expand Down
12 changes: 12 additions & 0 deletions packages/jsbattle-engine/test/engine/Simulation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,18 @@ describe('Simulation', function() {

describe('createUltimateBattleDescriptor', function() {

it('should throw error when custom finish condition is used', function() {
let sim = createSimulation();
sim.setFinishCondition(() => false);
sim.init(600, 600);
sim.addTank(new AiDefinitionMock());
sim.addTank(new AiDefinitionMock());
sim.addTank(new AiDefinitionMock());
sim.addTank(new AiDefinitionMock());

assert.throws(() => sim.createUltimateBattleDescriptor());
});

it('should return UBD with list of AIs', function() {
let sim = createSimulation();
sim.init(600, 600);
Expand Down

0 comments on commit 441d69a

Please sign in to comment.