diff --git a/README.md b/README.md index 8c5723b..40b1096 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ # Main commands As an administrator, you may affect the system by command line API. For instance, you may obtain amount of organisms in current population or set new configuration in real time. For this, you have to open Chrome console (press `F12`) and type `man.api[.namespace].xxx()`. Where `namespace` is an optional unit or module and `xxx()` is supported command of this module. It's possible to use `desc` property to get command description. Example: `man.api.getConfig.desc`. Here are all available commands separated by namespace: - global namespace - `man.api`: - - `man.api.visualize(show:Boolean = true)` - Turns on/off visualization in browser for current instance (world). Turning visualization off, increases application speed. - - `man.api.formatCode(code:Array)` - Converts byte code array into human readable JavaScript based code. This function is low level. For using it you have to get organism's virtual machine reference and then use it's `code` property. For example: `man.api.formatCode(man.api.organisms.getOrganism('128').vm.code)`. This example will find organism with id `128` and shows his byte code. + - `man.api.visualize(show:Boolean = true)` - Turns on/off visualization in browser for current instance (world). Turning visualization off, increases application speed. You may use hot key: Ctrl-V. + - `man.api.toJS(code:Array)` - Converts byte code array into human readable JavaScript based code. This function is low level. For using it you have to get organism's virtual machine reference and then use it's `code` property. For example: `man.api.formatCode(man.api.organisms.getOrganism('128').vm.code)`. This example will find organism with id `128` and shows his byte code. - `man.api.version` - Returns current app version - `man.api.getConfig(path:String)` - Returns specified config value. First parameter is a namespace (optional) and config name. For example, to get maximum amount of organisms in current instance/world type: `man.api.getConfig('organisms.orgMaxOrgs')`. Example of organism related configs you may find [here](https://github.com/tmptrash/construct/blob/master/client/src/manager/plugins/organisms/Config.js). Other configuration parameters are located in files with name `Config.js`. - `man.api.setConfig(path:String, value:Any)` - Sets configuration value in real time. Opposite to `getConfig()`. diff --git a/client/src/manager/Manager.js b/client/src/manager/Manager.js index 190f614..0ef83c1 100644 --- a/client/src/manager/Manager.js +++ b/client/src/manager/Manager.js @@ -13,7 +13,7 @@ * @author flatline */ const Observer = require('./../../../common/src/Observer'); -const Queue = require('./../../../common/src/Queue'); +const FastArray = require('./../../../common/src/FastArray'); const Config = require('./../share/Config').Config; const OConfig = require('./plugins/organisms/Config'); const Plugins = require('./Plugins'); @@ -23,7 +23,8 @@ const Console = require('./../share/Console'); const Helper = require('./../../../common/src/Helper'); const World = require('./../view/World').World; const WEVENTS = require('./../view/World').EVENTS; -const Canvas = require('./../view/Canvas'); +const Canvas = require('./../view/Canvas').Canvas; +const CEVENTS = require('./../view/Canvas').EVENTS; /** * {Function} Shortcut to the datetime stamp getter */ @@ -40,11 +41,11 @@ class Manager extends Observer { const width = Config.worldWidth; const height = Config.worldHeight; /** - * {Queue} Queue of organisms in current Manager. Should be used by plugins. - * Organisms plugin walk through this queue and run organism's code all the + * {FastArray} Array of organisms in current Manager. Should be used by plugins. + * Organisms plugin walk through this array and run organism's code all the * time. */ - this.organisms = new Queue(); + this.organisms = new FastArray(OConfig.orgMaxOrgs); /** * {Object} positions of organisms in a world. Is used to prevent collisions * and track all world objects @@ -71,6 +72,7 @@ class Manager extends Observer { */ this._codeRuns = 0; + this._sharedObj = {}; this._world = new World(Config.worldWidth, Config.worldHeight); this._canvas = hasView && new Canvas(Config.worldWidth, Config.worldHeight) || null; this._visualized = true; @@ -101,6 +103,7 @@ class Manager extends Observer { }); } + get sharedObj() {return this._sharedObj} get world() {return this._world} get canvas() {return this._canvas} get clientId() {return this._clientId} @@ -152,8 +155,9 @@ class Manager extends Observer { * Is called after all iterations * @param {Number} counter Global counter as an analog of time * @param {Number} stamp UNIX time stamp + * @param {Object} sharedObj Shared manager's object */ - onLoop(counter, stamp) { + onLoop(counter, stamp, sharedObj) { this.fire(EVENTS.LOOP); } @@ -269,12 +273,15 @@ class Manager extends Observer { for (i = counter, amount = counter + amount; i < amount; i++) { this.onIteration(i, TIMER()); } - this.onLoop(this._counter = i, TIMER()); + this.onLoop(this._counter = i, TIMER(), this._sharedObj); + Helper.delay((1 - Config.worldSpeed) * 100); this.zeroTimeout(this._onLoopCb); } _addHandlers() { - this._hasView && this._world.on(WEVENTS.DOT, this._onDot.bind(this)); + if (!this._hasView) {return} + this._world.on(WEVENTS.DOT, this._onDot.bind(this)); + this._canvas.on(CEVENTS.VISUALIZE, this._visualize.bind(this)); } _visualize(visualized = true) { diff --git a/client/src/manager/ManagerSpec.js b/client/src/manager/ManagerSpec.js index 254c847..ce0c8ca 100644 --- a/client/src/manager/ManagerSpec.js +++ b/client/src/manager/ManagerSpec.js @@ -19,7 +19,7 @@ describe("client/src/manager/Manager", () => { const port = SConfig.port; const maxConns = SConfig.maxConnections; const startOrgs = OConfig.orgStartAmount; - const energyCheck = Config.worldEnergyCheckPeriod; + const energyCheck = Config.checkPeriod; const emp = () => {}; let error; @@ -49,7 +49,7 @@ describe("client/src/manager/Manager", () => { SConfig.port = Config.serverPort; SConfig.maxConnections = 100; OConfig.orgStartAmount = 0; - Config.worldEnergyCheckPeriod = 0; + Config.checkPeriod = 0; error = Console.error; warn = Console.warn; @@ -80,7 +80,7 @@ describe("client/src/manager/Manager", () => { SConfig.port = port; SConfig.maxConnections = maxConns; OConfig.orgStartAmount = startOrgs; - Config.worldEnergyCheckPeriod = energyCheck; + Config.checkPeriod = energyCheck; }); describe('Manager creation/destroy', () => { @@ -91,7 +91,7 @@ describe("client/src/manager/Manager", () => { }); it("Checking manager creation and it's properties", (done) => { const man = new Manager(false); - expect(man.organisms.size).toBe(0); + expect(man.organisms.length).toBe(0); expect(man.positions.length).toBe(Config.worldWidth); expect(man.codeRuns).toBe(0); expect(!!man.api.version).toBe(true); @@ -288,18 +288,18 @@ describe("client/src/manager/Manager", () => { console.log = () => {}; expect(man1.clientId).toBe(null); expect(man2.clientId).toBe(null); - expect(man1.organisms.size).toBe(0); - expect(man2.organisms.size).toBe(0); + expect(man1.organisms.length).toBe(0); + expect(man2.organisms.length).toBe(0); man1.on(EVENTS.LOOP, () => { if (blocked) {return} - expect(man1.organisms.size).toBe(1); + expect(man1.organisms.length).toBe(1); if (iterated1 && iterated2) {destroy(); return} iterated1 = true; }); man2.on(EVENTS.LOOP, () => { if (blocked) {return} - expect(man2.organisms.size).toBe(1); + expect(man2.organisms.length).toBe(1); if (iterated2 && iterated1) {destroy(); return} iterated2 = true; }); @@ -335,10 +335,10 @@ describe("client/src/manager/Manager", () => { OConfig.orgStartAmount = 1; OConfig.orgRainMutationPeriod = 0; OConfig.orgCloneMutationPercent = 0; - expect(man.organisms.size).toBe(0); + expect(man.organisms.length).toBe(0); man.on(EVENTS.LOOP, () => { if (iterated) {return} - expect(man.organisms.size).toBe(1); + expect(man.organisms.length).toBe(1); man.stop(() => { man.destroy(() => { OConfig.orgCloneMutationPercent = percent; @@ -399,10 +399,10 @@ describe("client/src/manager/Manager", () => { man1.on(EVENTS.LOOP, () => { if (iterated1 > 0 && iterated2 > 0 && org1 === null) { - org1 = man1.organisms.first.val; + org1 = man1.organisms.added(); org1.vm.insertLine(); org1.vm.updateLine(0, 0b00001011000000000000000000000000); // onStepRight() - } else if (man2.organisms.size === 2) { + } else if (man2.organisms.length === 2) { destroy(); } if (iterated1 > 10000) {throw 'Error sending organism between Managers'} @@ -466,7 +466,7 @@ describe("client/src/manager/Manager", () => { man1.on(EVENTS.LOOP, () => { if (iterated1 > 0 && iterated2 > 0 && org1 === null && org2 !== null) { - org1 = man1.organisms.first.val; + org1 = man1.organisms.added(); org1.vm.code.push(0b00001011000000000000000000000000); // onStepRight() man1.on(EVENTS.STEP_OUT, () => { expect(doneInc < 3).toBe(true); @@ -474,14 +474,14 @@ describe("client/src/manager/Manager", () => { }); man2.on(EVENTS.STEP_IN, () => { ++doneInc; - expect(man1.organisms.size).toBe(1); - expect(man1.organisms.first.val.x).toBe(0); + expect(man1.organisms.length).toBe(1); + expect(man1.organisms.added().x).toBe(0); }); } else if (org1 !== null && org2 !== null && doneInc === 2) { - expect(man1.organisms.size).toBe(1); - expect(man1.organisms.first.val.x).toBe(0); - expect(man2.organisms.size).toBe(1); - expect(man2.organisms.first.val.x).toBe(0); + expect(man1.organisms.length).toBe(1); + expect(man1.organisms.added().x).toBe(0); + expect(man2.organisms.length).toBe(1); + expect(man2.organisms.added().x).toBe(0); destroy(); doneInc++; } @@ -489,7 +489,7 @@ describe("client/src/manager/Manager", () => { iterated1++; }); man2.on(EVENTS.LOOP, () => { - !iterated2 && (org2 = man2.organisms.first.val); + !iterated2 && (org2 = man2.organisms.added()); iterated2++; }); @@ -554,10 +554,10 @@ describe("client/src/manager/Manager", () => { man1.on(EVENTS.LOOP, () => { if (iterated1 > 0 && iterated2 > 0 && org1 === null) { - expect(man2.organisms.size).toBe(1); - org1 = man1.organisms.first.val; + expect(man2.organisms.length).toBe(1); + org1 = man1.organisms.added(); org1.vm.code.push(0b00001011000000000000000000000000); // onStepRight() - } else if (man2.organisms.size === 2) { + } else if (man2.organisms.length === 2) { destroy(); } if (iterated1 > 10000) {throw 'Error sending organism between Servers'} @@ -765,8 +765,8 @@ describe("client/src/manager/Manager", () => { // // man1.on(EVENTS.LOOP, () => { // if (iterated1 > 0 && iterated2 > 0 && org1 === null) { -// expect(man2.organisms.size).toBe(1); -// org1 = man1.organisms.first.val; +// expect(man2.organisms.length).toBe(1); +// org1 = man1.organisms.added(); // org1.vm.code.push(0b00001011000000000000000000000000); // onStepRight() // man1.on(EVENTS.KILL, () => destroyFlag = true); // man1.on(EVENTS.STEP_IN, () => stepInBack = true); @@ -840,13 +840,13 @@ describe("client/src/manager/Manager", () => { // // man1.on(EVENTS.LOOP, () => { // if (iterated1 > 0 && org1 === null) { -// org1 = man1.organisms.first.val; +// org1 = man1.organisms.added(); // org1.vm.code.push(0b00001011000000000000000000000000); // onStepRight() // man1.on(EVENTS.KILL, () => destroyFlag = true); // man1.on(EVENTS.STEP_IN, () => stepInBack = true); // } else if (destroyFlag && stepInBack) { // stepInBack = false; -// expect(man1.organisms.size).toBe(1); +// expect(man1.organisms.length).toBe(1); // destroy(); // } // if (iterated1 > 10000) {throw 'Error sending organism between Servers'} @@ -897,7 +897,7 @@ describe("client/src/manager/Manager", () => { // testQ(done, // [server, SEVENTS.RUN, () => server.run(), () => {man1.run(() => man2.run(() => man3.run(() => waitObj.done = true)))}], // [waitObj], -// [man1, EVENTS.LOOP, emp, () => man1.organisms.first.val.vm.code.push(0b00001011000000000000000000000000)], // onStepRight() +// [man1, EVENTS.LOOP, emp, () => man1.organisms.added().vm.code.push(0b00001011000000000000000000000000)], // onStepRight() // [man3, EVENTS.STEP_IN, emp, () => destroy()] // ); // }); diff --git a/client/src/manager/plugins/Energy.js b/client/src/manager/plugins/Energy.js deleted file mode 100644 index 938057a..0000000 --- a/client/src/manager/plugins/Energy.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Manager's plugin, which tracks amount of energy in a world and updates it. - * - * @author flatline - */ -const Helper = require('./../../../../common/src/Helper'); -const Config = require('./../../share/Config').Config; -const Organism = require('./../../manager/plugins/organisms/Organism').Organism; -const EVENTS = require('./../../share/Events').EVENTS; - -class Energy { - constructor(manager) { - this._manager = manager; - this._onIterationCb = this._onIteration.bind(this); - - Helper.override(manager, 'onIteration', this._onIterationCb); - } - - destroy() { - Helper.unoverride(this._manager, 'onIteration', this._onIterationCb); - this._manager = null; - this._onIterationCb = null; - } - - _onIteration(counter) { - if (Config.worldEnergyCheckPeriod === 0 || counter % Config.worldEnergyCheckPeriod !== 0) {return} - - let energy = this._getEnergyPercent(); - - this._manager.fire(EVENTS.WORLD_ENERGY, energy); - if (energy > Config.worldEnergyMinPercent) {return} - - const maxEnergy = Config.worldEnergyMaxPercent * Config.worldWidth * Config.worldHeight; - let amount = 0; - let attempts = 0; - while (amount < maxEnergy && attempts < 100) { - const startAmount = amount; - amount = this._addEnergyBlock(amount, maxEnergy); - if (amount === startAmount) { - attempts++; - } else { - attempts = 0; - } - } - } - - _addEnergyBlock(amount, maxEnergy) { - const width = Config.worldWidth; - const height = Config.worldHeight; - const color = Organism.getColor(Config.worldEnergyColorIndex); - let block = Config.worldEnergyBlockSize; - const world = this._manager.world; - let x = Helper.rand(width); - let y = Helper.rand(height); - - for (let i = 0; i < block; i++) { - x = x + Helper.rand(3) - 1; - y = y + Helper.rand(3) - 1; - if (x < 0 || x >= width || y < 0 || y >= height) { - return amount - } - if (world.isFree(x, y)) { - world.setDot(x, y, color); - if (++amount > maxEnergy) { - return amount - } - } - } - - return amount; - } - - _getEnergyPercent() { - let energy = 0; - const world = this._manager.world; - const width = Config.worldWidth; - const height = Config.worldHeight; - - for (let x = 0; x < width; x++) { - for (let y = 0; y < height; y++) { - if (world.getDot(x, y) > 0) {++energy} - } - } - - return energy / (width * height); - } -} - -module.exports = Energy; \ No newline at end of file diff --git a/client/src/manager/plugins/Stones.js b/client/src/manager/plugins/Stones.js deleted file mode 100644 index 344b717..0000000 --- a/client/src/manager/plugins/Stones.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Manager's plugin, which add stones to the world - * - * @author flatline - */ -const Helper = require('./../../../../common/src/Helper'); -const Config = require('./../../share/Config').Config; -const Organism = require('./../../manager/plugins/organisms/Organism').Organism; -const OBJECT_TYPES = require('./../../view/World').OBJECT_TYPES; - -const STONE_BLOCK_SIZE = 300; -// -// We have to add stone type to global types storage -// -OBJECT_TYPES.TYPE_STONE = -(Object.keys(OBJECT_TYPES).length + 1); - -class Stones { - constructor(manager) { - this._manager = manager; - this._onLoopCb = this._onLoop.bind(this); - - Helper.override(manager, 'onLoop', this._onLoopCb); - } - - destroy() { - Helper.unoverride(this._manager, 'onLoop', this._onLoopCb); - this._manager = null; - this._onLoopCb = null; - } - - _onLoop(counter) { - if (counter > 1 || Config.worldStonesPercent === .0) {return} - - const stones = Config.worldStonesPercent * Config.worldWidth * Config.worldHeight; - let amount = 0; - let attempts = 0; - while (amount < stones && attempts < 100) { - const startAmount = amount; - amount = this._addStoneBlock(amount, stones); - if (startAmount === amount) { - attempts++; - } else { - attempts = 0; - } - } - } - - _addStoneBlock(amount, stones) { - const width = Config.worldWidth; - const height = Config.worldHeight; - const color = Organism.getColor(Config.worldStoneColorIndex); - const man = this._manager; - const world = man.world; - const stone = OBJECT_TYPES.TYPE_STONE; - let x = Helper.rand(width); - let y = Helper.rand(height); - - for (let i = 0; i < STONE_BLOCK_SIZE; i++) { - x = x + Helper.rand(3) - 1; - y = y + Helper.rand(3) - 1; - if (x < 0 || x >= width || y < 0 || y >= height) {return amount} - if (world.isFree(x, y)) { - if (world.setDot(x, y, color)) { - man.positions[x][y] = stone; - if (++amount >= stones) {return amount} - } - } - } - - return amount; - } -} - -module.exports = Stones; \ No newline at end of file diff --git a/client/src/manager/plugins/energy/Config.js b/client/src/manager/plugins/energy/Config.js new file mode 100644 index 0000000..5386d96 --- /dev/null +++ b/client/src/manager/plugins/energy/Config.js @@ -0,0 +1,42 @@ +/** + * Configuration of Energy plugin + * + * @author flatline + */ +const Helper = require('./../../../../../common/src/Helper'); +const GConfig = require('./../../../share/Config').Config; + +const Config = { + /** + * {Number} An amount of iteration, after which we have to check world energy + * percent. May be 0 if you want to disable energy generation + */ + checkPeriod: 3000, + /** + * {Number} size of one clever energy block in dots + */ + blockSize: 2, + /** + * {Number} Index of energy color. Starts from 0. Ends with 4000. See Organism.MAX_COLORS + * constant for details + */ + colorIndex: GConfig.worldEnergyColor, + /** + * {Number} Maximum amount of energy dots + */ + maxValue: .8 * GConfig.worldEnergy, + /** + * {Number} Opposite to maxValue. Minimum amount of energy dots + */ + minValue: .7 * GConfig.worldEnergy, + /** + * {Array|null} In case of array you may set sequence of four values: x,y,w,h. + * They means x,y coordinates, width, height of places with high energy concentration. + * Example: assume, that resolution 1920, 1080. [1920 / 2, 1080 / 2, 1920, 1080]. In this + * example all the screen will be filled by energy. As many values by four, you set as + * many places with energy will be created. In case of null, grouping will be disabled. + */ + groups: [1920 + 1920/1.5, 1080 * 2, 500, 500, 1920 * 3 - 1920/1.5, 1080 * 2, 500, 500] +}; + +module.exports = Config; \ No newline at end of file diff --git a/client/src/manager/plugins/energy/Energy.js b/client/src/manager/plugins/energy/Energy.js new file mode 100644 index 0000000..8e8a15b --- /dev/null +++ b/client/src/manager/plugins/energy/Energy.js @@ -0,0 +1,44 @@ +/** + * Manager's plugin, which tracks amount of energy in a world and updates it. + * + * @author flatline + */ +const Dots = require('./../../../share/Dots'); +const EConfig = require('./Config'); +const Helper = require('./../../../../../common/src/Helper'); +const EVENTS = require('./../../../share/Events').EVENTS; + +class Energy extends Dots { + constructor(manager) { + super(manager, EConfig, { + addOnce : false, + compareCb: (x,y) => manager.world.getDot(x, y) > 0 && manager.positions[x][y] === 0, + checkMin : (val) => val + manager.sharedObj.orgEnergy + manager.sharedObj.objectsEnergy < EConfig.minValue + }); + this._color = Helper.getColor(EConfig.colorIndex); + this._energy = 0; + this._sharedObj = manager.sharedObj; + this._sharedObj.energy = 0; + } + + onIteration(counter) { + const energy = super.onIteration(counter); + if (energy !== false) { + this.manager.fire(EVENTS.WORLD_ENERGY, energy / this._color); + this._energy = energy; + } + } + + /** + * Override of Manager.onLoop() method. Stores amount of energy in sharedObj + * @param {Number} counter Global counter. Time analog + * @param {Number} stamp Time stamp + * @param {Object} sharedObj Shared object of the manager + */ + onLoop(counter, stamp, sharedObj) { + super.onLoop(counter, stamp, sharedObj); + sharedObj.energy = this._energy; + } +} + +module.exports = Energy; \ No newline at end of file diff --git a/client/src/manager/plugins/objects/Config.js b/client/src/manager/plugins/objects/Config.js new file mode 100644 index 0000000..0812cb9 --- /dev/null +++ b/client/src/manager/plugins/objects/Config.js @@ -0,0 +1,36 @@ +/** + * Configuration of Objects plugin + * + * @author flatline + */ +const GConfig = require('./../../../share/Config').Config; + +const Config = { + /** + * {Number} An amount of iteration, after which we have to check world energy + * objects percent. May be 0 if you want to disable energy objects generation + */ + checkPeriod: 4000, + /** + * {Number} size of one energy objects block in dots + */ + blockSize: 4, + /** + * {Number} Maximum amount of object dots + */ + maxValue: .8 * GConfig.worldEnergy, + /** + * {Number} Opposite to maxValue. Minimum amount of object dots + */ + minValue: .7 * GConfig.worldEnergy, + /** + * {Array|null} In case of array you may set sequence of four values: x,y,w,h. + * They means x,y coordinates, width, height of places with high objects concentration. + * Example: assume, that resolution 1920, 1080. [1920 / 2, 1080 / 2, 1920, 1080]. In this + * example all the screen will be filled by objects. As many values by four, you set as + * many places with objects will be created. In case of null, grouping will be disabled. + */ + groups: [1920 * 2, 1080 * 2, 300, 300] +}; + +module.exports = Config; \ No newline at end of file diff --git a/client/src/manager/plugins/objects/Objects.js b/client/src/manager/plugins/objects/Objects.js new file mode 100644 index 0000000..475512b --- /dev/null +++ b/client/src/manager/plugins/objects/Objects.js @@ -0,0 +1,60 @@ +/** + * Manager's plugin, which add energy objects to the world. Such objects + * may be joined together to get more energy + * + * @author flatline + */ +const Dots = require('./../../../share/Dots'); +const Helper = require('./../../../../../common/src/Helper'); +const OConfig = require('./Config'); +const OBJECT_TYPES = require('./../../../view/World').OBJECT_TYPES; +// +// We have to add object types to global types storage +// +OBJECT_TYPES.TYPE_ENERGY0 = -(Object.keys(OBJECT_TYPES).length + 1); +OBJECT_TYPES.TYPE_ENERGY1 = -(Object.keys(OBJECT_TYPES).length + 1); +OBJECT_TYPES.TYPE_ENERGY2 = -(Object.keys(OBJECT_TYPES).length + 1); +OBJECT_TYPES.TYPE_ENERGY3 = -(Object.keys(OBJECT_TYPES).length + 1); +OBJECT_TYPES.TYPE_ENERGY4 = -(Object.keys(OBJECT_TYPES).length + 1); + +const GET_COLOR = (index) => Helper.getColor(8500 + (Math.abs(index) + OBJECT_TYPES.TYPE_ENERGY0) * 500); +const COLOR_INDEX = OBJECT_TYPES.TYPE_ENERGY0; +const COLOR_RGB = GET_COLOR(COLOR_INDEX); + +class Objects extends Dots { + static getColor(index) { + return GET_COLOR(index); + } + + constructor(manager) { + super(manager, OConfig, { + addOnce : false, + compareCb : (x, y) => manager.world.getDot(x, y) > 0 && manager.positions[x][y] >= OBJECT_TYPES.TYPE_ENERGY4 && manager.positions[x][y] <= OBJECT_TYPES.TYPE_ENERGY0, + colorCb : ( ) => COLOR_RGB, + checkMin : (val ) => val + manager.sharedObj.orgEnergy + manager.sharedObj.energy < OConfig.minValue, + setCb : (x, y) => manager.positions[x][y] = COLOR_INDEX + }); + this._energy = 0; + this._sharedObj = manager.sharedObj; + this._sharedObj.objectsEnergy = 0; + } + + onIteration(counter) { + const energy = super.onIteration(counter); + if (energy !== false) { + this._energy = energy; + } + } + /** + * Override of Manager.onLoop() method. Stores amount of energy in sharedObj + * @param {Number} counter Global counter. Time analog + * @param {Number} stamp Time stamp + * @param {Object} sharedObj Shared object of the manager + */ + onLoop(counter, stamp, sharedObj) { + super.onLoop(counter, stamp, sharedObj); + sharedObj.objectsEnergy = this._energy; + } +} + +module.exports = Objects; \ No newline at end of file diff --git a/client/src/manager/plugins/organisms/Config.js b/client/src/manager/plugins/organisms/Config.js index 931954f..903d5e9 100644 --- a/client/src/manager/plugins/organisms/Config.js +++ b/client/src/manager/plugins/organisms/Config.js @@ -3,24 +3,36 @@ * * @author flatline */ -const Config = { +const Helper = require('./../../../../../common/src/Helper'); +const GConfig = require('./../../../share/Config').Config; + +const ENERGY_VALUE = Helper.getColor(GConfig.worldEnergyColor); + +const Config = { /** + * @constant * {Number} Max value, which we may use in orgMutationProbs array. We may use * range: [0...ORG_MUTATION_PROBS_MAX_VAL] including these values */ ORG_MUTATION_PROBS_MAX_VAL: 100, + /** + * @constant + * {Number} Amount of bits for storing operator. This is first XX bits + * in a number. Operator is a type of operation. e.g.: var, func, step, if,... + */ + CODE_BITS_PER_OPERATOR: 6, /** * {Array} Array of operators weights in energy equivalent. Every value of this * array is bind to special operator run on VM. The same sequence should be * implemented. See Operators.operators getter for details. Values may be float. */ orgOperatorWeights: [ - .0001, .0001, .00001, .00000001, .0001, // var, const, if, loop, operator, - .00001, 2, 2, 2, 2, // lookAt, eatLeft, eatRight, eatUp, eatDown, - 2, 2, 2, 2, // stepLeft, stepRight, stepUp, stepDown, - .001, .001, // fromMem, toMem, - .0001, .0001, // myX, myY, - .001, .001, .001, .001 // checkLeft, checkRight, checkUp, checkDown + .0001, .0001, .0001, .0001, .0001, // var, const, if, loop, operator, + .00001, .00001, .00001, .00001, // func, funcCall, return, bracket + .0001, .0001, .0001, // toMem, fromMem, rand, + .000001, .01, .0001, .0001, .0001, // lookAt, step, dir, myX, myY, + .01, .00001, .0001, .0001, // eat, put, energy, pick, + .000001, .000001, .00001, .0001, .0001, .0001 // say, listen, check, myEnergy, myAge, myDir ], /** * {Array} Probabilities which used, when mutator decides what to do: @@ -52,18 +64,18 @@ const Config = { * {Number} Minimum age for cloning. Before that, cloning is impossible. It should * be less then orgAlivePeriod config */ - orgCloneMinAge: 3000, + orgCloneMinAge: 1000, /** * {Number} Minimum energy for cloning */ - orgCloneMinEnergy: 20000000, + orgCloneMinEnergy: 8 * ENERGY_VALUE, /** * {Boolean} If true, then random organism will be killed after new one has * cloned and amount of organisms is greater then orgMaxOrgs config. false * mean, that new organism will not be cloned, if amount of organisms is >= * orgMaxOrgs config. */ - orgKillOnClone: true, + orgKillOnClone: false, /** * {Number} Amount of iterations between tournament. During tournament one * organism (looser) will be killed @@ -74,7 +86,7 @@ const Config = { * do mutations according to orgRainMutationPercent config. If 0, then * mutations wilsabled. Should be less then ORGANISM_MAX_MUTATION_PERIOD */ - orgRainMutationPeriod: 100, + orgRainMutationPeriod: 500, /** * {Number} Percent of mutations from code size. 0 is a possible value if * we want to disable mutations. Should be less then 1.0 (1.0 === 100%) @@ -90,17 +102,27 @@ const Config = { * {Number} Amount of iterations, after which crossover will be applied * to random organisms. May be set to 0 to turn crossover off */ - orgCrossoverPeriod: 4000, + orgCrossoverPeriod: 500, /** * {Number} Period of iterations for creation of random organisms. Set it to 0 * to turn off this feature */ - orgRandomOrgPeriod: 5000, + orgRandomOrgPeriod: 600, /** * {Number} Amount of iterations when organism is alive. It will die after * this period. If 0, then will not be used and organism may leave forever */ - orgAlivePeriod: 0, + orgAlivePeriod: 30000, + /** + * {Number} Maximum energy organism may reach collecting energy + */ + orgMaxEnergy: 20 * ENERGY_VALUE, + /** + * {Number} Amount of energy for first organisms. They are like Adam and + * Eve. It means that these empty (without vm) organism were created + * by operator and not by evolution. + */ + orgStartEnergy: 20 * ENERGY_VALUE, /** * {Number} Size of organism stack (internal memory) in bits. Real amount of * organism's internal memory will be 2^orgMemBits. Example: if orgMemBits=3, @@ -126,27 +148,41 @@ const Config = { * try to clone itself, when entire amount of organisms are equal * this value, the cloning will not happen. */ - orgMaxOrgs: 1000, + orgMaxOrgs: 10000, /** * {Number} Amount of organisms we have to create on program start */ - orgStartAmount: 1000, + orgStartAmount: 10000, /** - * {Number} Amount of energy for first organisms. They are like Adam and - * Eve. It means that these empty (without vm) organism were created - * by operator and not by evolution. + * {Array} Array of four elements: x,y,width,height. These values means + * position (x,y) and square (width,height) of high density organisms area. + * You may set this value to empty array to create organisms randomly in + * whole world. + */ + orgPosition: [1920 + 1920/1.5, 1080 * 2, 500, 500, 1920 * 3 - 1920/1.5, 1080 * 2, 500, 500], + /** + * {Boolean} true means, that code size affects amount of grabbed energy from + * organism by the system on every code line run + */ + orgSizeAffectsEnergy: false, + /** + * {Number} If organism reach this limit of amount of vm lines, then codeSizeCoef + * will be used during it's energy grabbing by system. We use this approach, + * because our CPU's are slow and organisms with big codes are very slow. But + * it's possible for organisms to go outside the limit by inventing new + * effective mechanisms of energy obtaining. */ - orgStartEnergy: 90000000, + codeMaxSize: 100, /** * {Number} Amount of bits for storing a numeric constant inside byte code */ - codeConstBits: 16, + codeConstBits: 8, /** * {Number} The value from -X/2 to X/2, which is used for setting * default value, while organism is delivering. So, if the value is * 1000, then range will be: -500..500 */ - codeVarInitRange: 500, + codeVarInitRange: 1000, /** * {Number} This value is amount of code lines, which will be run for one * organism without interruption by one VM. Set this value to value bigger @@ -157,30 +193,12 @@ const Config = { * {Number} Amount of bits per one variable. It affects maximum value, * which this variable may contain. This value shouldn't be less then 2. */ - codeBitsPerVar: 4, - /** - * {Number} Amount of bits for storing operator. This is first XX bits - * in a number. - */ - codeBitsPerOperator: 8, - /** - * {Number} Amount of bits, which stores maximum block length. Under block - * length we mean maximum amount of lines in one block like if, for,... - */ - codeBitsPerBlock: 10, + codeBitsPerVar: 3, /** * {Number} Amount of iterations between calls to V8 event loop. See * Manager._initLoop(), Manager.run() methods for details. */ - codeIterationsPerOnce: 100, - /** - * {Number} If organism reach this limit of amount of vm lines, then codeSizeCoef - * will be used during it's energy grabbing by system. We use this approach, - * because our CPU's are slow and organisms with big codes are very slow. But - * it's possible for organisms to go outside the limit by inventing new - * effective mechanisms of energy obtaining. - */ - codeMaxSize: 300 + codeIterationsPerOnce: 100 }; module.exports = Config; \ No newline at end of file diff --git a/client/src/manager/plugins/organisms/Mutator.js b/client/src/manager/plugins/organisms/Mutator.js index e7152a5..e8177c0 100644 --- a/client/src/manager/plugins/organisms/Mutator.js +++ b/client/src/manager/plugins/organisms/Mutator.js @@ -39,20 +39,20 @@ class Mutator { */ static _onSmallChange(org) { const rand = Helper.rand; - const vm = org.vm; + const vm = org.vm; const index = rand(vm.size); const rnd = rand(2); // // Toggles operator bits only // if (rnd === 0) { - vm.updateLine(index, Num.setOperator(vm.getLine(index), rand(vm.operators.operators.length))); + vm.updateLine(index, Num.setOperator(vm.getLine(index), rand(vm.operators.length))); org.changes += Num.BITS_PER_OPERATOR; // // Toggles specified bit, except operator bits // } else { - vm.updateLine(index, vm.getLine(index) ^ (1 << rand(Num.VAR_BITS_OFFS - 1))); + vm.updateLine(index, (vm.getLine(index) ^ (1 << rand(Num.VAR_BITS_OFFS - 1))) >>> 0); org.changes++; } } diff --git a/client/src/manager/plugins/organisms/Organism.js b/client/src/manager/plugins/organisms/Organism.js index c1a7ff2..9ca5536 100644 --- a/client/src/manager/plugins/organisms/Organism.js +++ b/client/src/manager/plugins/organisms/Organism.js @@ -11,6 +11,8 @@ const Helper = require('./../../../../../common/src/Helper'); const OConfig = require('./../../../manager/plugins/organisms/Config'); const EVENT_AMOUNT = require('./../../../share/Events').EVENT_AMOUNT; const VM = require('./../../../vm/VM'); +const OFFSX = require('./../../../../../common/src/Directions').OFFSX; +const OFFSY = require('./../../../../../common/src/Directions').OFFSY; const DESTROY = 0; const CLONE = 1; @@ -25,28 +27,13 @@ const ORG_EVENTS = { ITERATION }; -const MAX_COLORS = 4000; +const MAX_COLORS = 10000; +const ORG_START_COLOR = 3000; +const ORG_END_COLOR = 4000; +const ORG_COLORS = ORG_START_COLOR - ORG_END_COLOR; const UPDATE_COLOR_PERIOD = 50; class Organism extends Observer { - /** - * Returns color by index. Index may be increased without limit - * @param {Number} index Color index. Starts from 0 till Number.MAX_VALUE - * @returns {Number} RGB value - */ - static getColor(index) { - // - // Maximum possible colors for this value is MAX_COLORS - // - const frequency = 0.0005; - - const r = Math.sin(frequency * index ) * 127 + 128; - const g = Math.sin(frequency * index + 2) * 127 + 128; - const b = Math.sin(frequency * index + 4) * 127 + 128; - - return r << 16 | g << 8 | b; - } - /** * Is called before every run. Should return true, if everything * is okay and we don't need to interrupt running. If true, then @@ -86,13 +73,14 @@ class Organism extends Observer { this._iterations = -1; this._changes = 0; this._item = item; + this._msg = 0; this._energyChanges = 0; - this._fnId = 0; } get id() {return this._id} get x() {return this._x} get y() {return this._y} + get dir() {return this._dir} get item() {return this._item} get iterations() {return this._iterations} get changes() {return this._changes} @@ -103,15 +91,24 @@ class Organism extends Observer { get startEnergy() {return this._startEnergy} get color() {return this._color} get mem() {return this._mem} - get posId() {return Helper.posId(this._x, this._y)} + get msg() {return this._msg} + get dirX() {return this._x + OFFSX[this._dir]} + get dirY() {return this._y + OFFSY[this._dir]} set x(newX) {this._x = newX} set y(newY) {this._y = newY} set mutationPeriod(m) {this._mutationPeriod = m} set mutationPercent(p) {this._mutationPercent = p} - set energy(e) {if (this.vm !== null) { this._energy = e; ++this._energyChanges % UPDATE_COLOR_PERIOD === 0 && this._updateColor()}} + set energy(e) { + if (this.vm !== null) { + this._energy = e; + ++this._energyChanges % UPDATE_COLOR_PERIOD === 0 && this._updateColor(); + } + } set startEnergy(e) {this._startEnergy = e} set changes(c) {this._changes = c} + set dir(d) {this._dir = d} + set msg(m) {this._msg = m} /** * Runs one code iteration (amount of lines set in Config.codeYieldPeriod) and returns @@ -122,12 +119,14 @@ class Organism extends Observer { this._iterations++; if (this.onBeforeRun() === false) {return true} const lines = this._energy > 0 ? this.onRun() : 0; - this._updateEnergy(); - if (this._energy > 0) { - this._updateClone(); + this.fire(ITERATION, lines, this); + if (this._iterations % 10 === 0) { + this._updateEnergy(); if (this._energy > 0) { - this.fire(ITERATION, lines, this); - this._energy > 0 && this._updateAge(); + this._updateClone(); + if (this._energy > 0) { + this._energy > 0 && this._updateAge(); + } } } @@ -146,7 +145,6 @@ class Organism extends Observer { changes : this._changes, // 'item' will be added after insertion iterations : this._iterations, - fnId : this._fnId, vm : this.vm.serialize(), energy : this._energy, startEnergy : this._startEnergy, @@ -154,7 +152,8 @@ class Organism extends Observer { mutationProbs : this._mutationProbs, mutationPeriod : this._mutationPeriod, mutationPercent : this._mutationPercent, - mem : this.mem.slice() + mem : this.mem.slice(), + dir : this._dir }; return JSON.stringify(json); @@ -174,7 +173,6 @@ class Organism extends Observer { this._changes = json.changes; // 'item' will be added after insertion this._iterations = json.iterations; - this._fnId = json.fnId; this.vm.unserialize(json.vm); this._energy = json.energy; this._startEnergy = json.startEnergy; @@ -183,6 +181,7 @@ class Organism extends Observer { this._mutationPeriod = json.mutationPeriod; this._mutationPercent = json.mutationPercent; this._mem = json.mem.slice(); + this._dir = json.dir; } // TODO: describe fitness in details @@ -192,7 +191,7 @@ class Organism extends Observer { //return (OConfig.codeMaxSize + 1 - this.vm.size) * (this._energy - this._startEnergy) * (this._changes || 1); //return (OConfig.codeMaxSize + 1 - this.vm.size) * (this._energy - this._startEnergy); //return (OConfig.codeMaxSize + 1 - this.vm.size) * ((this._energy - this._startEnergy) / this._iterations); - return this._energy / (this.vm.size || 1); + return this._energy; } destroy() { @@ -215,11 +214,12 @@ class Organism extends Observer { this.vm = new VM(this, this._operatorCls, OConfig.orgOperatorWeights); this._energy = OConfig.orgStartEnergy; this._startEnergy = OConfig.orgStartEnergy; - this._color = Organism.getColor(MAX_COLORS); + this._color = Helper.getColor(ORG_END_COLOR); this._mutationProbs = OConfig.orgMutationProbs.slice(); this._mutationPeriod = OConfig.orgRainMutationPeriod; this._mutationPercent = OConfig.orgRainMutationPercent; this._mem = new Array(Math.pow(2, OConfig.orgMemBits)); + this._dir = Helper.rand(OFFSX.length); _fill(this._mem, 0); } @@ -233,14 +233,15 @@ class Organism extends Observer { this._mutationPeriod = parent.mutationPeriod; this._mutationPercent = parent.mutationPercent; this._mem = parent.mem.slice(); + this._dir = parent.dir; } _updateColor() { - this._color = Organism.getColor(OConfig.orgAlivePeriod === 0 ? MAX_COLORS : this._iterations * (MAX_COLORS / OConfig.orgAlivePeriod)); + this._color = Helper.getColor(OConfig.orgAlivePeriod === 0 ? ORG_END_COLOR : this._iterations * (ORG_COLORS / OConfig.orgAlivePeriod) + ORG_START_COLOR); } _updateClone() { - if (this._iterations > OConfig.orgCloneMinAge && this._energy > OConfig.orgCloneMinEnergy) {this.fire(CLONE, this)} + if (OConfig.orgCloneMinAge > 0 && OConfig.orgCloneMinEnergy > 0 && this._iterations > OConfig.orgCloneMinAge && this._energy > OConfig.orgCloneMinEnergy) {this.fire(CLONE, this)} } /** @@ -264,9 +265,6 @@ class Organism extends Observer { * @return {Boolean} false means that organism was destroyed. */ _updateEnergy() { - // - // this.vm === null means, that organism has already destroyed - // if (this._energy < 1 && this.vm) { this.fire(KILL_NO_ENERGY, this); this.destroy(); diff --git a/client/src/manager/plugins/organisms/Organisms.js b/client/src/manager/plugins/organisms/Organisms.js index 28e0631..e24514e 100644 --- a/client/src/manager/plugins/organisms/Organisms.js +++ b/client/src/manager/plugins/organisms/Organisms.js @@ -16,10 +16,6 @@ const ORG_EVENTS = require('./../../../../src/manager/plugins/organisms/Organi //const Backup = require('./../backup/Backup'); const Mutator = require('./Mutator'); const Num = require('./../../../vm/Num'); -/** - * {Number} Random range for selection of random organism from a Queue - */ -const RAND_RANGE = 5; // TODO: inherit this class from Configurable class Organisms extends Configurable { @@ -59,49 +55,61 @@ class Organisms extends Configurable { constructor(manager) { super(manager, {Config, cfg: OConfig}, { getAmount : ['_apiGetAmount', 'Shows amount of organisms within current Client(Manager)'], - getOrganism: ['_apiGetOrganism', 'Returns organism instance by id or int\'s index in a Queue'] + getOrganism: ['_apiGetOrganism', 'Returns organism instance by id or int\'s index in an array'] }); - this.organisms = manager.organisms; - this.randOrgItem = this.organisms.first; - this.positions = manager.positions; - this.world = manager.world; - - this._mutator = new Mutator(manager, this); - this._maxEnergy = 0; - this._oldMaxEnergy = 0; - this._onIterationCb = this._onIteration.bind(this); - this._onLoopCb = this._onLoop.bind(this); + this.organisms = manager.organisms; + this.positions = manager.positions; + this.world = manager.world; + + this._mutator = new Mutator(manager, this); + this._oldOrg = null; + this._curOldOrg = null; + this._energy = 0; + this._curEnergy = 0; + this._onIterationCb = this._onIteration.bind(this); + this._onLoopCb = this._onLoop.bind(this); + this._onConfigChange = this._onConfigChange.bind(this); this.reset(); Helper.override(manager, 'onIteration', this._onIterationCb); Helper.override(manager, 'onLoop', this._onLoopCb); + manager.on(EVENTS.CONFIG_CHANGE, this._onConfigChange); } destroy() { - let item = this.organisms.first; - let org; - while (item && (org = item.val)) {org.destroy(); item = item.next} + const orgs = this.organisms; + const len = orgs.length; + for (let i = 0; i < len; i++) {if (orgs.get(i)) {orgs.get(i).destroy()}} Helper.unoverride(this.parent, 'onIteration', this._onIterationCb); Helper.unoverride(this.parent, 'onLoop', this._onLoopCb); + this.parent.off(EVENTS.CONFIG_CHANGE, this._onConfigChange); + this._energy = 0; + this._curEnergy = 0; + this._curOldOrg = null; + this._oldOrg = null; this._mutator.destroy(); - this._mutator = null; - this._onIterationCb = null; - this._onLoopCb = null; + this._mutator = null; + this.world = null; + this.positions = null; + this.organisms = null; + this._onConfigChange = null; + this._onIterationCb = null; + this._onLoopCb = null; super.destroy(); } /** - * Is called at the end of run() method. Updates maxEnergy value for population + * Is called at the end of run() method. Found oldest organism and calculates + * energy of all organisms * @param {Organism} org Current organism */ onOrganism(org) { - if (org.energy > this._oldMaxEnergy) {this._oldMaxEnergy = org.energy} - if (org === this.organisms.last.val) { - this._maxEnergy = this._oldMaxEnergy; - this._oldMaxEnergy = 0; - } + this._curEnergy += org.energy; + if (org.vm === null || !OConfig.orgKillOnClone) {return} + if (this._curOldOrg === null) {this._curOldOrg = org} + if (this._curOldOrg.iterations < org.iterations) {this._curOldOrg = org} } addOrgHandlers(org) { @@ -114,6 +122,7 @@ class Organisms extends Configurable { reset() { this._orgId = 0; + this.parent.sharedObj.orgEnergy = 0; } move(x1, y1, x2, y2, org) { @@ -134,35 +143,27 @@ class Organisms extends Configurable { createOrg(x, y, parent = null) { if (x === -1) {return false} - this._randOrg(); - const item = this.organisms.addAfter(this.randOrgItem, null); - let org = this.createEmptyOrg(++this._orgId + '', x, y, item, parent); + const orgs = this.organisms; + let org = this.createEmptyOrg(++this._orgId + '', x, y, orgs.freeIndex, parent); - item.val = org; + orgs.add(org); this.addOrgHandlers(org); this.world.setDot(x, y, org.color); this.positions[x][y] = org; this.parent.fire(EVENTS.BORN_ORGANISM, org); //Console.info(org.id, ' born'); - return item; + return org.item; } /** * Returns random organism of current population - * @return {Organism|null} + * @return {Organism|false} */ _randOrg() { - const offs = Helper.rand(RAND_RANGE) + 1; - let item = this.randOrgItem; - - for (let i = 0; i < offs; i++) { - if ((item = item.next) === null) { - item = this.organisms.first; - } - } - - return (this.randOrgItem = item).val; + const org = this.organisms.get(Helper.rand(this.organisms.size)); + if (org === null) {return false} + return org; } /** @@ -172,15 +173,16 @@ class Organisms extends Configurable { * @param {Number} stamp Time stamp of current iteration */ _onIteration(counter, stamp) { - let item = this.organisms.first; - let org; + const orgs = this.organisms; + let org; - while (org = item && item.val) { + for (let i = 0, len = orgs.size; i < len; i++) { + if ((org = orgs.get(i)) === null) {continue} org.run(); this.onOrganism(org); - item = item.next; } + this._onAfterIteration(); this._updateTournament(counter); this._updateRandomOrgs(counter); this._updateCrossover(counter); @@ -190,10 +192,29 @@ class Organisms extends Configurable { this._updateCreate(); } + /** + * Is called when global configuration has changed. Tracks if amount of + * organisms has changed and updated this.organisms array + * @param {String} key Unique config key + * @param {*} val New value + */ + _onConfigChange(key, val) { + if (key === 'organisms.orgMaxOrgs') { + const orgs = this.organisms; + if (val < orgs.size) { + for (let i = orgs.size - 1; i >= val; i--) {orgs.get(i) !== null && orgs.get(i).destroy()} + } + orgs.resize(val); + } + } + _tournament(org1 = null, org2 = null) { org1 = org1 || this._randOrg(); org2 = org2 || this._randOrg(); + if (org1 === false && org2 !== false) {return org2} + if (org2 === false && org1 !== false) {return org1} + if (org1 === false && org2 === false) {return false} if (org1.energy < 1 && org2.energy < 1) {return false} if ((org2.energy > 0 && org1.energy < 1) || this.compare(org2, org1)) { return org2; @@ -206,22 +227,22 @@ class Organisms extends Configurable { if (this.onBeforeClone(org) === false) {return false} let x; let y; - let item; + [x, y] = this.world.getNearFreePos(org.x, org.y); - if (x === -1 || (item = this.createOrg(x, y, org)) === false) {return false} - let child = item.val; + if (x === -1 || this.createOrg(x, y, org) === false) {return false} + let child = this.organisms.added(); + if (!child) {return false} this.onClone(org, child); if (org.energy < 1 || child.energy < 1) {return false} this.parent.fire(EVENTS.CLONE, org, child, isCrossover); - return item; + return true; } _crossover(org1, org2) { - const item = this._clone(org1, true); - if (item === false) {return false} - let child = item.val; + if (!this._clone(org1, true)) {return false} + let child = this.organisms.added(); if (child.energy > 0 && org2.energy > 0) { child.changes += (Math.abs(child.vm.crossover(org2.vm)) * Num.MAX_BITS); @@ -232,11 +253,24 @@ class Organisms extends Configurable { } _createPopulation() { - const world = this.world; + const world = this.world; + const positions = OConfig.orgPosition; + const posAmount = positions.length / 4; + const hasPosition = posAmount >= 1; this.reset(); - for (let i = 0, len = OConfig.orgStartAmount; i < len; i++) { - this.createOrg(...world.getFreePos()); + for (let i = 0, len = OConfig.orgStartAmount; i < len; i += posAmount) { + if (hasPosition) { + for (let j = 0; j < posAmount; j++) { + const x = Helper.rand(positions[j * 4 + 2]) + positions[j * 4] - positions[j * 4 + 2] / 2; + const y = Helper.rand(positions[j * 4 + 3]) + positions[j * 4 + 1] - positions[j * 4 + 3] / 2; + if (world.isFree(x, y)) { + this.createOrg(x, y) + } + } + } else { + this.createOrg(...world.getFreePos()); + } } Console.info('Population has created'); } @@ -247,37 +281,20 @@ class Organisms extends Configurable { * @return {Number} Amount of organisms within current Manager */ _apiGetAmount() { - return this.parent.organisms.size; + return this.parent.organisms.length; } /** - * Return organism instance by id or it's index in a Queue + * Return organism instance by id or it's index in array * @param {Number|String} index Index or id - * @return {Organism} Organism instance or null + * @return {Organism|null} Organism instance or null * @api */ _apiGetOrganism(index) { - if (Helper.isNumeric(index)) { - return this.organisms.get(index); - } - - let item = this.organisms.first; - let org; - - while (org = item && item.val) { - if (org.id === index) {return org} - item = item.next; - } - - return null; + return this.organisms.get(index); } _onDestroyOrg(org) { - if (this.randOrgItem === org.item) { - if ((this.randOrgItem = org.item.next) === null) { - this.randOrgItem = this.organisms.first; - } - } this.organisms.del(org.item); this.world.setDot(org.x, org.y, 0); this.positions[org.x][org.y] = 0; @@ -294,15 +311,16 @@ class Organisms extends Configurable { _onCloneOrg(org) { //const maxOrgs = OConfig.orgMaxOrgs; - //const orgAmount = this.organisms.size; + //const orgAmount = this.organisms.length; //if (OConfig.orgKillOnClone && orgAmount >= maxOrgs) {this._killInTour()} //if (orgAmount >= maxOrgs && (OConfig.orgKillOnClone || Math.random() <= (org.energy / org.vm.size) / this._maxEnergy)) {this._randOrg().destroy()} - // if (this.organisms.size >= OConfig.orgMaxOrgs && Math.random() <= ((org.energy / 10000000000000) * (org.iterations / OConfig.orgAlivePeriod))) { + // if (this.organisms.length >= OConfig.orgMaxOrgs && Math.random() <= ((org.energy / 10000000000000) * (org.iterations / OConfig.orgAlivePeriod))) { + // this._randOrg().destroy(); // this._randOrg().destroy(); // } - //if (this.organisms.size < maxOrgs) {this._clone(org)} - //if (this.organisms.size >= maxOrgs && Math.random() <= (org.energy / org.vm.size) / this._maxEnergy) {this._randOrg().destroy()} + //if (this.organisms.length < maxOrgs) {this._clone(org)} + //if (this.organisms.length >= maxOrgs && Math.random() <= (org.energy / org.vm.size) / this._maxEnergy) {this._randOrg().destroy()} // // This is very important part of application! Cloning should be available only if // amount of organisms is less then maximum or if current organism has ate other just @@ -311,17 +329,18 @@ class Organisms extends Configurable { // organisms before cloning. They should kill each other to have a possibility // to clone them. // - if (OConfig.orgKillOnClone && this.organisms.size >= OConfig.orgMaxOrgs) { - const randOrg = this._randOrg(); - if (randOrg !== org && Math.random() <= org.iterations / OConfig.orgAlivePeriod) {randOrg.destroy()} + if (OConfig.orgKillOnClone && this.organisms.length >= OConfig.orgMaxOrgs && this._oldOrg) { + this._oldOrg.destroy(); + this._oldOrg = null; + //this._killInTour(); } - if (this.organisms.size < OConfig.orgMaxOrgs) {this._clone(org)} + if (this.organisms.length < OConfig.orgMaxOrgs && org.vm !== null) {this._clone(org)} } _killInTour() { let org1 = this._randOrg(); let org2 = this._randOrg(); - if (org1.energy < 1 || org2.energy < 1 || org1 === org2 || this.organisms.size < 1) {return false} + if (org1 === false || org2 === false || org1.energy < 1 || org2.energy < 1 || org1 === org2 || this.organisms.length < 1) {return false} const winner = this._tournament(org1, org2); if (winner === false) {return false} @@ -332,6 +351,14 @@ class Organisms extends Configurable { return true; } + _onAfterIteration() { + this.parent.sharedObj.orgEnergy = this._curEnergy; + this._oldOrg = this._curOldOrg; + this._energy = this._curEnergy; + this._curOldOrg = null; + this._curEnergy = 0; + } + /** * Does tournament between two random organisms and kill looser. In general * this function is a natural selection in our system. @@ -340,12 +367,13 @@ class Organisms extends Configurable { */ _updateTournament(counter) { const period = OConfig.orgTournamentPeriod; - return counter % period === 0 && this.organisms.size > 0 || period !== 0 && this._killInTour(); + return counter % period === 0 && this.organisms.length > 0 && period !== 0 && this._killInTour(); } _updateRandomOrgs(counter) { - if (counter % OConfig.orgRandomOrgPeriod !== 0 || OConfig.orgRandomOrgPeriod === 0 || this.organisms.size < 1) {return false} + if (counter % OConfig.orgRandomOrgPeriod !== 0 || OConfig.orgRandomOrgPeriod === 0 || this.organisms.length < 1) {return false} const vm = this._randOrg().vm; + if (!vm) {return false} const size = Helper.rand(vm.size) + 1; const pos = Helper.rand(vm.size - size); @@ -355,14 +383,14 @@ class Organisms extends Configurable { } _updateCrossover(counter) { - const orgAmount = this.organisms.size; + const orgAmount = this.organisms.length; if (counter % OConfig.orgCrossoverPeriod !== 0 || OConfig.orgCrossoverPeriod === 0 || orgAmount < 1) {return false} // // We have to have a possibility to crossover not only with best // organisms, but with low fit also // - let org1 = Helper.rand(2) === 0 ? this._tournament() : this._randOrg(); - let org2 = Helper.rand(2) === 0 ? this._tournament() : this._randOrg(); + let org1 = this._tournament(); + let org2 = this._tournament(); if (org1 === false || org2 === false || org1.energy < 1 || org2.energy < 1) {return false} this._crossover(org1, org2); @@ -378,7 +406,7 @@ class Organisms extends Configurable { } _updateCreate() { - if (this.organisms.size < 1) {this._createPopulation()} + if (this.organisms.length < 1) {this._createPopulation()} } } diff --git a/client/src/manager/plugins/organisms/dos/Code2JS.js b/client/src/manager/plugins/organisms/dos/Code2JS.js new file mode 100644 index 0000000..365f994 --- /dev/null +++ b/client/src/manager/plugins/organisms/dos/Code2JS.js @@ -0,0 +1,64 @@ +/** + * This class is used only for code visualization from byte code to human + * readable form (JS compatible syntax). It uses Code2JS base class as + * a parent. + * + * @author flatline + */ +const Num = require('./../../../../vm/Num'); +const Helper = require('./../../../../../../common/src/Helper'); +const BaseCode2JS = require('./../../../../vm/Code2JS'); + +class Code2JS extends BaseCode2JS { + constructor(manager) { + super(); + + this._manager = manager; + /** + * {Object} These operator handlers should return string representation + * of numeric based byte vm. + */ + this.operators[0b101100] = this._onLookAt.bind(this); + this.operators[0b101101] = this._onStep.bind(this); + this.operators[0b101110] = this._onDir.bind(this); + this.operators[0b101111] = this._onMyX.bind(this); + this.operators[0b110000] = this._onMyY.bind(this); + this.operators[0b110001] = this._onEat.bind(this); + this.operators[0b110010] = this._onPut.bind(this); + this.operators[0b110011] = this._onEnergy.bind(this); + this.operators[0b110100] = this._onPick.bind(this); + this.operators[0b110101] = this._onSay.bind(this); + this.operators[0b110110] = this._onListen.bind(this); + this.operators[0b110111] = this._onCheck.bind(this); + this.operators[0b111000] = this._onMyEnergy.bind(this); + this.operators[0b111001] = this._onMyAge.bind(this); + this.operators[0b111010] = this._onMyDir.bind(this); + // + // API of the Manager for accessing outside. (e.g. from Console) + // + Helper.setApi(manager.api, 'toJS', (code) => this.format(code), 'Converts byte code array into human readable JavaScript based code. This function is low level. For using it you have to get organism\'s virtual machine reference and then use it\'s code property. For example: man.api.toJS(man.api.organisms.getOrganism(\'128\').vm.code). This example will find organism with id \'128\' and shows his byte code.'); + } + + destroy() { + super.destroy(); + this._manager = null; + } + + _onLookAt(num) {return `v${Num.getVar0(num)}=lookAt(v${Num.getVar1(num)},v${Num.getVar2(num)})`} + _onStep() {return `step()`} + _onDir(num) {return `dir(v${Num.getVar0(num)})`} + _onMyX(num) {return `v${Num.getVar0(num)}=myX()`} + _onMyY(num) {return `v${Num.getVar0(num)}=myY()`} + _onEat(num) {return `eat(v${Num.getVar0(num)})`} + _onPut(num) {return `put(v${Num.getVar0(num)})`} + _onEnergy() {return `energy()`} + _onPick(num) {return `pick(v${Num.getVar0(num)})`} + _onSay(num) {return `say(v${Num.getVar0(num)})`} + _onListen(num) {return `v${Num.getVar0(num)}=listen()`} + _onCheck(num) {return `v${Num.getVar0(num)}=check()`} + _onMyEnergy(num) {return `v${Num.getVar0(num)}=myEnergy()`} + _onMyAge(num) {return `v${Num.getVar0(num)}=myAge()`} + _onMyDir(num) {return `v${Num.getVar0(num)}=myDir()`} +} + +module.exports = Code2JS; \ No newline at end of file diff --git a/client/src/manager/plugins/organisms/dos/Code2String.js b/client/src/manager/plugins/organisms/dos/Code2String.js deleted file mode 100644 index 139b0f4..0000000 --- a/client/src/manager/plugins/organisms/dos/Code2String.js +++ /dev/null @@ -1,254 +0,0 @@ -/** - * This class is used only for code visualization in readable human like form. - * It converts numeric based byte code into JS string. This class must be - * synchronized with 'Operators' one. - * - * @author flatline - */ -const Num = require('./../../../../vm/Num'); -const OConfig = require('./../Config'); -const Helper = require('./../../../../../../common/src/Helper'); -/** - * {Function} Just a shortcuts - */ -const FOUR_BITS = 4; -const CONDITION_BITS = 2; - -class Code2String { - constructor(manager) { - this._manager = manager; - /** - * {Object} These operator handlers should return string representation - * of numeric based byte vm. - */ - this._OPERATORS_CB = [ - this._onVar.bind(this), - this._onConst.bind(this), - this._onCondition.bind(this), - this._onLoop.bind(this), - this._onOperator.bind(this), - this._onLookAt.bind(this), - this._onEatLeft.bind(this), - this._onEatRight.bind(this), - this._onEatUp.bind(this), - this._onEatDown.bind(this), - this._onStepLeft.bind(this), - this._onStepRight.bind(this), - this._onStepUp.bind(this), - this._onStepDown.bind(this), - this._onFromMem.bind(this), - this._onToMem.bind(this), - this._onMyX.bind(this), - this._onMyY.bind(this), - this._onCheckLeft.bind(this), - this._onCheckRight.bind(this), - this._onCheckUp.bind(this), - this._onCheckDown.bind(this) - ]; - this._OPERATORS_CB_LEN = this._OPERATORS_CB.length; - /** - * {Array} Available conditions for if operator. Amount should be - * the same like (1 << Num.BITS_PER_VAR) - */ - this._CONDITIONS = ['<', '>', '==', '!=']; - /** - * {Array} Available operators for math calculations - */ - this._OPERATORS = [ - '+', '-', '*', '/', '%', '&', '|', '^', '>>', '<<', '>>>', '<', '>', '==', '!=', '<=' - ]; - /** - * {Array} Contains closing bracket offset for "if", "loop",... operators - */ - this._offsets = [0]; - - Num.init(this._OPERATORS_CB_LEN); - - this._BITS_AFTER_ONE_VAR = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR; - this._BITS_AFTER_TWO_VARS = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 2; - this._BITS_AFTER_THREE_VARS = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 3; - // - // API of the Manager for accessing outside. (e.g. from Console) - // - Helper.setApi(manager.api, 'formatCode', (code) => this.format(code), 'Converts byte code array into human readable JavaScript based code. This function is low level. For using it you have to get organism\'s virtual machine reference and then use it\'s code property. For example: man.api.formatCode(man.api.organisms.getOrganism(\'128\').vm.code). This example will find organism with id \'128\' and shows his byte code.'); - } - - destroy() { - this._OPERATORS_CB = null; - this._CONDITIONS = null; - this._OPERATORS = null; - this._offsets = null; - delete this._manager.api.formatCode; - this._manager = null; - } - - format(code, separator = '\n') { - const len = code.length; - const operators = this._OPERATORS_CB; - const offs = this._offsets; - let lines = new Array(len); - // - // First number always amount of code lines - // - offs.splice(0, offs.length, len); - for (let line = 0; line < len; line++) { - lines[line] = operators[Num.getOperator(code[line])](code[line], line); - // - // We found closing bracket '}' of some loop and have to add - // it to output code array - // - while (offs.length > 1 && line === offs[offs.length - 1]) { - line = offs.pop(); - lines[line] += '}'; - } - } - // - // All closing brackets st the end of JS script - // - const length = lines.length - 1; - for (let i = 1; i < offs.length; i++) { - lines[length] += '}'; - } - - return js_beautify(lines.join(separator), {indent_size: 4}); - } - - /** - * Parses variable operator. Format: let = const|number. Num bits format: - * Num.BITS_PER_OPERATOR bits - operator id - * Num.BITS_PER_VAR bits - destination var index - * Num.BITS_PER_VAR bits - assign type (const (half of bits) or variable (half of bits)) - * Num.BITS_PER_VAR bits - variable index or all bits till the end for constant - * - * @param {Num} num Packed into number vm line - * @return {String} Parsed vm line string - */ - _onVar(num) { - return `v${Num.getVar0(num)}=${'v' + Num.getVar1(num)}`; - } - - _onConst(num) { - return `v${Num.getVar0(num)}=${Num.getBits(num, this._BITS_AFTER_ONE_VAR, OConfig.codeConstBits)}`; - } - - _onCondition(num, line) { - const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS); - const blockOffs = Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock); - - this._offsets.push(this._getOffs(line, blockOffs)); - return `if(v${Num.getVar0(num)}${this._CONDITIONS[cond]}v${Num.getVar1(num)}){`; - } - - _onLoop(num, line) { - const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS); - const blockOffs = Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock); - - this._offsets.push(this._getOffs(line, blockOffs)); - return `while(v${Num.getVar0(num)}${this._CONDITIONS[cond]}v${Num.getVar1(num)}){`; - } - - _onOperator(num) { - return `v${Num.getVar0(num)}=v${Num.getVar1(num)}${this._OPERATORS[Num.getBits(num, this._BITS_AFTER_THREE_VARS, FOUR_BITS)]}v${Num.getVar2(num)}`; - } - - _onLookAt(num) { - return `v${Num.getVar0(num)}=lookAt(v${Num.getVar1(num)},v${Num.getVar2(num)})`; - } - - _onEatLeft(num) { - return `v${Num.getVar0(num)}=eatLeft(v${Num.getVar1(num)})`; - } - - _onEatRight(num) { - return `v${Num.getVar0(num)}=eatRight(v${Num.getVar1(num)})`; - } - - _onEatUp(num) { - return `v${Num.getVar0(num)}=eatUp(v${Num.getVar1(num)})`; - } - - _onEatDown(num) { - return `v${Num.getVar0(num)}=eatDown(v${Num.getVar1(num)})`; - } - - _onStepLeft(num) { - return `v${Num.getVar0(num)}=stepLeft()`; - } - - _onStepRight(num) { - return `v${Num.getVar0(num)}=stepRight()`; - } - - _onStepUp(num) { - return `v${Num.getVar0(num)}=stepUp()`; - } - - _onStepDown(num) { - return `v${Num.getVar0(num)}=stepDown()`; - } - - _onFromMem(num) { - if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) { - return `v${Num.getVar0(num)}=fromMem(v${Num.getVar1(num)})`; - } - - const offs = Num.getBits(num, this._BITS_AFTER_TWO_VARS + 1, OConfig.orgMemBits); - return `v${Num.getVar0(num)}=fromMem(${offs})`; - } - - _onToMem(num) { - if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) { - return `toMem(v${Num.getVar0(num)},v${Num.getVar1(num)})`; - } - return `toMem(v${Num.getVar0(num)},${Num.getBits(num, this._BITS_AFTER_TWO_VARS, OConfig.orgMemBits)})`; - } - - _onMyX(num) { - return `v${Num.getVar0(num)}=myX()`; - } - - _onMyY(num) { - return `v${Num.getVar0(num)}=myY()`; - } - - _onCheckLeft(num) { - return `v${Num.getVar0(num)}=checkLeft()`; - } - - _onCheckRight(num) { - return `v${Num.getVar0(num)}=checkRight()`; - } - - _onCheckUp(num) { - return `v${Num.getVar0(num)}=checkUp()`; - } - - _onCheckDown(num) { - return `v${Num.getVar0(num)}=checkDown()`; - } - - /** - * Returns offset for closing bracket of blocked operators like - * "if", "for" and so on. These operators shouldn't overlap each - * other. for example: - * - * for (...) { // 0 - * if (...) { // 1 - * ... // 2 - * } // 3 - * } // 4 - * - * Closing bracket in line 3 shouldn't be after bracket in line 4. - * So it's possible to set it to one of 1...3. So we change it in - * real time to fix the overlap problem. - * @param {Number} line Current line index - * @param {Number} offs Local offset of closing bracket we want to set - * @returns {Number} - */ - _getOffs(line, offs) { - const offsets = this._offsets || [0]; - return line + offs > offsets[offsets.length - 1] ? offsets[offsets.length - 1] : line + offs; - } -} - -module.exports = Code2String; \ No newline at end of file diff --git a/client/src/manager/plugins/organisms/dos/Operators.js b/client/src/manager/plugins/organisms/dos/Operators.js index e99548c..70a4672 100644 --- a/client/src/manager/plugins/organisms/dos/Operators.js +++ b/client/src/manager/plugins/organisms/dos/Operators.js @@ -6,343 +6,636 @@ * * @author flatline */ -const Helper = require('./../../../../../../common/src/Helper'); -const EVENTS = require('./../../../../../src/share/Events').EVENTS; -const OConfig = require('./../Config'); -const Operators = require('./../../../../vm/Operators'); -const Num = require('./../../../../vm/Num'); - -/** - * {Function} Is created to speed up this function call. constants are run - * much faster, then Helper.normalize() - */ -const IN_WORLD = Helper.inWorld; -const IS_FINITE = Number.isFinite; -const IS_NAN = Number.isNaN; -/** - * {Function} Just a shortcuts - */ -const FOUR_BITS = 4; -const CONDITION_BITS = 2; -const MAX_VAL = Number.MAX_VALUE; +const Helper = require('./../../../../../../common/src/Helper'); +const EVENTS = require('./../../../../../src/share/Events').EVENTS; +const OConfig = require('./../Config'); +const EConfig = require('./../../energy/Config'); +const Operators = require('./../../../../vm/Operators'); +const Objects = require('./../../objects/Objects'); +const Organism = require('./../../../plugins/organisms/Organism').Organism; +const Num = require('./../../../../vm/Num'); +const OFFSX = require('./../../../../../../common/src/Directions').OFFSX; +const OFFSY = require('./../../../../../../common/src/Directions').OFFSY; +const OBJECT_TYPES = require('./../../../../view/World').OBJECT_TYPES; + +const NORMALIZE_NO_DIR = Helper.normalizeNoDir; /** - * {Array} Available conditions for if operator. Amount should be - * the same like (1 << Num.BITS_PER_VAR) + * {Number} World object types */ -const CONDITIONS = [(a,b)=>aa>b, (a,b)=>a===b, (a,b)=>a!==b]; +const EMPTY = 0; +const ENERGY = 1; +const ORGANISM = 2; /** - * {Array} Available operators for math calculations + * {Function} Is created to speed up this function call. constants are run + * much faster, then Helper.normalize() */ -const OPERATORS = [ - (a,b) => {const v=a+b; return IS_FINITE(v)?v:MAX_VAL}, - (a,b) => {const v=a-b; return IS_FINITE(v)?v:-MAX_VAL}, - (a,b) => {const v=a*b; return IS_FINITE(v)?v:MAX_VAL}, - (a,b) => {const v=a/b; return IS_FINITE(v)?v:MAX_VAL}, - (a,b) => {const v=a%b; return IS_NAN(v)?0:v}, - (a,b) => a&b, - (a,b) => a|b, - (a,b) => a^b, - (a,b) => a>>b, - (a,b) => a< a>>>b, - (a,b) => +(a +(a>b), - (a,b) => +(a===b), - (a,b) => +(a!==b), - (a,b) => +(a<=b) -]; +const IN_WORLD = Helper.inWorld; class OperatorsDos extends Operators { - constructor(offs, vars, obs) { - super(offs, vars, obs); - /** - * {Object} These operator handlers should return next script line - * number VM should step to - */ - this._OPERATORS_CB = [ - this.onVar.bind(this), - this.onConst.bind(this), - this.onCondition.bind(this), - this.onLoop.bind(this), - this.onOperator.bind(this), - this.onLookAt.bind(this), - this.onEatLeft.bind(this), - this.onEatRight.bind(this), - this.onEatUp.bind(this), - this.onEatDown.bind(this), - this.onStepLeft.bind(this), - this.onStepRight.bind(this), - this.onStepUp.bind(this), - this.onStepDown.bind(this), - this.onFromMem.bind(this), - this.onToMem.bind(this), - this.onMyX.bind(this), - this.onMyY.bind(this), - this.onCheckLeft.bind(this), - this.onCheckRight.bind(this), - this.onCheckUp.bind(this), - this.onCheckDown.bind(this) - ]; + static compile() { + const bitsPerOp = OConfig.CODE_BITS_PER_OPERATOR; /** - * {Object} Reusable object to pass it as a parameter to this.fire(..., ret) + * {Number} Total amount of operators. Base lang + custom */ - this._ret = {ret: 0}; + this.OPERATOR_AMOUNT = 27; // - // We have to set amount of available operators for correct - // working of mutations of operators. + // IMPORTANT: don't use super here, because it breaks Operators + // IMPORTANT: class internal logic. Operators.global will be point + // IMPORTANT: to the Window // - Num.init(this._OPERATORS_CB.length); - - this._BITS_AFTER_ONE_VAR = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR; - this._BITS_AFTER_TWO_VARS = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 2; - this._BITS_AFTER_THREE_VARS = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 3; + Operators.compile(this.OPERATOR_AMOUNT); + + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 3)); // lookAt + this.LENS.push(Num.MAX_BITS - bitsPerOp); // step + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // dir + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // myX + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // myY + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // eat + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // put + this.LENS.push(Num.MAX_BITS - bitsPerOp); // energy + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // pick + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // say + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // listen + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // check + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // myEnergy + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // myAge + this.LENS.push(Num.MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar)); // myDir + + this._compileLookAt(); // 12 + this._compileStep(); // 13 + this._compileDir(); // 14 + this._compileMyX(); // 15 + this._compileMyY(); // 16 + this._compileEat(); // 17 + this._compilePut(); // 18 + this._compileEnergy(); // 19 + this._compilePick(); // 20 + this._compileSay(); // 21 + this._compileListen(); // 22 + this._compileCheck(); // 23 + this._compileMyEnergy(); // 24 + this._compileMyAge(); // 25 + this._compileMyDir(); // 26 } - destroy() { - super.destroy(); - this._OPERATORS_CB = null; - this._ret = null; + /** + * Compiles all variants of lookAt operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx xx + * number: 101100 00 01 00... + * string: v0 = lookAt(v1, v0) + */ + static _compileLookAt() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + for (let v2 = 0; v2 < vars; v2++) { + eval(`OperatorsDos.global.fn = function lookAt(line) { + const vars = this.vars; + const x = (vars[${v1}] + .5) << 0; + const y = (vars[${v2}] + .5) << 0; + if (!IN_WORLD(x, y)) { + vars[${v0}] = ${EMPTY}; + return ++line; + } + vars[${v0}] = this._getDotType(x, y); + return ++line; + }`); + ops[h(`${'101100'}${b(v0, bpv)}${b(v1, bpv)}${b(v2, bpv)}`)] = this.global.fn; + } + } + } } - get operators() {return this._OPERATORS_CB} + /** + * Compiles all variants of 'step' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 + * number: 101101... + * string: step + */ + static _compileStep() { + eval(`OperatorsDos.global.fn = function step(line, num, org) { + this._obs.fire(${EVENTS.STEP}, org, org.x, org.y, org.dirX, org.dirY); + return ++line; + }`); + this._compiledOperators[Helper.toHexNum(`${'101101'}`)] = this.global.fn; + } /** - * Handler of variable assignment operator. 'xx' means, that amount of - * bits depends on configuration. '...' means, that all other bits are + * Compiles all variants of 'dir' operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are * ignored. Example: - * bits : 8 xx xx - * number: 00000000 00 01... - * desc : var v0 v1 - * string: v0 = v1 * - * @param {Number} num One bit packed byte code row - * @param {Number} line Current line in DOS code - * @return {Number} Next line number to proceed + * bits : 6 xx + * number: 101110 11... + * string: dir(v3) */ - onVar(num, line) { - this.vars[Num.getVar0(num)] = this.vars[Num.getVar1(num)]; - return ++line; + static _compileDir() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function dir(line, num, org) { + org.dir = ((this.vars[${v0}] + .5) << 0 >>> 0) % ${OFFSX.length}; + return ++line; + }`); + ops[h(`${'101110'}${b(v0, bpv)}`)] = this.global.fn; + } } /** - * Handler of numeric constant assignment operator. 'xx' means, that amount of - * bits depends on configuration. '...' means, that all other bits are + * Compiles all variants of 'myX' operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are * ignored. Example: - * bits : 8 xx xx - * number: 00000001 00 01... - * desc : const v0 1 - * string: v0 = 1 * - * @param {Number} num One bit packed byte code row - * @param {Number} line Current line in DOS code - * @return {Number} Next line number to proceed + * bits : 6 xx + * number: 101111 00... + * string: v0 = myX() */ - onConst(num, line) { - this.vars[Num.getVar0(num)] = Num.getBits(num, this._BITS_AFTER_ONE_VAR, OConfig.codeConstBits); - return ++line; + static _compileMyX() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function myX(line, num, org) { + this.vars[${v0}] = org.x; + return ++line; + }`); + ops[h(`${'101111'}${b(v0, bpv)}`)] = this.global.fn; + } } /** - * Handler of 'if' operator. 'xx' means, that amount of bits depends on - * configuration. '...' means, that all other bits are - * ignored. Offset of closing bracket means row number after, which this - * bracket will be added. Offset of closing bracket is calculating using - * formula: line + offs. Example: - * bits : 8 xx xx 2 xx - * number: 00000010 00 01 00 00... - * desc : if v0 v1 < } - * string: if (v0 < v1) {} + * Compiles all variants of 'myY' operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: * - * @param {Number} num One bit packed byte code row - * @param {Number} line Current line in DOS code - * @return {Number} Next line number to proceed + * bits : 6 xx + * number: 110000 00... + * string: v0 = myY() */ - onCondition(num, line) { - const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS); - const offs = this._getOffs(line, Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock)); - - if (CONDITIONS[cond](this.vars[Num.getVar0(num)], this.vars[Num.getVar1(num)])) { - this.offs.push(offs, offs); - return ++line; + static _compileMyY() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function myY(line, num, org) { + this.vars[${v0}] = org.y; + return ++line; + }`); + ops[h(`${'110000'}${b(v0, bpv)}`)] = this.global.fn; } - - return offs; } /** - * Handler of 'while' operator. 'xx' means, that amount of bits depends on - * configuration. '...' means, that all other bits are - * ignored. Offset of closing bracket means row number after, which this - * bracket will be added. Offset of closing bracket is calculating using - * formula: line + offs. Example: - * bits : 8 xx xx 2 xx - * number: 00000011 00 01 00 00... - * desc : while v0 v1 < } - * string: while (v0 < v1) {} + * Compiles all variants of 'eat' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: * - * @param {Number} num One bit packed byte code row - * @param {Number} line Current line in DOS code - * @return {Number} Next line number to proceed + * bits : 6 xx + * number: 110001 01... + * string: eat(v1) */ - onLoop(num, line) { - const cond = Num.getBits(num, this._BITS_AFTER_TWO_VARS, CONDITION_BITS); - const offs = this._getOffs(line, Num.getBits(num, this._BITS_AFTER_TWO_VARS + CONDITION_BITS, OConfig.codeBitsPerBlock)); - - if (CONDITIONS[cond](this.vars[Num.getVar0(num)], this.vars[Num.getVar1(num)])) { - this.offs.push(line, offs); - return ++line; + static _compileEat() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function eat(line, num, org) { + let eat = this.vars[${v0}]; + if (eat <= 0) {return ++line} + let x; + let y; + [x, y] = NORMALIZE_NO_DIR(org.dirX, org.dirY); + const victim = this._positions[x][y]; + + if (victim < 0) {return ++line} // World object found. We can't eat objects + if (victim === 0) { // Energy found + if ((eat = this._world.grabDot(x, y, eat)) > 0) { + if (org.energy + eat > OConfig.orgMaxEnergy) {eat = OConfig.orgMaxEnergy - org.energy} + org.energy += eat; + this._obs.fire(${EVENTS.EAT_ENERGY}, eat); + } + return ++line; + } + if (victim.energy <= eat) { // Organism found + if (org.energy + victim.energy > OConfig.orgMaxEnergy) {return ++line} + this._obs.fire(${EVENTS.KILL_EAT}, victim); + org.energy += victim.energy; + // + // IMPORTANT: + // We have to do destroy here, to have a possibility for current + // (winner) organism to clone himself after eating other organism. + // This is how organisms compete for an ability to clone + // + victim.destroy(); + return ++line; + } + + if (org.energy + eat > OConfig.orgMaxEnergy) {return ++line} + this._obs.fire(${EVENTS.EAT_ORG}, victim, eat); + org.energy += eat; + victim.energy -= eat; + + return ++line; + }`); + ops[h(`${'110001'}${b(v0, bpv)}`)] = this.global.fn; } - - return offs; } - onOperator(num, line) { - const vars = this.vars; - vars[Num.getVar0(num)] = OPERATORS[Num.getBits(num, this._BITS_AFTER_THREE_VARS, FOUR_BITS)](vars[Num.getVar1(num)], vars[Num.getVar2(num)]); - return ++line; + /** + * Compiles all variants of 'put' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 110010 01... + * string: put(v1) + */ + static _compilePut() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function put(line, num, org) { + let put = this.vars[${v0}]; + if (put <= 0) {return ++line} + if (put > 0xffffff) {put = 0xffffff} + const x = org.dirX; + const y = org.dirY; + if (!IN_WORLD(x, y)) {return ++line} + if (this._world.data[x][y] !== 0) {return ++line} + if (org.energy <= put) { + put = org.energy; + this._world.setDot(x, y, put); + this._obs.fire(${EVENTS.PUT_ENERGY}, put); + org.destroy(); + return ++line; + } + + this._world.setDot(x, y, put); + this._obs.fire(${EVENTS.PUT_ENERGY}, put); + org.energy -= put; + return ++line; + }`); + ops[h(`${'110010'}${b(v0, bpv)}`)] = this.global.fn; + } } - onLookAt(num, line) { - const vars = this.vars; - const x = (vars[Num.getVar1(num)] + .5) << 0; - const y = (vars[Num.getVar2(num)] + .5) << 0; - - if (IN_WORLD(x, y)) { - this.obs.fire(EVENTS.GET_ENERGY, x, y, this._ret); - vars[Num.getVar0(num)] = this._ret.ret; + /** + * Compiles all variants of 'energy' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. If organism calls energy command near + * only one energy object it will not be transformed to energy. + * Example: + * + * bits : 6 + * number: 110011... + * string: energy() + */ + static _compileEnergy() { + const ops = this._compiledOperators; + const h = Helper.toHexNum; + + eval(`Operators.global.fn = function energy(line, num, org) { + const poses = this._positions; + const world = this._world; + const energy = {1:[], 2:[], 3:[], 4:[], 5:[]}; + let e = 0; + // + // this block creates new energy object + // + for (let x = org.x - 1, xlen = org.x + 2; x < xlen; x++) { + for (let y = org.y - 1, ylen = org.y + 2; y < ylen; y++) { + if (IN_WORLD(x, y) && poses[x][y] <= ${OBJECT_TYPES.TYPE_ENERGY0} && poses[x][y] >= ${OBJECT_TYPES.TYPE_ENERGY4}) { + e = -poses[x][y]; + energy[e].push(x, y); + if (energy[e].length === 6) { + const xy = energy[e]; + + world.setDot(xy[0], xy[1], Objects.getColor(e)); + poses[xy[0]][xy[1]] = -(e+1); + world.setDot(xy[2], xy[3], 0); + poses[xy[2]][xy[3]] = 0; + world.setDot(xy[4], xy[5], 0); + poses[xy[4]][xy[5]] = 0; + return ++line; + } + } + } + } + // + // this block changes two energy objects into organism energy + // + for (let e = 1, len = Math.abs(${OBJECT_TYPES.TYPE_ENERGY4} - ${OBJECT_TYPES.TYPE_ENERGY0}) + 1; e <= len; e++) { + if (energy[e].length === 4) { + const xy = energy[e]; + let eat = (2**e) * Helper.getColor(EConfig.colorIndex); + + if (org.energy + eat > OConfig.orgMaxEnergy) {eat = OConfig.orgMaxEnergy - org.energy} + org.energy += eat; + world.setDot(xy[0], xy[1], 0); + poses[xy[0]][xy[1]] = 0; + world.setDot(xy[2], xy[3], 0); + poses[xy[2]][xy[3]] = 0; + return ++line; + } + } return ++line; - } - - vars[Num.getVar0(num)] = 0; - return ++line; + }`); + ops[h(`${'110011'}`)] = this.global.fn; } - onEatLeft(num, line, org) { - const amount = this.vars[Num.getVar1(num)]; - if (amount === 0) {this.vars[Num.getVar0(num)] = 0; return ++line} - const ret = this._ret; - - ret.ret = amount; - this.obs.fire(EVENTS.EAT, org, org.x - 1, org.y, ret); - org.energy += ret.ret; - this.vars[Num.getVar0(num)] = ret.ret; - - return ++line; + /** + * Compiles all variants of 'pick' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 110100 01... + * string: pick(v1) + */ + static _compilePick() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function pick(line, num, org) { + const poses = this._positions; + const world = this._world; + const x = org.dirX; + const y = org.dirY; + if (IN_WORLD(x, y) && (poses[x][y] <= ${OBJECT_TYPES.TYPE_ENERGY0} && poses[x][y] >= ${OBJECT_TYPES.TYPE_ENERGY4} || poses[x][y] === 0 && world.data[x][y] > 0)) { + const dir = ((this.vars[${v0}] + .5) << 0 >>> 0) % ${OFFSX.length}; + const dx = org.x + OFFSX[dir]; + const dy = org.y + OFFSY[dir]; + if (IN_WORLD(dx, dy) && world.data[dx][dy] === 0) { + poses[dx][dy] = poses[x][y]; + poses[x][y] = 0; + world.setDot(dx, dy, world.data[x][y]); + world.setDot(x, y, 0); + } + } + return ++line; + }`); + ops[h(`${'110100'}${b(v0, bpv)}`)] = this.global.fn; + } } - onEatRight(num, line, org) { - const amount = this.vars[Num.getVar1(num)]; - if (amount === 0) {this.vars[Num.getVar0(num)] = 0; return ++line} - const ret = this._ret; - - ret.ret = amount; - this.obs.fire(EVENTS.EAT, org, org.x + 1, org.y, ret); - org.energy += ret.ret; - this.vars[Num.getVar0(num)] = ret.ret; - return ++line; + /** + * Compiles all variants of 'say' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 110101 01... + * string: say(v1) + */ + static _compileSay() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function say(line, num, org) { + const poses = this._positions; + + for (let x = org.x - 1, xlen = org.x + 2; x < xlen; x++) { + for (let y = org.y - 1, ylen = org.y + 2; y < ylen; y++) { + IN_WORLD(x, y) && !(poses[x][y] <= 0) && (poses[x][y].msg = this.vars[${v0}]); + } + } + return ++line; + }`); + ops[h(`${'110101'}${b(v0, bpv)}`)] = this.global.fn; + } } - onEatUp(num, line, org) { - const amount = this.vars[Num.getVar1(num)]; - if (amount === 0) {this.vars[Num.getVar0(num)] = 0; return ++line} - const ret = this._ret; - ret.ret = amount; - this.obs.fire(EVENTS.EAT, org, org.x, org.y - 1, ret); - org.energy += ret.ret; - this.vars[Num.getVar0(num)] = ret.ret; - - return ++line; + /** + * Compiles all variants of 'listen' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 110110 01... + * string: v1 = listen() + */ + static _compileListen() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function listen(line, num, org) { + this.vars[${v0}] = org.msg; + return ++line; + }`); + ops[h(`${'110110'}${b(v0, bpv)}`)] = this.global.fn; + } } - onEatDown(num, line, org) { - const amount = this.vars[Num.getVar1(num)]; - if (amount === 0) {this.vars[Num.getVar0(num)] = 0; return ++line} - const ret = this._ret; - - ret.ret = amount; - this.obs.fire(EVENTS.EAT, org, org.x, org.y + 1, ret); - org.energy += ret.ret; - this.vars[Num.getVar0(num)] = ret.ret; - return ++line; + /** + * Compiles all variants of 'check' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 110111 00... + * string: v0 = check() + */ + static _compileCheck() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function check(line, num, org) { + const x = org.dirX; + const y = org.dirY; + if (!IN_WORLD(x, y)) { + this.vars[${v0}] = ${EMPTY}; + return ++line; + } + this.vars[${v0}] = this._getDotType(x, y); + + return ++line; + }`); + ops[h(`${'110111'}${b(v0, bpv)}`)] = this.global.fn; + } } - onStepLeft(num, line, org) {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x - 1, org.y, org.x - 1); return ++line} - onStepRight(num, line, org) {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x + 1, org.y, org.x + 1); return ++line} - onStepUp(num, line, org) {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x, org.y - 1, org.y - 1); return ++line} - onStepDown(num, line, org) {this.vars[Num.getVar0(num)] = this._step(org, org.x, org.y, org.x, org.y + 1, org.y + 1); return ++line} - - onFromMem(num, line, org) { - if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) { - const offs = (this.vars[Num.getVar1(num)] + .5) << 0; - this.vars[Num.getVar0(num)] = org.mem[offs >= org.mem.length || offs < 0 ? 0 : offs]; - return ++line; + /** + * Compiles all variants of 'myEnergy' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 111000 00... + * string: v0 = myEnergy() + */ + static _compileMyEnergy() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function myEnergy(line, num, org) { + this.vars[${v0}] = org.energy; + return ++line; + }`); + ops[h(`${'111000'}${b(v0, bpv)}`)] = this.global.fn; } + } - this.vars[Num.getVar0(num)] = org.mem[Num.getBits(num, this._BITS_AFTER_TWO_VARS + 1, OConfig.orgMemBits)]; - return ++line; + /** + * Compiles all variants of 'myAge' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 111001 00... + * string: v0 = myAge() + */ + static _compileMyAge() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function myEnergy(line, num, org) { + this.vars[${v0}] = org.iterations; + return ++line; + }`); + ops[h(`${'111001'}${b(v0, bpv)}`)] = this.global.fn; + } } - onToMem(num, line, org) { - if (Num.getBits(num, this._BITS_AFTER_TWO_VARS, 1)) { - const offs = (this.vars[Num.getVar0(num)] + .5) << 0; - org.mem[offs >= org.mem.length || offs < 0 ? 0 : offs] = this.vars[Num.getVar1(num)]; - return ++line; + + /** + * Compiles all variants of 'myDir' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx + * number: 111010 00... + * string: v0 = myDir() + */ + static _compileMyDir() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function myDir(line, num, org) { + this.vars[${v0}] = org.dir; + return ++line; + }`); + ops[h(`${'111010'}${b(v0, bpv)}`)] = this.global.fn; } + } - org.mem[Num.getBits(num, this._BITS_AFTER_TWO_VARS + 1, OConfig.orgMemBits)] = this.vars[Num.getVar0(num)]; - return ++line; + constructor(offs, vars, obs) { + super(offs, vars, obs); + /** + * {Observer} Observer for sending external events + */ + this._obs = obs; + this._world = man.world; + this._positions = man.positions; } - onMyX(num, line, org) {this.vars[Num.getVar0(num)] = org.x; return ++line} - onMyY(num, line, org) {this.vars[Num.getVar0(num)] = org.y; return ++line} + /** + * Returns operators array. Should be overridden in child class + * @abstract + */ + get length() {return OperatorsDos.OPERATOR_AMOUNT} - onCheckLeft(num, line, org) { - this.obs.fire(EVENTS.CHECK_AT, org.x - 1, org.y, this._ret); - this.vars[Num.getVar0(num)] = this._ret.ret; - return ++line; - } - onCheckRight(num, line, org) { - this.obs.fire(EVENTS.CHECK_AT, org.x + 1, org.y, this._ret); - this.vars[Num.getVar0(num)] = this._ret.ret; - return ++line; - } - onCheckUp(num, line, org) { - this.obs.fire(EVENTS.CHECK_AT, org.x, org.y - 1, this._ret); - this.vars[Num.getVar0(num)] = this._ret.ret; - return ++line; - } - onCheckDown(num, line, org) { - this.obs.fire(EVENTS.CHECK_AT, org.x, org.y + 1, this._ret); - this.vars[Num.getVar0(num)] = this._ret.ret; - return ++line; - } + /** + * Returns World instance reference + * @returns {World} + */ + get world() {return this._world} + + destroy() { + super.destroy(); - _step(org, x1, y1, x2, y2, step) { - this.obs.fire(EVENTS.STEP, org, x1, y1, x2, y2); - return org.x === x2 && org.y === y2 ? step : 0; + this._world = null; + this._positions = null; + this._obs = null; } /** - * Returns offset for closing bracket of block operators like - * "if", "for" and so on. These operators shouldn't overlap each - * other. for example: - * - * for (...) { // 0 - * if (...) { // 1 - * ... // 2 - * } // 3 - * } // 4 - * - * Closing bracket in line 3 shouldn't be after bracket in line 4. - * So it's possible to set it to one of 1...3. So we change it in - * real time to fix the overlap problem. - * @param {Number} line Current line index - * @param {Number} offs Local offset of closing bracket we want to set - * @returns {Number} + * Returns type of the dot under x,y coordinat3s + * @param {Number} x X coordinate + * @param {Number} y Y coordinate + * @returns {Number} dot type */ - _getOffs(line, offs) { - const offsets = this.offs || [0]; - return line + offs > offsets[offsets.length - 1] ? offsets[offsets.length - 1] : line + offs + 1; + _getDotType(x, y) { + if (this._positions[x][y] < 0) { + return this._positions[x][y]; + } + if (this._positions[x][y] === 0) { + return this._world.getDot(x, y) > 0 ? ENERGY : EMPTY; + } + + return ORGANISM; } } +OperatorsDos.compile(); + module.exports = OperatorsDos; \ No newline at end of file diff --git a/client/src/manager/plugins/organisms/dos/OperatorsSpec.js b/client/src/manager/plugins/organisms/dos/OperatorsSpec.js index 16da5a2..3e26f1e 100644 --- a/client/src/manager/plugins/organisms/dos/OperatorsSpec.js +++ b/client/src/manager/plugins/organisms/dos/OperatorsSpec.js @@ -1,1196 +1,3107 @@ -const _fill = require('lodash/fill'); - -describe("client/src/organism/OperatorsDos", () => { - const OConfig = require('./../../organisms/Config'); - const THelper = require('./../../../../../../common/tests/Helper'); - const cbpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 2; - const OperatorsDos = require('./Operators'); - const EVENTS = require('./../../../../share/Events').EVENTS; - const Config = require('./../../../../share/Config').Config; - const OrganismDos = require('./../../organisms/dos/Organism'); - const ConfigHelper = require('./../../../../../../common/tests/Config'); - - afterAll(() => OConfig.codeBitsPerVar = cbpv); - - describe('Checks creation, destroy and public API', () => { - it('Checking instance creation', () => { - let offs = []; - let vars = []; - let obs = {}; - let ops = new OperatorsDos(offs, vars, obs); - expect(ops.offs).toBe(offs); - expect(ops.vars).toBe(vars); - expect(ops.obs).toBe(obs); - ops.destroy(); - }); - it('Checking destroy', () => { - let offs = []; - let vars = []; - let obs = {}; - let ops = new OperatorsDos(offs, vars, obs); - ops.destroy(); - expect(ops.offsets).not.toBe(offs); - expect(ops.vars).not.toBe(vars); - expect(ops.obs).not.toBe(obs); - }); - it('Checking operators getter', () => { - let offs = []; - let vars = []; - let obs = {}; - let ops = new OperatorsDos(offs, vars, obs); - expect(Array.isArray(ops.operators)).toBe(true); - expect(ops.operators.length > 0).toBe(true); - ops.destroy(); - }); +const _fill = require('lodash/fill'); +const OperatorsDos = require('./Operators'); +const OConfig = require('./../../../plugins/organisms/Config'); +const EConfig = require('./../../../plugins/energy/Config'); +const Config = require('./../../../../share/Config').Config; +const Helper = require('./../../../../../../common/src/Helper'); +const OrganismDos = require('./Organism'); +const World = require('./../../../../view/World').World; +const EVENTS = require('./../../../../share/Events').EVENTS; +const DIRS = require('./../../../../../../common/src/Directions').DIR; +const OFFSX = require('./../../../../../../common/src/Directions').OFFSX; +const OBJECT_TYPES = require('./../../../../view/World').OBJECT_TYPES; + +const EMPTY = 0; +const ENERGY = 1; +const ORGANISM = 2; + +describe("client/src/manager/plugins/organisms/dos/OperatorsDos", () => { + const hex = Helper.toHexNum; + const ww = Config.worldWidth; + const wh = Config.worldHeight; + const oldMan = global.man; + let org; + let cbpv; + let ccb; + let ops; + let offs; + let vars; + let w; + let h; + + beforeAll (() => { + cbpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 2; + ccb = OConfig.codeConstBits; + OConfig.codeConstBits = 3; + global.man = {world: {}, positions: [ + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0,0] + ]}; + OperatorsDos.compile(); + }); + afterAll (() => { + OConfig.codeBitsPerVar = cbpv; + OConfig.codeConstBits = ccb; + global.man = oldMan; + }); + beforeEach(() => { + w = 10; + h = 10; + Config.worldWidth = w; + Config.worldHeight = h; + global.man.world = new World(w, h); + vars = [0,1,2,3]; + offs = new Array(10); + org = new OrganismDos(0, 0, 0, {}); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach (() => { + ops.destroy(); + global.man.world.destroy(); + org.destroy(); + ops = null; + offs = null; + vars = null; + Config.worldWidth = ww; + Config.worldHeight = wh; }); - describe('onVar() method', () => { - let org; - let ops; - - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - - it("Checking variables working", () => { - expect(ops.onVar(0x00dfffff, 0)).toEqual(1); // 0xd === 0b1101, var3 = var1 - expect(ops.vars).toEqual([0, 1, 2, 1]); - expect(ops.onVar(0x000fffff, 0)).toEqual(1); // 0x0 === 0b0000, var0 = var0 - expect(ops.vars).toEqual([0, 1, 2, 1]); - expect(ops.onVar(0x006fffff, 0)).toEqual(1); // 0x6 === 0b0110, var1 = var2 - expect(ops.vars).toEqual([0, 2, 2, 1]); - expect(ops.onVar(0x00ffffff, 0)).toEqual(1); // 0xf === 0b1111, var3 = var3 - expect(ops.vars).toEqual([0, 2, 2, 1]); + describe('lookAt() operator', () => { + it("Checking lookAt() is found nothing", () => { + expect(ops.operators[hex('101100 01 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,EMPTY,2,3]); }); - it("Checking onVar() method with 3 bits per var config", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([], [0, 1, 2, 3, 4, 5, 6, 7], org); - - expect(ops1.onVar(0x00ffffff, 0)).toEqual(1); // 0xff === 0b[111111]11, var7 = var7 - expect(ops1.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); - expect(ops1.onVar(0x005dffff, 0)).toEqual(1); // 0x5d === 0b[010111]01, var2 = var7 - expect(ops1.vars).toEqual([0, 1, 7, 3, 4, 5, 6, 7]); - expect(ops1.onVar(0x005fffff, 0)).toEqual(1); // 0x5f === 0b[010111]11, var2 = var7 - expect(ops1.vars).toEqual([0, 1, 7, 3, 4, 5, 6, 7]); - expect(ops1.onVar(0x0000ffff, 0)).toEqual(1); // 0x00 === 0b[000000]00, var0 = var0 - expect(ops1.vars).toEqual([0, 1, 7, 3, 4, 5, 6, 7]); - - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking lookAt() looking outside of the world - x", () => { + ops.vars[0] = w + 1; + expect(ops.operators[hex('101100 01 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([w + 1,EMPTY,2,3]); }); - it('Checking line increase', () => { - expect(ops.onVar(0x000fffff, 0)).toEqual(1); // 0x0 === 0b0000, var0 = var0 - expect(ops.onVar(0x000fffff, 1)).toEqual(2); // 0x0 === 0b0000, var0 = var0 - expect(ops.onVar(0x000fffff, 100)).toEqual(101); // 0x0 === 0b0000, var0 = var0 + it("Checking lookAt() looking outside of the world - y", () => { + ops.vars[3] = h + 1; + expect(ops.operators[hex('101100 01 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,EMPTY,2,h + 1]); }); - }); - - describe('onConst() method', () => { - let org; - let ops; - let codeConstBits = OConfig.codeConstBits; - OConfig.codeConstBits = 16; - - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - afterAll (() => OConfig.codeConstBits = codeConstBits); - it("Checking different constant values", () => { - expect(ops.onConst(0x01dfffff, 0)).toEqual(1); // 0xdffff === 0b[11][0111111111111111]11, var3 = 0x7fff - expect(ops.vars).toEqual([0, 1, 2, 0x7fff]); - expect(ops.onConst(0x010fffff, 0)).toEqual(1); // 0x0ffff === 0b[00][0011111111111111]11, var0 = 0x3fff - expect(ops.vars).toEqual([0x3fff, 1, 2, 0x7fff]); - expect(ops.onConst(0x01000000, 0)).toEqual(1); // 0x00000 === 0b[00][0000000000000000]00, var0 = 0x0000 - expect(ops.vars).toEqual([0, 1, 2, 0x7fff]); + it('Checking lookAt() found an energy', () => { + global.man.world.setDot(0,3,0xaabbcc); + expect(ops.operators[hex('101100 01 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,ENERGY,2,3]); + global.man.world.setDot(0,3,0); }); - it("Checking onConst() method with 3 bits per var config", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([], [0, 1, 2, 3, 4, 5, 6, 7], org); - - expect(ops1.onConst(0x01ffffff, 0)).toEqual(1); // 0xfffff === 0b[111][1111111111111111]1, var7 = 0xffff - expect(ops1.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 0xffff]); - expect(ops1.onConst(0x015dffff, 0)).toEqual(1); // 0x5dfff === 0b[010][1110111111111111]1, var2 = 0xefff - expect(ops1.vars).toEqual([0, 1, 0xefff, 3, 4, 5, 6, 0xffff]); - expect(ops1.onConst(0x0100ffff, 0)).toEqual(1); // 0x00fff === 0b[000][0000011111111111]1, var0 = 0x07ff - expect(ops1.vars).toEqual([0x07ff, 1, 0xefff, 3, 4, 5, 6, 0xffff]); + it('Checking lookAt() found an object', () => { + global.man.world.setDot(0,3,0xaabbcc); + global.man.positions[0][3] = OBJECT_TYPES.TYPE_ENERGY2; + expect(ops.operators[hex('101100 01 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,OBJECT_TYPES.TYPE_ENERGY2,2,3]); + global.man.world.setDot(0,3,0); + global.man.positions[0][3] = 0; + }); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it('Checking lookAt() found an organism', () => { + global.man.world.setDot(0,3,0xaabbcc); + global.man.positions[0][3] = {energy: 123}; + expect(ops.operators[hex('101100 01 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,ORGANISM,2,3]); + global.man.world.setDot(0,3,0); + global.man.positions[0][3] = 0; }); - it('Checking line increase', () => { - expect(ops.onConst(0x01ffffff, 0)).toEqual(1); // 0xfffff === 0b[111][1111111111111111]1, var7 = 0xffff - expect(ops.onConst(0x015dffff, 1)).toEqual(2); // 0x5dfff === 0b[010][1110111111111111]1, var2 = 0xefff - expect(ops.onConst(0x0100ffff, 700)).toEqual(701); // 0x00fff === 0b[000][0000011111111111]1, var0 = 0x07ff + it('Checking lookAt() with floating coordinates', () => { + ops.vars[0] = .1; + ops.vars[3] = .2; + expect(ops.operators[hex('101100 01 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([.1,EMPTY,2,.2]); }); - }); - describe('onCondition() method', () => { - let org; - let ops; + describe('lookAt() method 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll (() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); + it("Checking lookAt() is found nothing", () => { + expect(ops.operators[hex('101100 001 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,EMPTY,2,3,4,5,6,7]); + }); - it("Checking conditions", () => { - expect(ops.onCondition(0x02ffffff, 0)).toEqual(1); //if(v3!==v3); - ops.offsets = [1]; - expect(ops.onCondition(0x021fffff, 0)).toEqual(1); //if(v0!==v1); - ops.offsets = [1]; - expect(ops.onCondition(0x021abfff, 0)).toEqual(1); //if(v0===v1); - ops.offsets = [1]; - expect(ops.onCondition(0x0213ffff, 0)).toEqual(1); //if(v0 < v1); - }); + it("Checking lookAt() looking outside of the world - x", () => { + ops.vars[0] = w + 1; + expect(ops.operators[hex('101100 001 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([w + 1,EMPTY,2,3,4,5,6,7]); + }); - it('Checking closing bracket offset', () => { - ops.offsets = [2]; - expect(ops.onCondition(0x02ffffff, 0)).toEqual(2); //if(v3!==v3); - ops.offsets = [1]; - expect(ops.onCondition(0x02ffffff, 0)).toEqual(1); //if(v3!==v3); - ops.offsets = [1]; - expect(ops.onCondition(0x0213ffff, 0)).toEqual(1); //if(v0 < v1); - }); + it("Checking lookAt() looking outside of the world - y", () => { + ops.vars[3] = h + 1; + expect(ops.operators[hex('101100 001 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,EMPTY,2,h + 1,4,5,6,7]); + }); - it("Checking onCondition() method with 3 bits per var config", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([], [0, 1, 2, 3, 4, 5, 6, 7], org); + it('Checking lookAt() found an energy', () => { + global.man.world.setDot(0,3,0xaabbcc); + expect(ops.operators[hex('101100 001 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,ENERGY,2,3,4,5,6,7]); + global.man.world.setDot(0,3,0); + }); - ops.offsets = [2]; - expect(ops.onCondition(0x02ffffff, 0)).toEqual(2); //if(v3!==v3); - ops.offsets = [2]; - expect(ops.onCondition(0x021fffff, 0)).toEqual(1); //if(v0!==v7); + it('Checking lookAt() found an object', () => { + global.man.world.setDot(0,3,0xaabbcc); + global.man.positions[0][3] = OBJECT_TYPES.TYPE_ENERGY2; + expect(ops.operators[hex('101100 001 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,OBJECT_TYPES.TYPE_ENERGY2,2,3,4,5,6,7]); + global.man.world.setDot(0,3,0); + global.man.positions[0][3] = 0; + }); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it('Checking lookAt() found an organism', () => { + global.man.world.setDot(0,3,0xaabbcc); + global.man.positions[0][3] = {energy: 123}; + expect(ops.operators[hex('101100 001 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,ORGANISM,2,3,4,5,6,7]); + global.man.world.setDot(0,3,0); + global.man.positions[0][3] = 0; + }); + + it('Checking lookAt() with floating coordinates', () => { + ops.vars[0] = .1; + ops.vars[3] = .2; + expect(ops.operators[hex('101100 001 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([.1,EMPTY,2,.2,4,5,6,7]); + }); }); }); - describe('onLoop() method', () => { - let org; - let ops; - - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - - it("Checking conditions", () => { - expect(ops.onCondition(0x03ffffff, 0)).toEqual(1); //while(v3!==v3); - ops.offsets = [1]; - expect(ops.onCondition(0x031fffff, 0)).toEqual(1); //while(v0!==v1); - ops.offsets = [1]; - expect(ops.onCondition(0x031abfff, 0)).toEqual(1); //while(v0===v1); - ops.offsets = [1]; - expect(ops.onCondition(0x0313ffff, 0)).toEqual(1); //while(v0 < v1); + describe('step() operator', () => { + it("Checking step left", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 0 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(1); }); - - it('Checking closing bracket offset', () => { - ops.offsets = [2]; - expect(ops.onCondition(0x03ffffff, 0)).toEqual(2); //while(v3!==v3); - ops.offsets = [1]; - expect(ops.onCondition(0x03ffffff, 0)).toEqual(1); //while(v3!==v3); - ops.offsets = [1]; - expect(ops.onCondition(0x0313ffff, 0)).toEqual(1); //while(v0 < v1); + it("Checking step left with no free space on the left", () => { + org.dir = DIRS.LEFT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 0 && y === 0 && x1 === -1 && y1 === 0).toBe(true); + o.x = x; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(0); }); - it("Checking onLoop() method with 3 bits per var config", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([], [0, 1, 2, 3, 4, 5, 6, 7], org); - - ops.offsets = [2]; - expect(ops.onCondition(0x03ffffff, 0)).toEqual(2); //while(v3!==v3); - ops.offsets = [2]; - expect(ops.onCondition(0x031fffff, 0)).toEqual(1); //while(v0!==v7); - - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking step right", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 2 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(2); + expect(org.y).toBe(1); + }); + it("Checking step right with no free space on the right", () => { + org.x = w - 1; + org.y = 0; + org.dir = DIRS.RIGHT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === w - 1 && y === 0 && x1 === w && y1 === 0).toBe(true); + o.x = x; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(w - 1); + expect(org.y).toBe(0); }); - }); - describe('onOperator() method', () => { - let org; - let ops; + it("Checking step up", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 0).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(0); + }); + it("Checking step up with no free space on the up", () => { + org.x = 0; + org.y = 0; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 0 && y === 0 && x1 === 0 && y1 === -1).toBe(true); + o.x = x1; + o.y = y; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(0); + }); + + describe('step() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll (() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); + it("Checking step left", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 0 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(1); + }); + it("Checking step left with no space", () => { + org.dir = DIRS.LEFT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 0 && y === 0 && x1 === -1 && y1 === 0).toBe(true); + o.x = x; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(0); + }); - it("Checking onOperator() method", () => { - expect(ops.onOperator(0x045a3fff, 0)).toEqual(1); //v1=v1>>v2; - expect(ops.vars).toEqual([0, 0, 2, 3]); - expect(ops.onOperator(0x046c7fff, 1)).toEqual(2); //v1=v2-v3; - expect(ops.vars).toEqual([0, -1, 2, 3]); - expect(ops.onOperator(0x046fffff, 3)).toEqual(4); //v1=v2<=v3; - expect(ops.vars).toEqual([0, 1, 2, 3]); - expect(ops.onOperator(0x04ffffff, 7)).toEqual(8); //v3=v3<=v3; - expect(ops.vars).toEqual([0, 1, 2, 1]); - expect(ops.onOperator(0x046d3fff, 0)).toEqual(1); //v1=v2%v3; - expect(ops.vars).toEqual([0, 0, 2, 1]); - - expect(ops.onOperator(0x046c3fff, 0)).toEqual(1); //v1=v2+v3; - expect(ops.vars).toEqual([0, 3, 2, 1]); - expect(ops.onOperator(0x046c7fff, 0)).toEqual(1); //v1=v2-v3; - expect(ops.vars).toEqual([0, 1, 2, 1]); - expect(ops.onOperator(0x046cbfff, 0)).toEqual(1); //v1=v2*v3; - expect(ops.vars).toEqual([0, 2, 2, 1]); - ops.vars = [0, 1, 2, 4]; - expect(ops.onOperator(0x046cffff, 0)).toEqual(1); //v1=v2/v3; - expect(ops.vars).toEqual([0, .5, 2, 4]); - ops.vars = [0, 1, 2, 3]; - expect(ops.onOperator(0x046d3fff, 0)).toEqual(1); //v1=v2%v3; - expect(ops.vars).toEqual([0, 2, 2, 3]); - }); + it("Checking step right", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 2 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(2); + expect(org.y).toBe(1); + }); + it("Checking step right with no free space on the right", () => { + org.x = w - 1; + org.y = 0; + org.dir = DIRS.RIGHT; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === w - 1 && y === 0 && x1 === w && y1 === 0).toBe(true); + o.x = x; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(w - 1); + expect(org.y).toBe(0); + }); - it('Checking onOperator() with 4 bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); + it("Checking step up", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 0).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(0); + }); + it("Checking step up with no free space on the up", () => { + org.x = 0; + org.y = 0; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 0 && y === 0 && x1 === 0 && y1 === -1).toBe(true); + o.x = x1; + o.y = y; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(0); + }); - expect(ops1.onOperator(0x04ffffff, 0)).toEqual(1); //v15=v15<=v15 - expect(ops1.vars).toEqual([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,1]); - expect(ops1.onOperator(0x046ff0ff, 0)).toEqual(1); //v6=v15+v15 - expect(ops1.vars).toEqual([0,1,2,3,4,5,2,7,8,9,10,11,12,13,14,1]); + it("Checking step down", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 2).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(2); + }); + it("Checking step down with no free space on the down", () => { + org.x = 0; + org.y = h - 1; + org.dir = DIRS.DOWN; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 0 && y === h - 1 && x1 === 0 && y1 === h).toBe(true); + o.x = x1; + o.y = y; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(h - 1); + }); + }); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking step up", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 0).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(0); + }); + it("Checking step up with no free space on the up", () => { + org.x = 0; + org.y = 0; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 0 && y === 0 && x1 === 0 && y1 === -1).toBe(true); + o.x = x1; + o.y = y; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(0); }); - it("Checking overflows", () => { - const max = Number.MAX_VALUE; - - ops.vars = [0, 1, max, max]; - expect(ops.onOperator(0x046c3fff, 0)).toEqual(1); //v1=v2+v3; - expect(ops.vars).toEqual([0, max, max, max]); - ops.vars = [0, 1, -max, max]; - expect(ops.onOperator(0x046c7fff, 0)).toEqual(1); //v1=v2-v3; - expect(ops.vars).toEqual([0, -max, -max, max]); - ops.vars = [0, 1, max, max]; - expect(ops.onOperator(0x046cbfff, 0)).toEqual(1); //v1=v2*v3; - expect(ops.vars).toEqual([0, max, max, max]); - ops.vars = [0, 1, max, 0]; - expect(ops.onOperator(0x046cffff, 0)).toEqual(1); //v1=v2/v3; - expect(ops.vars).toEqual([0, max, max, 0]); - ops.vars = [0, 1, 2, 0]; - expect(ops.onOperator(0x046d3fff, 0)).toEqual(1); //v1=v2%v3; - expect(ops.vars).toEqual([0, 0, 2, 0]); + it("Checking step down", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 2).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(2); + }); + it("Checking step down with no free space on the down", () => { + org.x = 0; + org.y = h - 1; + org.dir = DIRS.DOWN; + org.on(EVENTS.STEP, (o,x,y,x1,y1) => { + expect(x === 0 && y === h - 1 && x1 === 0 && y1 === h).toBe(true); + o.x = x1; + o.y = y; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(h - 1); }); }); - describe('onLookAt() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; - - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); + describe('dir() operator', () => { + it("Checking up direction 1", () => { + ops.vars[0] = DIRS.UP; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.UP); + }); + it("Checking up direction 2", () => { + ops.vars[0] = DIRS.UP + 4; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.UP); + }); + it("Checking up direction 3", () => { + ops.vars[0] = DIRS.UP + 4 + .1; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.UP); + }); - it("Checking onLookAt() is found nothing", () => { - org.on(EVENTS.GET_ENERGY, (x, y, ret) => { - expect(x).toBe(2); - expect(y).toBe(3); - ret.ret = 0; - }); - expect(ops.onLookAt(0x056fffff, 0, org)).toEqual(1); //v1=lookAt(v2,v3); - expect(ops.vars).toEqual([0,0,2,3]); + it("Checking right direction 1", () => { + ops.vars[0] = DIRS.RIGHT; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.RIGHT); + }); + it("Checking right direction 2", () => { + ops.vars[0] = DIRS.RIGHT + 4; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.RIGHT); + }); + it("Checking right direction 3", () => { + ops.vars[0] = DIRS.RIGHT + 4 + .1; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.RIGHT); }); - it("Checking onLookAt() looking outside of the world", () => { - ops.vars = [0, 1, 20, 30]; - expect(ops.onLookAt(0x056fffff, 0, org)).toEqual(1); //v1=lookAt(v2,v3); - expect(ops.vars).toEqual([0,0,20,30]); + it("Checking down direction 1", () => { + ops.vars[0] = DIRS.DOWN; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.DOWN); + }); + it("Checking down direction 2", () => { + ops.vars[0] = DIRS.DOWN + 4; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.DOWN); + }); + it("Checking down direction 3", () => { + ops.vars[0] = DIRS.DOWN + 4 + .1; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.DOWN); + }); - ops.vars = [0, 1, -20, -30]; - expect(ops.onLookAt(0x056fffff, 0, org)).toEqual(1); //v1=lookAt(v2,v3); - expect(ops.vars).toEqual([0,0,-20,-30]); + it("Checking left direction 1", () => { + ops.vars[0] = DIRS.LEFT; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.LEFT); + }); + it("Checking left direction 2", () => { + ops.vars[0] = DIRS.LEFT + 4; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.LEFT); + }); + it("Checking left direction 3", () => { + ops.vars[0] = DIRS.LEFT + 4 + .1; + expect(ops.operators[hex('101110 00')].call(ops, 0, hex('101110 00'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.LEFT); }); - it('Checking onLookAt() found an energy', () => { - org.on(EVENTS.GET_ENERGY, (x, y, ret) => { - expect(x).toBe(2); - expect(y).toBe(3); - ret.ret = 13; + it("Checking left direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 0 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(1); + }); + it("Checking right direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 2 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(2); + expect(org.y).toBe(1); + }); + it("Checking up direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 0).toBe(true); + o.x = x1; + o.y = y1; }); - expect(ops.onLookAt(0x056fffff, 0, org)).toEqual(1); //v1=lookAt(v2,v3); - expect(ops.vars).toEqual([0,13,2,3]); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(0); }); + it("Checking down direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 2).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(2); + }); + + describe('dir() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - it('Checking onLookAt() with 4 bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,7], org); + it("Checking up direction 1", () => { + ops.vars[0] = DIRS.UP; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.UP); + }); + it("Checking up direction 2", () => { + ops.vars[0] = DIRS.UP + 4; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.UP); + }); + it("Checking up direction 3", () => { + ops.vars[0] = DIRS.UP + 4 + .1; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.UP); + }); - org.on(EVENTS.GET_ENERGY, (x, y, ret) => { - expect(x).toBe(7); - expect(y).toBe(7); - ret.ret = 13; + it("Checking right direction 1", () => { + ops.vars[0] = DIRS.RIGHT; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.RIGHT); + }); + it("Checking right direction 2", () => { + ops.vars[0] = DIRS.RIGHT + 4; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.RIGHT); + }); + it("Checking right direction 3", () => { + ops.vars[0] = DIRS.RIGHT + 4 + .1; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.RIGHT); }); - expect(ops1.onLookAt(0x056fffff, 0, org)).toEqual(1); //v6=lookAt(v15,v15); - expect(ops1.vars).toEqual([0,1,2,3,4,5,13,7,8,9,10,11,12,13,14,7]); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); - }); + it("Checking down direction 1", () => { + ops.vars[0] = DIRS.DOWN; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.DOWN); + }); + it("Checking down direction 2", () => { + ops.vars[0] = DIRS.DOWN + 4; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.DOWN); + }); + it("Checking down direction 3", () => { + ops.vars[0] = DIRS.DOWN + 4 + .1; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.DOWN); + }); - it('Checking onLookAt() with floating coordinates', () => { - org.on(EVENTS.GET_ENERGY, (x, y, ret) => { - expect(x).toBe(0); - expect(y).toBe(4); - ret.ret = 13; + it("Checking left direction 1", () => { + ops.vars[0] = DIRS.LEFT; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.LEFT); + }); + it("Checking left direction 2", () => { + ops.vars[0] = DIRS.LEFT + 4; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.LEFT); + }); + it("Checking left direction 3", () => { + ops.vars[0] = DIRS.LEFT + 4 + .1; + expect(ops.operators[hex('101110 000')].call(ops, 0, hex('101110 000'), org)).toEqual(1); + expect(org.dir).toBe(DIRS.LEFT); + }); + + it("Checking left direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 0 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(0); + expect(org.y).toBe(1); + }); + it("Checking right direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 2 && y1 === 1).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(2); + expect(org.y).toBe(1); + }); + it("Checking up direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 0).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(0); + }); + it("Checking down direction while moving", () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + org.on(EVENTS.STEP, (o, x, y, x1, y1) => { + expect(x === 1 && y === 1 && x1 === 1 && y1 === 2).toBe(true); + o.x = x1; + o.y = y1; + }); + expect(ops.operators[hex('101101')].call(ops, 0, hex('101101'), org)).toEqual(1); + expect(org.x).toBe(1); + expect(org.y).toBe(2); }); - ops.vars = [0, 1, .1, 3.6]; - expect(ops.onLookAt(0x056fffff, 0, org)).toEqual(1); //v1=lookAt(v2,v3); - expect(ops.vars).toEqual([0,13,.1,3.6]); }); }); - describe('onEatLeft() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; + describe('myX() method', () => { + it("Checking simple values", () => { + org.x = 1; + expect(ops.operators[hex('101111 00')].call(ops, 0, hex('101111 00'), org)).toEqual(1); // v0=myX() + expect(ops.vars).toEqual([1,1,2,3]); + org.x = 3; + expect(ops.operators[hex('101111 01')].call(ops, 0, hex('101111 01'), org)).toEqual(1); // v1=myX() + expect(ops.vars).toEqual([1,3,2,3]); + org.x = 0; + expect(ops.operators[hex('101111 11')].call(ops, 0, hex('101111 11'), org)).toEqual(1); // v3=myX() + expect(ops.vars).toEqual([1,3,2,0]); + }); - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); + describe('myX() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - it("Checking eating nothing", () => { - ops.vars = [1, 0, 1, 2]; - expect(ops.onEatLeft(0x061fffff, 0, org)).toEqual(1); // v0=eatLeft(v1); - expect(ops.vars).toEqual([0, 0, 1, 2]); - }); - it("Checking eating nothing 2", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(1); - expect(y).toBe(3); - ret.ret = 0; + it("Checking simple values", () => { + org.x = 1; + expect(ops.operators[hex('101111 000')].call(ops, 0, hex('101111 000'), org)).toEqual(1); // v0=myX() + expect(ops.vars).toEqual([1,1,2,3,4,5,6,7]); + org.x = 3; + expect(ops.operators[hex('101111 001')].call(ops, 0, hex('101111 001'), org)).toEqual(1); // v1=myX() + expect(ops.vars).toEqual([1,3,2,3,4,5,6,7]); + org.x = 0; + expect(ops.operators[hex('101111 011')].call(ops, 0, hex('101111 011'), org)).toEqual(1); // v3=myX() + expect(ops.vars).toEqual([1,3,2,0,4,5,6,7]); }); - org.x = 2; - org.y = 3; - expect(ops.onEatLeft(0x061fffff, 0, org)).toEqual(1); // v0=eatLeft(v1); - expect(ops.vars).toEqual([0,1,2,3]); }); + }); - it("Checking eating energy", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(1); - expect(y).toBe(3); - ret.ret = 5; - }); - org.x = 2; + describe('myY() method', () => { + it("Checking simple values", () => { + org.y = 1; + expect(ops.operators[hex('110000 00')].call(ops, 0, hex('110000 00'), org)).toEqual(1); // v0=myY() + expect(ops.vars).toEqual([1,1,2,3]); org.y = 3; - expect(ops.onEatLeft(0x061fffff, 0, org)).toEqual(1); // v0=eatLeft(v1); - expect(ops.vars).toEqual([5,1,2,3]); + expect(ops.operators[hex('110000 01')].call(ops, 0, hex('110000 01'), org)).toEqual(1); // v1=myY() + expect(ops.vars).toEqual([1,3,2,3]); + org.y = 0; + expect(ops.operators[hex('110000 11')].call(ops, 0, hex('110000 11'), org)).toEqual(1); // v3=myY() + expect(ops.vars).toEqual([1,3,2,0]); }); - it('Checking eating with 3bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7], org); - - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(4); - expect(x).toBe(1); - expect(y).toBe(3); - ret.ret = 5; + describe('myY() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; }); - org.x = 2; - org.y = 3; - expect(ops1.onEatLeft(0x0633ffff, 0, org)).toEqual(1); // v1=eatLeft(v4); - expect(ops1.vars).toEqual([0,5,2,3,4,5,6,7]); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); - }) + it("Checking simple values", () => { + org.y = 1; + expect(ops.operators[hex('110000 000')].call(ops, 0, hex('110000 000'), org)).toEqual(1); // v0=myY() + expect(ops.vars).toEqual([1,1,2,3,4,5,6,7]); + org.y = 3; + expect(ops.operators[hex('110000 001')].call(ops, 0, hex('110000 001'), org)).toEqual(1); // v1=myY() + expect(ops.vars).toEqual([1,3,2,3,4,5,6,7]); + org.y = 0; + expect(ops.operators[hex('110000 011')].call(ops, 0, hex('110000 011'), org)).toEqual(1); // v3=myY() + expect(ops.vars).toEqual([1,3,2,0,4,5,6,7]); + }); + }); }); - describe('onEatRight() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; - - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); + describe('eat() operator', () => { + let maxEnergy = OConfig.orgMaxEnergy; + beforeEach(() => OConfig.orgMaxEnergy = 100); + afterEach (() => OConfig.orgMaxEnergy = maxEnergy); - it("Checking eating nothing", () => { - ops.vars = [1, 0, 1, 2]; - expect(ops.onEatRight(0x071fffff, 0, org)).toEqual(1); // v0=eatRight(v1); + it("Checking eating nothing 1", () => { + const energy = org.energy; + ops.vars = [0, 0, 1, 2]; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); expect(ops.vars).toEqual([0, 0, 1, 2]); + expect(org.energy).toEqual(energy); }); it("Checking eating nothing 2", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(3); - expect(y).toBe(3); - ret.ret = 0; - }); - org.x = 2; - org.y = 3; - expect(ops.onEatRight(0x071fffff, 0, org)).toEqual(1); // v0=eatRight(v1); - expect(ops.vars).toEqual([0,1,2,3]); + const energy = org.energy; + ops.vars = [1, 0, 1, 2]; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2]); + expect(org.energy).toEqual(energy); + }); + it("Checking eating negative value", () => { + const energy = org.energy; + ops.vars = [-1, 0, 1, 2]; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([-1, 0, 1, 2]); + expect(org.energy).toEqual(energy); }); it("Checking eating energy", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(3); - expect(y).toBe(3); - ret.ret = 5; - }); - org.x = 2; - org.y = 3; - expect(ops.onEatRight(0x071fffff, 0, org)).toEqual(1); // v0=eatRight(v1); - expect(ops.vars).toEqual([5,1,2,3]); + org.energy = 1; + ops.vars = [1, 0, 1, 2]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2]); + expect(org.energy).toEqual(2); + }); + it("Checking eating energy more then OConfig.orgMaxEnergy", () => { + org.energy = 1; + ops.vars = [100, 0, 1, 2]; + ops.world.setDot(1,0,100); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([100, 0, 1, 2]); + expect(org.energy).toEqual(100); }); - it('Checking eating with 3bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7], org); - - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(4); - expect(x).toBe(3); - expect(y).toBe(3); - ret.ret = 5; - }); - org.x = 2; - org.y = 3; - expect(ops1.onEatRight(0x0733ffff, 0, org)).toEqual(1); // v1=eatRight(v4); - expect(ops1.vars).toEqual([0,5,2,3,4,5,6,7]); - - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); - }) - }); - - describe('onEatUp() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; + it("Checking eating world object", () => { + org.energy = 1; + ops.vars = [1, 0, 1, 2]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY2; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2]); + expect(org.energy).toEqual(1); - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); + global.man.positions[1][0] = 0; + ops.world.setDot(1,0,0); + }); - it("Checking eating nothing", () => { + it("Checking eating out of the world", () => { + org.energy = 1; ops.vars = [1, 0, 1, 2]; - expect(ops.onEatUp(0x081fffff, 0, org)).toEqual(1); // v0=eatUp(v1); - expect(ops.vars).toEqual([0, 0, 1, 2]); - }); - it("Checking eating nothing 2", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(2); - expect(y).toBe(2); - ret.ret = 0; - }); - org.x = 2; - org.y = 3; - expect(ops.onEatUp(0x081fffff, 0, org)).toEqual(1); // v0=eatUp(v1); - expect(ops.vars).toEqual([0,1,2,3]); + org.dir = DIRS.UP; + org.x = 0; + org.y = 0; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2]); + expect(org.energy).toEqual(1); }); - it("Checking eating energy", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(2); - expect(y).toBe(2); - ret.ret = 5; + it("Checking eating other organism", () => { + const org2 = new OrganismDos(1, 0, 0, {}); + const energy = org2.energy; + org.energy = 1; + ops.vars = [1, 0, 1, 2]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2]); + expect(org.energy).toEqual(2); + expect(org2.energy).toEqual(energy - 1); + + global.man.positions[1][0] = 0; + ops.world.setDot(1,0,0); + }); + it("Checking eating and kill other organism", () => { + const org2 = new OrganismDos(1, 0, 0, {}); + const energy = org2.energy = 1; + org.energy = 1; + ops.vars = [energy, 0, 1, 2]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110001 00')].call(ops, 0, hex('110001 00'), org)).toEqual(1); + expect(ops.vars).toEqual([energy, 0, 1, 2]); + expect(org.energy).toEqual(2); + expect(org2.energy).toEqual(0); + expect(org2.vm).toEqual(null); + + global.man.positions[1][0] = 0; + ops.world.setDot(1,0,0); + }); + + describe('eat() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; }); - org.x = 2; - org.y = 3; - expect(ops.onEatUp(0x081fffff, 0, org)).toEqual(1); // v0=eatUp(v1); - expect(ops.vars).toEqual([5,1,2,3]); - }); - it('Checking eating with 3bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7], org); + it("Checking eating nothing 1", () => { + const energy = org.energy; + ops.vars = [0, 0, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 0, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(energy); + }); + it("Checking eating nothing 2", () => { + const energy = org.energy; + ops.vars = [1, 0, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(energy); + }); + it("Checking eating negative value", () => { + const energy = org.energy; + ops.vars = [-1, 0, 1, 2, 3, 4, 5, 6]; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([-1, 0, 1, 2, 3, 4, 5, 6]); + expect(org.energy).toEqual(energy); + }); - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(4); - expect(x).toBe(2); - expect(y).toBe(2); - ret.ret = 5; + it("Checking eating energy", () => { + org.energy = 1; + ops.vars = [1, 0, 1, 2, 3, 4, 5, 6, 7]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(2); + }); + it("Checking eating energy more then OConfig.orgMaxEnergy", () => { + org.energy = 1; + ops.vars = [100, 0, 1, 2, 3, 4, 5, 6]; + ops.world.setDot(1,0,100); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([100, 0, 1, 2, 3, 4, 5, 6]); + expect(org.energy).toEqual(100); }); - org.x = 2; - org.y = 3; - expect(ops1.onEatUp(0x0833ffff, 0, org)).toEqual(1); // v1=eatUp(v4); - expect(ops1.vars).toEqual([0,5,2,3,4,5,6,7]); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); - }) - }); + it("Checking eating world object", () => { + org.energy = 1; + ops.vars = [1, 0, 1, 2, 3, 4, 5, 6, 7]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY2; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(1); + + global.man.positions[1][0] = 0; + ops.world.setDot(1,0,0); + }); - describe('onEatDown() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; + it("Checking eating out of the world", () => { + org.energy = 1; + ops.vars = [1, 0, 1, 2, 3, 4, 5, 6]; + org.dir = DIRS.UP; + org.x = 0; + org.y = 0; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2, 3, 4, 5, 6]); + expect(org.energy).toEqual(1); + }); - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); - - it("Checking eating nothing", () => { - ops.vars = [1, 0, 1, 2]; - expect(ops.onEatDown(0x091fffff, 0, org)).toEqual(1); // v0=eatDown(v1); - expect(ops.vars).toEqual([0, 0, 1, 2]); - }); - it("Checking eating nothing 2", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(2); - expect(y).toBe(4); - ret.ret = 0; + it("Checking eating other organism", () => { + const org2 = new OrganismDos(1, 0, 0, {}); + const energy = org2.energy; + org.energy = 1; + ops.vars = [1, 0, 1, 2, 3, 4, 5, 6, 7]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 0, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(2); + expect(org2.energy).toEqual(energy - 1); + + global.man.positions[1][0] = 0; + ops.world.setDot(1,0,0); }); - org.x = 2; - org.y = 3; - expect(ops.onEatDown(0x091fffff, 0, org)).toEqual(1); // v0=eatDown(v1); - expect(ops.vars).toEqual([0,1,2,3]); - }); - - it("Checking eating energy", () => { - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(1); - expect(x).toBe(2); - expect(y).toBe(4); - ret.ret = 5; + it("Checking eating and kill other organism", () => { + const org2 = new OrganismDos(1, 0, 0, {}); + const energy = org2.energy = 1; + org.energy = 1; + ops.vars = [energy, 0, 1, 2, 3, 4, 5, 6]; + ops.world.setDot(1,0,10); + org.dir = DIRS.UP; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110001 000')].call(ops, 0, hex('110001 000'), org)).toEqual(1); + expect(ops.vars).toEqual([energy, 0, 1, 2, 3, 4, 5, 6]); + expect(org.energy).toEqual(2); + expect(org2.energy).toEqual(0); + expect(org2.vm).toEqual(null); + + global.man.positions[1][0] = 0; + ops.world.setDot(1,0,0); }); - org.x = 2; - org.y = 3; - expect(ops.onEatDown(0x091fffff, 0, org)).toEqual(1); // v0=eatDown(v1); - expect(ops.vars).toEqual([5,1,2,3]); }); - - it('Checking eating with 3bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 3; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7], org); - - org.on(EVENTS.EAT, (org, x, y, ret) => { - expect(ret.ret).toBe(4); - expect(x).toBe(2); - expect(y).toBe(4); - ret.ret = 5; - }); - org.x = 2; - org.y = 3; - expect(ops1.onEatDown(0x0933ffff, 0, org)).toEqual(1); // v1=eatDown(v4); - expect(ops1.vars).toEqual([0,5,2,3,4,5,6,7]); - - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); - }) }); - describe('onStepLeft() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; - - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); - - it("Checking step left", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 2 && y2 === 4).toBe(true); - }); - expect(ops.onStepLeft(0x0a1fffff, 0, org)).toEqual(1); // v0=stepLeft(); - expect(ops.vars).toEqual([2,1,2,3]); - expect(org.x).toBe(2); - expect(org.y).toBe(4); + describe('put() operator', () => { + it("Checking put nothing", () => { + const energy = org.energy; + ops.vars = [0, 1, 2, 3]; + expect(ops.operators[hex('110010 00')].call(ops, 0, hex('110010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(org.energy).toEqual(energy); }); - it("Checking step left with no free space on the left", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - expect(x1 === 3 && y1 === 4 && x2 === 2 && y2 === 4).toBe(true); - }); - expect(ops.onStepLeft(0x0a1fffff, 0, org)).toEqual(1); // v0=stepLeft(); - expect(ops.vars).toEqual([0,1,2,3]); - expect(org.x).toBe(3); - expect(org.y).toBe(4); + it("Checking put of energy", () => { + const energy = org.energy = 10; + org.x = 1; + org.y = 1; + ops.vars = [1, 1, 2, 3]; + expect(ops.operators[hex('110010 00')].call(ops, 0, hex('110010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3]); + expect(org.energy).toEqual(energy - 1); }); - it("Checking step left with 4 bits per var", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); - - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 2 && y2 === 4).toBe(true); - }); - expect(ops1.onStepLeft(0x0a1fffff, 0, org)).toEqual(1); // v1=stepLeft(); - expect(ops1.vars).toEqual([0,2,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); - expect(org.x).toBe(2); - expect(org.y).toBe(4); - - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking put huge amount of energy", () => { + org.energy = 0xfffffff; + org.x = 1; + org.y = 1; + ops.vars = [0xfffffff, 1, 2, 3]; + expect(ops.operators[hex('110010 00')].call(ops, 0, hex('110010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([0xfffffff, 1, 2, 3]); + expect(org.energy).toEqual(0xfffffff - 0xffffff); }); - }); - describe('onStepRight() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; - - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); - - it("Checking step right", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 4 && y2 === 4).toBe(true); - }); - expect(ops.onStepRight(0x0a1fffff, 0, org)).toEqual(1); // v0=stepRight(); - expect(ops.vars).toEqual([4,1,2,3]); - expect(org.x).toBe(4); - expect(org.y).toBe(4); + it("Checking killing of organism while put energy", () => { + org.energy = 1; + org.x = 1; + org.y = 1; + ops.vars = [1, 1, 2, 3]; + expect(ops.operators[hex('110010 00')].call(ops, 0, hex('110010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3]); + expect(org.energy).toEqual(0); }); - it("Checking step right with no free space on the right", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - expect(x1 === 3 && y1 === 4 && x2 === 4 && y2 === 4).toBe(true); - }); - expect(ops.onStepRight(0x0a1fffff, 0, org)).toEqual(1); // v0=stepRight(); - expect(ops.vars).toEqual([0,1,2,3]); - expect(org.x).toBe(3); - expect(org.y).toBe(4); + it("Checking put if organism is out of bounds", () => { + org.energy = 1; + org.x = 0; + org.y = 0; + org.dir = DIRS.UP; + ops.vars = [1, 1, 2, 3]; + expect(ops.operators[hex('110010 00')].call(ops, 0, hex('110010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3]); + expect(org.energy).toEqual(1); }); - it("Checking step right with 4 bits per var", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); - - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 4 && y2 === 4).toBe(true); - }); - expect(ops1.onStepRight(0x0a1fffff, 0, org)).toEqual(1); // v1=stepRight(); - expect(ops1.vars).toEqual([0,4,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); - expect(org.x).toBe(4); - expect(org.y).toBe(4); - - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking negative put", () => { + org.energy = 1; + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + ops.vars = [-1, 1, 2, 3]; + expect(ops.operators[hex('110010 00')].call(ops, 0, hex('110010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([-1, 1, 2, 3]); + expect(org.energy).toEqual(1); }); - }); - describe('onStepUp() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; - - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); - - it("Checking step up", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 3 && y2 === 3).toBe(true); + it("Checking put if something is there", () => { + org.energy = 1; + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + ops.vars = [1, 1, 2, 3]; + global.man.world.setDot(1,0,0xaabbcc); + expect(ops.operators[hex('110010 00')].call(ops, 0, hex('110010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3]); + expect(org.energy).toEqual(1); + + global.man.world.setDot(1,0,0); + }); + + describe('eat() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); }); - expect(ops.onStepUp(0x0a1fffff, 0, org)).toEqual(1); // v0=stepUp(); - expect(ops.vars).toEqual([3,1,2,3]); - expect(org.x).toBe(3); - expect(org.y).toBe(3); - }); - - it("Checking step up with no free space on above", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - expect(x1 === 3 && y1 === 4 && x2 === 3 && y2 === 3).toBe(true); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); }); - expect(ops.onStepUp(0x0a1fffff, 0, org)).toEqual(1); // v0=stepUp(); - expect(ops.vars).toEqual([0,1,2,3]); - expect(org.x).toBe(3); - expect(org.y).toBe(4); - }); - - it("Checking step up with 4 bits per var", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); - - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 3 && y2 === 3).toBe(true); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; }); - expect(ops1.onStepUp(0x0a1fffff, 0, org)).toEqual(1); // v1=stepUp(); - expect(ops1.vars).toEqual([0,3,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); - expect(org.x).toBe(3); - expect(org.y).toBe(3); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); - }); - }); - - describe('onStepDown() method', () => { - let org; - let ops; - const w = Config.worldWidth; - const h = Config.worldHeight; + it("Checking put nothing", () => { + const energy = org.energy; + ops.vars = [0, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110010 000')].call(ops, 0, hex('110010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(energy); + }); - beforeEach(() => {Config.worldHeight = Config.worldWidth = 10;org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy(); Config.worldHeight = h; Config.worldWidth = w}); + it("Checking put of energy", () => { + const energy = org.energy = 10; + org.x = 1; + org.y = 1; + ops.vars = [1, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110010 000')].call(ops, 0, hex('110010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(energy - 1); + }); - it("Checking step down", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 3 && y2 === 5).toBe(true); + it("Checking put huge amount of energy", () => { + org.energy = 0xfffffff; + org.x = 1; + org.y = 1; + ops.vars = [0xfffffff, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110010 000')].call(ops, 0, hex('110010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([0xfffffff, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(0xfffffff - 0xffffff); }); - expect(ops.onStepDown(0x0a1fffff, 0, org)).toEqual(1); // v0=stepDown(); - expect(ops.vars).toEqual([5,1,2,3]); - expect(org.x).toBe(3); - expect(org.y).toBe(5); - }); - it("Checking step down with no free space below", () => { - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - expect(x1 === 3 && y1 === 4 && x2 === 3 && y2 === 5).toBe(true); + it("Checking killing of organism while put energy", () => { + org.energy = 1; + org.x = 1; + org.y = 1; + ops.vars = [1, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110010 000')].call(ops, 0, hex('110010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(0); }); - expect(ops.onStepDown(0x0a1fffff, 0, org)).toEqual(1); // v0=stepDown(); - expect(ops.vars).toEqual([0,1,2,3]); - expect(org.x).toBe(3); - expect(org.y).toBe(4); - }); - it("Checking step down with 4 bits per var", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); + it("Checking put if organism is out of bounds", () => { + org.energy = 1; + org.x = 0; + org.y = 0; + org.dir = DIRS.UP; + ops.vars = [1, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110010 000')].call(ops, 0, hex('110010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(1); + }); - org.x = 3; - org.y = 4; - org.on(EVENTS.STEP, (org, x1, y1, x2, y2) => { - org.x = x2; - org.y = y2; - expect(x1 === 3 && y1 === 4 && x2 === 3 && y2 === 5).toBe(true); + it("Checking negative put", () => { + org.energy = 1; + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + ops.vars = [-1, 1, 2, 3, 4, 5, 6, 7]; + expect(ops.operators[hex('110010 000')].call(ops, 0, hex('110010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([-1, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(1); }); - expect(ops1.onStepDown(0x0a1fffff, 0, org)).toEqual(1); // v1=stepDown(); - expect(ops1.vars).toEqual([0,5,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); - expect(org.x).toBe(3); - expect(org.y).toBe(5); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking put if something is there", () => { + org.energy = 1; + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + ops.vars = [1, 1, 2, 3, 4, 5, 6, 7]; + global.man.world.setDot(1,0,0xaabbcc); + expect(ops.operators[hex('110010 000')].call(ops, 0, hex('110010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3, 4, 5, 6, 7]); + expect(org.energy).toEqual(1); + + global.man.world.setDot(1,0,0); + }); }); }); - describe('onFromMem() method', () => { - let org; - let ops; - let mbits; + describe('energy() operator', () => { + let maxEnergy = OConfig.orgMaxEnergy; + beforeEach(() => OConfig.orgMaxEnergy = Helper.getColor(EConfig.colorIndex) * 32); + afterEach (() => OConfig.orgMaxEnergy = maxEnergy); - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - beforeAll(() => {mbits = OConfig.orgMemBits; OConfig.orgMemBits = 2}); - afterAll(() => OConfig.orgMemBits = mbits); - - it("Checking getting value by constant", () => { - org.mem.splice(0, org.mem.length, ...[1,2,3,4]); - expect(ops.onFromMem(0x0b10ffff, 0, org)).toEqual(1); //v0=fromMem(); - expect(ops.vars).toEqual([1,1,2,3]); - - org.mem.splice(0, org.mem.length, ...[0,1,2,3]); - expect(ops.onFromMem(0x0b50ffff, 0, org)).toEqual(1); //v1=fromMem(); - expect(ops.vars).toEqual([1,0,2,3]); + it("Checking energy nothing", () => { + const energy = org.energy; + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + expect(org.energy).toEqual(energy); }); - it("Checking getting value by variable value", () => { - org.mem.splice(0, org.mem.length, ...[1,2,3,4]); - expect(ops.onFromMem(0x0b1fffff, 0, org)).toEqual(1); //v0=fromMem(); - expect(ops.vars).toEqual([2,1,2,3]); + it("Two dots of e0 should give 2x energy 1", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(1,0,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 2", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(2,0,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 3", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeeee); + global.man.world.setDot(2,0,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 4", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(0,1,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 5", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[0][2] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(0,2,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 6", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,1,0xeeeeee); + global.man.world.setDot(2,1,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 7", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][2] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(2,2,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[2][2]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,2)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 8", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][2] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,2,0xeeeeee); + global.man.world.setDot(2,0,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + }); + it("Two dots of e0 should give 2x energy 8", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[2][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][2] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(2,1,0xeeeeee); + global.man.world.setDot(2,2,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); - org.mem.splice(0, org.mem.length, ...[0,1,2,3]); - expect(ops.onFromMem(0x0b58ffff, 0, org)).toEqual(1); //v1=fromMem(); - expect(ops.vars).toEqual([2,1,2,3]); + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.positions[2][2]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(global.man.world.getDot(2,2)).toEqual(EMPTY); }); - it("Checking getting value by variable floating value", () => { - ops.vars.splice(0, ops.vars.length, ...[.1,3.2,.3,.4]); - org.mem.splice(0, org.mem.length, ...[1,2,3,4]); - expect(ops.onFromMem(0x0b1fffff, 0, org)).toEqual(1); //v0=fromMem(); - expect(ops.vars).toEqual([4,3.2,.3,.4]); + it("Two dots of e0 should not give 2x energy if they are distant 1", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[3][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeeee); + global.man.world.setDot(3,0,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[1][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[3][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeeee); + expect(global.man.world.getDot(3,0)).toEqual(0xeeeeee); + + global.man.positions[1][0] = EMPTY; + global.man.positions[3][0] = EMPTY; + global.man.world.setDot(1,0,EMPTY); + global.man.world.setDot(3,0,EMPTY); + }); + it("Two dots of e0 should not give 2x energy if they are distant 2", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[0][3] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(0,3,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[0][3]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(0,0)).toEqual(0xeeeeee); + expect(global.man.world.getDot(0,3)).toEqual(0xeeeeee); + + global.man.positions[0][0] = EMPTY; + global.man.positions[0][3] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + global.man.world.setDot(0,3,EMPTY); + }); + + it("One e0 and one e1 should not give 2x energy", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][2] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.world.setDot(0,2,0xeeeee0); + global.man.world.setDot(2,0,0xeeeee1); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][2]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[2][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY1); + expect(global.man.world.getDot(0,2)).toEqual(0xeeeee0); + expect(global.man.world.getDot(2,0)).toEqual(0xeeeee1); + + global.man.positions[0][2] = EMPTY; + global.man.positions[2][0] = EMPTY; + global.man.world.setDot(0,2,EMPTY); + global.man.world.setDot(2,0,EMPTY); + }); + it("One e1 and two e0 should give 2x energy", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[0][2] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeee1); + global.man.world.setDot(0,1,0xeeeee0); + global.man.world.setDot(0,2,0xeeeee0); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 2); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY1); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(0xeeeee1); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + + global.man.positions[0][0] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + }); + + describe("e1 relates tests", () => { + it("Three e0 should create one e1", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(1,0,0xeeeeee); + global.man.world.setDot(2,0,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY1); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0) > 0).toEqual(true); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + + global.man.positions[0][0] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + }); + it("Four e0 should create one e1 and one e0", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeeee); + global.man.world.setDot(1,0,0xeeeeee); + global.man.world.setDot(2,0,0xeeeeee); + global.man.world.setDot(0,1,0xeeeeee); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY1); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0) > 0).toEqual(true); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(0xeeeeee); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + + global.man.positions[0][0] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + }); - ops.vars.splice(0, ops.vars.length, ...[.1,3.2,.3,.4]); - org.mem.splice(0, org.mem.length, ...[0,1,2,3]); - expect(ops.onFromMem(0x0b58ffff, 0, org)).toEqual(1); //v1=fromMem(); - expect(ops.vars).toEqual([.1,3,.3,.4]); + it("Two e1 should give 4x energy increase 1", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.world.setDot(0,0,0xeeeee1); + global.man.world.setDot(1,0,0xeeeee1); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 4); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + }); + it("Two e1 should give 4x energy increase 2", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.world.setDot(0,0,0xeeeee1); + global.man.world.setDot(2,0,0xeeeee1); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 4); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + }); + it("Two e1 should give 4x energy increase 3", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.world.setDot(0,0,0xeeeee1); + global.man.world.setDot(0,1,0xeeeee1); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 4); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + }); + it("Two e1 should give 4x energy increase 4", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.positions[0][2] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.world.setDot(0,0,0xeeeee1); + global.man.world.setDot(0,2,0xeeeee1); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 4); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + }); + it("Two e1 should give 4x energy increase 4", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.positions[2][2] = OBJECT_TYPES.TYPE_ENERGY1; + global.man.world.setDot(0,0,0xeeeee1); + global.man.world.setDot(2,2,0xeeeee1); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 4); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[2][2]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,2)).toEqual(EMPTY); + }); }); - it("Checking getting value by variable value with 4 bits per var", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); - - org.mem.splice(0, org.mem.length, ...[1,2,3,4]); - expect(ops1.onFromMem(0x0b0fffff, 0, org)).toEqual(1); //v1=fromMem(); - expect(ops1.vars).toEqual([1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); + describe("e4 relates tests", () => { + it("Three e3 should create one e4", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY3; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY3; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY3; + global.man.world.setDot(0,0,0xeeeee3); + global.man.world.setDot(1,0,0xeeeee3); + global.man.world.setDot(2,0,0xeeeee3); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY4); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0) > 0).toEqual(true); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + + global.man.positions[0][0] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + }); + it("Four e3 should create one e4 and one e3", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY3; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY3; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY3; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY3; + global.man.world.setDot(0,0,0xeeeee3); + global.man.world.setDot(1,0,0xeeeee3); + global.man.world.setDot(2,0,0xeeeee3); + global.man.world.setDot(0,1,0xeeeee3); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY4); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY3); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0) > 0).toEqual(true); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(0xeeeee3); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + + global.man.positions[0][0] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + }); - org.mem.splice(0, org.mem.length, ...[0,7,2,3]); - expect(ops1.onFromMem(0x0b51ffff, 0, org)).toEqual(1); //v5=fromMem(); - expect(ops1.vars).toEqual([1,1,2,3,4,7,6,7,8,9,10,11,12,13,14,15]); + it("Two e4 should give 32x energy increase 1", () => { + const energy = org.energy = 0; // shouldn't be more then 0 + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.world.setDot(0,0,0xeeeee4); + global.man.world.setDot(1,0,0xeeeee4); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 32); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + }); + it("Two e4 should give 32x energy increase 2", () => { + const energy = org.energy = 0; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.world.setDot(0,0,0xeeeee4); + global.man.world.setDot(2,0,0xeeeee4); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 32); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + }); + it("Two e4 should give 32x energy increase 3", () => { + const energy = org.energy = 0; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.world.setDot(0,0,0xeeeee4); + global.man.world.setDot(0,1,0xeeeee4); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 32); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + }); + it("Two e4 should give 32x energy increase 4", () => { + const energy = org.energy = 0; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.positions[0][2] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.world.setDot(0,0,0xeeeee4); + global.man.world.setDot(0,2,0xeeeee4); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 32); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + }); + it("Two e4 should give 32x energy increase 5", () => { + const energy = org.energy = 0; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.positions[2][2] = OBJECT_TYPES.TYPE_ENERGY4; + global.man.world.setDot(0,0,0xeeeee4); + global.man.world.setDot(2,2,0xeeeee4); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 32); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[2][2]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,2)).toEqual(EMPTY); + }); + }); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + describe("Complex energy objects scenarios", () => { + it("Six e0 should give two e1 and 4x energy", () => { + const energy = org.energy = 1; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[0][2] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.positions[2][1] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeee0); + global.man.world.setDot(1,0,0xeeeee0); + global.man.world.setDot(2,0,0xeeeee0); + global.man.world.setDot(0,1,0xeeeee0); + global.man.world.setDot(0,2,0xeeeee0); + global.man.world.setDot(2,1,0xeeeee0); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY1); + expect(global.man.positions[1][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[2][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(0,0) > 0).toEqual(true); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeee0); + expect(global.man.world.getDot(2,0)).toEqual(0xeeeee0); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeee0); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy); + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY1); + expect(global.man.positions[1][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY1); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0) > 0).toEqual(true); + expect(global.man.world.getDot(1,0) > 0).toEqual(true); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(ops.operators[hex('110011')].call(ops, 0, hex('110011'), org)).toEqual(1); + + expect(org.energy).toEqual(energy + Helper.getColor(EConfig.colorIndex) * 4); + expect(global.man.positions[0][0]).toEqual(EMPTY); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.positions[2][0]).toEqual(EMPTY); + expect(global.man.positions[0][1]).toEqual(EMPTY); + expect(global.man.positions[0][2]).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(0,0)).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.world.getDot(2,0)).toEqual(EMPTY); + expect(global.man.world.getDot(0,1)).toEqual(EMPTY); + expect(global.man.world.getDot(0,2)).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + }); }); }); - describe('onToMem() method', () => { - let org; - let ops; - let mbits; + describe('pick() operator', () => { + it("Checking picking from up to right 1", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeee0); + + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); + it("Checking picking from up to right 2", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT + OFFSX.length; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeee0); + + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); + it("Checking picking from up to down", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.DOWN; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[1][2]).toEqual(EMPTY); + expect(global.man.world.getDot(1,2)).toEqual(EMPTY); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[1][2]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(1,2)).toEqual(0xeeeee0); + + global.man.positions[1][2] = EMPTY; + global.man.world.setDot(1,2,EMPTY); + }); + + it("Checking picking out of the world", () => { + org.x = 1; + org.y = 0; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeee0); + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(2,0,0xeeeee0); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[2][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(0,0)).toEqual(0xeeeee0); + expect(global.man.world.getDot(2,0)).toEqual(0xeeeee0); + + global.man.positions[0][0] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + global.man.positions[2][0] = EMPTY; + global.man.world.setDot(2,0,EMPTY); + }); + + it("Checking picking other organism", () => { + const org2 = new OrganismDos(1, 0, 0, {}); + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.positions[1][0] = org2; + global.man.world.setDot(1,0,0xeeeee0); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(org2); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + + global.man.positions[1][0] = EMPTY; + global.man.world.setDot(1,0,EMPTY); + org2.destroy(); + }); + + it("Checking picking simple energy", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xeeeeee); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeeee); + + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); + + it("Checking impossible picking simple energy", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xeeeee0); + global.man.world.setDot(2,1,0xeeeee1); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeee1); + + global.man.positions[1][0] = EMPTY; + global.man.world.setDot(1,0,EMPTY); + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); + + it("Checking picking from up to up", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.UP; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(ops.operators[hex('110100 00')].call(ops, 0, hex('110100 00'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + + global.man.positions[1][0] = EMPTY; + global.man.world.setDot(1,0,EMPTY); + }); + + describe('pick() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([1], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - beforeAll(() => {mbits = OConfig.orgMemBits; OConfig.orgMemBits = 2}); - afterAll(() => OConfig.orgMemBits = mbits); + it("Checking picking from up to right 1", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeee0); + + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); + it("Checking picking from up to right 2", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT + OFFSX.length; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeee0); + + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); + it("Checking picking from up to down", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.DOWN; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[1][2]).toEqual(EMPTY); + expect(global.man.world.getDot(1,2)).toEqual(EMPTY); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[1][2]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(1,2)).toEqual(0xeeeee0); + + global.man.positions[1][2] = EMPTY; + global.man.world.setDot(1,2,EMPTY); + }); - it("Checking setting value by constant", () => { - org.mem.splice(0, org.mem.length, ...[1,2,3,4]); - expect(ops.onToMem(0x0b17ffff, 0, org)).toEqual(1); //toMem(v0, 3); - expect(org.mem).toEqual([1,2,3,0]); - }); + it("Checking picking out of the world", () => { + org.x = 1; + org.y = 0; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.positions[0][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(0,0,0xeeeee0); + global.man.positions[2][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(2,0,0xeeeee0); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[0][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.positions[2][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(0,0)).toEqual(0xeeeee0); + expect(global.man.world.getDot(2,0)).toEqual(0xeeeee0); + + global.man.positions[0][0] = EMPTY; + global.man.world.setDot(0,0,EMPTY); + global.man.positions[2][0] = EMPTY; + global.man.world.setDot(2,0,EMPTY); + }); - it("Checking setting value by variable value", () => { - org.mem.splice(0, org.mem.length, ...[0,2,3,4]); - expect(ops.onToMem(0x0b1fffff, 0, org)).toEqual(1); //toMem(v0, v0); - expect(org.mem).toEqual([1,2,3,4]); - }); + it("Checking picking other organism", () => { + const org2 = new OrganismDos(1, 0, 0, {}); + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.positions[1][0] = org2; + global.man.world.setDot(1,0,0xeeeee0); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(org2); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + + global.man.positions[1][0] = EMPTY; + global.man.world.setDot(1,0,EMPTY); + org2.destroy(); + }); - it("Checking setting value by variable value with 4 bits per var", () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); + it("Checking picking simple energy", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xeeeeee); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(EMPTY); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeeee); + + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); - org.mem.splice(0, org.mem.length, ...[0,2,3,4]); - expect(ops1.onToMem(0x0b1fffff, 0, org)).toEqual(1); //toMem(v0, v0); - expect(org.mem).toEqual([0,15,3,4]); + it("Checking impossible picking simple energy", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.RIGHT; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xeeeee0); + global.man.world.setDot(2,1,0xeeeee1); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(0xeeeee1); + + global.man.positions[1][0] = EMPTY; + global.man.world.setDot(1,0,EMPTY); + global.man.positions[2][1] = EMPTY; + global.man.world.setDot(2,1,EMPTY); + }); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking picking from up to up", () => { + org.x = 1; + org.y = 1; + ops.vars[0] = DIRS.UP; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + global.man.world.setDot(1,0,0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + expect(ops.operators[hex('110100 000')].call(ops, 0, hex('110100 000'), org)).toEqual(1); + + expect(global.man.positions[1][0]).toEqual(OBJECT_TYPES.TYPE_ENERGY0); + expect(global.man.world.getDot(1,0)).toEqual(0xeeeee0); + expect(global.man.positions[2][1]).toEqual(EMPTY); + expect(global.man.world.getDot(2,1)).toEqual(EMPTY); + + global.man.positions[1][0] = EMPTY; + global.man.world.setDot(1,0,EMPTY); + }); }); }); - describe('onMyX() method', () => { - let org; - let ops; + describe('say() operator', () => { + it("Checking say to nothing", () => { + ops.vars[0] = 1; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + + expect(org.msg).toEqual(0); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1, 0)).toEqual(EMPTY); + }); + + it("Checking say to other organism 1", () => { + const org2 = new OrganismDos(1, 1, 0, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + global.man.positions[1][0] = EMPTY; + }); + it("Checking say to other organism 2", () => { + const org2 = new OrganismDos(1, 1, 1, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][1] = org2; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + global.man.positions[1][1] = EMPTY; + }); + it("Checking say to other organism 3", () => { + const org2 = new OrganismDos(1, 0, 1, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[0][1] = org2; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + global.man.positions[0][1] = EMPTY; + }); + it("Checking say to other organism twice", () => { + const org2 = new OrganismDos(1, 1, 0, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + + ops.vars[0] = 12; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + expect(org2.msg).toEqual(12); + expect(ops.vars).toEqual([12, 1, 2, 3]); + + global.man.positions[1][0] = EMPTY; + }); + + it("Checking say to many organisms 1", () => { + const org2 = new OrganismDos(1, 1, 0, {}); + const org3 = new OrganismDos(1, 1, 1, {}); + + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][0] = org2; + global.man.positions[1][1] = org3; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(org3.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + + global.man.positions[1][0] = EMPTY; + global.man.positions[1][1] = EMPTY; + org2.destroy(); + org3.destroy(); + }); + it("Checking say to many organisms 2", () => { + const org2 = new OrganismDos(1, 1, 1, {}); + const org3 = new OrganismDos(1, 0, 1, {}); + + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][1] = org2; + global.man.positions[0][1] = org3; + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(org3.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + global.man.positions[1][1] = EMPTY; + global.man.positions[0][1] = EMPTY; + org2.destroy(); + org3.destroy(); + }); + it("Checking say to 8 organisms", () => { + const org1 = new OrganismDos(1, 0, 0, {}); + const org2 = new OrganismDos(1, 1, 0, {}); + const org3 = new OrganismDos(1, 2, 0, {}); + const org4 = new OrganismDos(1, 0, 1, {}); + const org5 = new OrganismDos(1, 0, 2, {}); + const org6 = new OrganismDos(1, 1, 2, {}); + const org7 = new OrganismDos(1, 2, 2, {}); + const org8 = new OrganismDos(1, 2, 1, {}); + + ops.vars[0] = 11; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = org1; + global.man.positions[1][0] = org2; + global.man.positions[2][0] = org3; + global.man.positions[0][1] = org4; + global.man.positions[0][2] = org5; + global.man.positions[1][2] = org6; + global.man.positions[2][2] = org7; + global.man.positions[2][1] = org8; + + expect(ops.operators[hex('110101 00')].call(ops, 0, hex('110101 00'), org)).toEqual(1); + + expect(org1.msg).toEqual(11); + expect(org2.msg).toEqual(11); + expect(org3.msg).toEqual(11); + expect(org4.msg).toEqual(11); + expect(org5.msg).toEqual(11); + expect(org6.msg).toEqual(11); + expect(org7.msg).toEqual(11); + expect(org8.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + global.man.positions[0][0] = EMPTY; + global.man.positions[1][0] = EMPTY; + global.man.positions[2][0] = EMPTY; + global.man.positions[0][1] = EMPTY; + global.man.positions[0][2] = EMPTY; + global.man.positions[1][2] = EMPTY; + global.man.positions[2][2] = EMPTY; + global.man.positions[2][1] = EMPTY; + + org1.destroy(); + org2.destroy(); + org3.destroy(); + org4.destroy(); + org5.destroy(); + org6.destroy(); + org7.destroy(); + org8.destroy(); + }); + + describe('say() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); + it("Checking say to nothing", () => { + ops.vars[0] = 1; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); - it("Checking simple values", () => { - org.x = 1; - expect(ops.onMyX(0x0c1fffff, 0, org)).toEqual(1); // v0=myX() - expect(ops.vars).toEqual([1,1,2,3]); - org.x = 3; - expect(ops.onMyX(0x0c6fffff, 0, org)).toEqual(1); // v1=myX() - expect(ops.vars).toEqual([1,3,2,3]); - org.x = 0; - expect(ops.onMyX(0x0cffffff, 0, org)).toEqual(1); // v3=myX() - expect(ops.vars).toEqual([1,3,2,0]); - }); - - it('Checking simple values with 4 bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); + expect(org.msg).toEqual(0); + expect(global.man.positions[1][0]).toEqual(EMPTY); + expect(global.man.world.getDot(1, 0)).toEqual(EMPTY); + }); - org.x = 3; - expect(ops1.onMyX(0x0c1fffff, 0, org)).toEqual(1); // v1=myX() - expect(ops1.vars).toEqual([0,3,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); - org.x = 3; - expect(ops1.onMyX(0x0c6fffff, 0, org)).toEqual(1); // v6=myX() - expect(ops1.vars).toEqual([0,3,2,3,4,5,3,7,8,9,10,11,12,13,14,15]); - org.x = 0; - expect(ops1.onMyX(0x0cffffff, 0, org)).toEqual(1); // v15=myX() - expect(ops1.vars).toEqual([0,3,2,3,4,5,3,7,8,9,10,11,12,13,14,0]); + it("Checking say to other organism 1", () => { + const org2 = new OrganismDos(1, 1, 0, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + global.man.positions[1][0] = EMPTY; + }); + it("Checking say to other organism 2", () => { + const org2 = new OrganismDos(1, 1, 1, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][1] = org2; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + global.man.positions[1][1] = EMPTY; + }); + it("Checking say to other organism 3", () => { + const org2 = new OrganismDos(1, 0, 1, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[0][1] = org2; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + global.man.positions[0][1] = EMPTY; + }); + it("Checking say to other organism twice", () => { + const org2 = new OrganismDos(1, 1, 0, {}); + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][0] = org2; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + expect(org2.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + + ops.vars[0] = 12; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + expect(org2.msg).toEqual(12); + expect(ops.vars).toEqual([12, 1, 2, 3, 4, 5, 6, 7]); + + global.man.positions[1][0] = EMPTY; + }); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); - }); + it("Checking say to many organisms 1", () => { + const org2 = new OrganismDos(1, 1, 0, {}); + const org3 = new OrganismDos(1, 1, 1, {}); + + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][0] = org2; + global.man.positions[1][1] = org3; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(org3.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + + global.man.positions[1][0] = EMPTY; + global.man.positions[1][1] = EMPTY; + org2.destroy(); + org3.destroy(); + }); + it("Checking say to many organisms 2", () => { + const org2 = new OrganismDos(1, 1, 1, {}); + const org3 = new OrganismDos(1, 0, 1, {}); + + ops.vars[0] = 11; + org.x = 0; + org.y = 0; + global.man.positions[1][1] = org2; + global.man.positions[0][1] = org3; + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + + expect(org2.msg).toEqual(11); + expect(org3.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + global.man.positions[1][1] = EMPTY; + global.man.positions[0][1] = EMPTY; + org2.destroy(); + org3.destroy(); + }); + it("Checking say to 8 organisms", () => { + const org1 = new OrganismDos(1, 0, 0, {}); + const org2 = new OrganismDos(1, 1, 0, {}); + const org3 = new OrganismDos(1, 2, 0, {}); + const org4 = new OrganismDos(1, 0, 1, {}); + const org5 = new OrganismDos(1, 0, 2, {}); + const org6 = new OrganismDos(1, 1, 2, {}); + const org7 = new OrganismDos(1, 2, 2, {}); + const org8 = new OrganismDos(1, 2, 1, {}); + + ops.vars[0] = 11; + org.x = 1; + org.y = 1; + global.man.positions[0][0] = org1; + global.man.positions[1][0] = org2; + global.man.positions[2][0] = org3; + global.man.positions[0][1] = org4; + global.man.positions[0][2] = org5; + global.man.positions[1][2] = org6; + global.man.positions[2][2] = org7; + global.man.positions[2][1] = org8; + + expect(ops.operators[hex('110101 000')].call(ops, 0, hex('110101 000'), org)).toEqual(1); + + expect(org1.msg).toEqual(11); + expect(org2.msg).toEqual(11); + expect(org3.msg).toEqual(11); + expect(org4.msg).toEqual(11); + expect(org5.msg).toEqual(11); + expect(org6.msg).toEqual(11); + expect(org7.msg).toEqual(11); + expect(org8.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + global.man.positions[0][0] = EMPTY; + global.man.positions[1][0] = EMPTY; + global.man.positions[2][0] = EMPTY; + global.man.positions[0][1] = EMPTY; + global.man.positions[0][2] = EMPTY; + global.man.positions[1][2] = EMPTY; + global.man.positions[2][2] = EMPTY; + global.man.positions[2][1] = EMPTY; + + org1.destroy(); + org2.destroy(); + org3.destroy(); + org4.destroy(); + org5.destroy(); + org6.destroy(); + org7.destroy(); + org8.destroy(); + }); + }) }); - describe('onMyY() method', () => { - let org; - let ops; - - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - - it("Checking simple values", () => { - org.y = 1; - expect(ops.onMyY(0x0c1fffff, 0, org)).toEqual(1); // v0=myY() - expect(ops.vars).toEqual([1,1,2,3]); - org.y = 3; - expect(ops.onMyY(0x0c6fffff, 0, org)).toEqual(1); // v1=myY() - expect(ops.vars).toEqual([1,3,2,3]); - org.y = 0; - expect(ops.onMyY(0x0cffffff, 0, org)).toEqual(1); // v3=myY() - expect(ops.vars).toEqual([1,3,2,0]); + describe('listen() operator', () => { + it("Checking listen command 1", () => { + org.msg = 11; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110110 00')].call(ops, 0, hex('110110 00'), org)).toEqual(1); + expect(org.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3]); + }); + it("Checking listen command 2", () => { + org.msg = 0; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110110 01')].call(ops, 0, hex('110110 01'), org)).toEqual(1); + expect(org.msg).toEqual(0); + expect(ops.vars).toEqual([0, 0, 2, 3]); }); - it('Checking simple values with 4 bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); - - org.y = 3; - expect(ops1.onMyY(0x0c1fffff, 0, org)).toEqual(1); // v1=myX() - expect(ops1.vars).toEqual([0,3,2,3,4,5,6,7,8,9,10,11,12,13,14,15]); - org.y = 3; - expect(ops1.onMyY(0x0c6fffff, 0, org)).toEqual(1); // v6=myX() - expect(ops1.vars).toEqual([0,3,2,3,4,5,3,7,8,9,10,11,12,13,14,15]); - org.y = 0; - expect(ops1.onMyY(0x0cffffff, 0, org)).toEqual(1); // v15=myX() - expect(ops1.vars).toEqual([0,3,2,3,4,5,3,7,8,9,10,11,12,13,14,0]); + describe('listen() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it("Checking listen command 1", () => { + org.msg = 11; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110110 000')].call(ops, 0, hex('110110 000'), org)).toEqual(1); + expect(org.msg).toEqual(11); + expect(ops.vars).toEqual([11, 1, 2, 3, 4, 5, 6, 7]); + }); + it("Checking listen command 2", () => { + org.msg = 0; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(org.msg).toEqual(0); + expect(ops.vars).toEqual([0, 0, 2, 3, 4, 5, 6, 7]); + }); }); }); - describe('onCheckLeft() method', () => { - let org; - let ops; - - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - - it('Checks left, but nothing there', () => { - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(0); - expect(y).toBe(2); - ret.ret = 0; - }); - org.x = 1; - org.y = 2; - expect(ops.onCheckLeft(0x0c7fffff, 0, org)).toEqual(1); // v1=checkLeft() - expect(ops.vars).toEqual([0,0,2,3]); + describe('check() operator', () => { + it('Check out of the world ', () => { + org.dir = DIRS.UP; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 01')].call(ops, 0, hex('110111 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, EMPTY, 2, 3]); + }); + it('Check object 1', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 01')].call(ops, 0, hex('110111 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY0, 2, 3]); + global.man.positions[1][0] = EMPTY; + }); + it('Check object 2', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + global.man.positions[2][1] = OBJECT_TYPES.TYPE_ENERGY1; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 01')].call(ops, 0, hex('110111 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY1, 2, 3]); + global.man.positions[2][1] = EMPTY; + }); + it('Check object 3', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + global.man.positions[1][2] = OBJECT_TYPES.TYPE_ENERGY3; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 01')].call(ops, 0, hex('110111 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY3, 2, 3]); + global.man.positions[1][2] = EMPTY; + }); + it('Check object 4', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY4; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 01')].call(ops, 0, hex('110111 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY4, 2, 3]); + global.man.positions[0][1] = EMPTY; }); - it('Checks left and energy there', () => { - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(0); - expect(y).toBe(2); - ret.ret = 9; + it('Check energy 1', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 01')].call(ops, 0, hex('110111 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, ENERGY, 2, 3]); + global.man.world.setDot(1,0,EMPTY); + }); + it('Check energy 2', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + global.man.world.setDot(2,1,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 01')].call(ops, 0, hex('110111 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, ENERGY, 2, 3]); + global.man.world.setDot(2,1,EMPTY); + }); + it('Check energy 3', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + global.man.world.setDot(1,2,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 11')].call(ops, 0, hex('110111 11'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, ENERGY]); + global.man.world.setDot(1,2,EMPTY); + }); + it('Check energy 4', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + global.man.world.setDot(0,1,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 00')].call(ops, 0, hex('110111 00'), org)).toEqual(1); + expect(ops.vars).toEqual([ENERGY, 1, 2, 3]); + global.man.world.setDot(0,1,EMPTY); + }); + + it('Check organism 1', () => { + const org2 = new OrganismDos('0', 1, 0, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xaabbcc); + global.man.positions[1][0] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 00')].call(ops, 0, hex('110111 00'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3]); + global.man.world.setDot(1,0,EMPTY); + global.man.positions[1][0] = EMPTY; + org2.destroy(); + }); + it('Check organism 2', () => { + const org2 = new OrganismDos('0', 2, 1, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + global.man.world.setDot(2,1,0xaabbcc); + global.man.positions[2][1] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 00')].call(ops, 0, hex('110111 00'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3]); + global.man.world.setDot(2,1,EMPTY); + global.man.positions[2][1] = EMPTY; + org2.destroy(); + }); + it('Check organism 3', () => { + const org2 = new OrganismDos('0', 1, 2, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + global.man.world.setDot(1,2,0xaabbcc); + global.man.positions[1][2] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 00')].call(ops, 0, hex('110111 00'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3]); + global.man.world.setDot(1,2,EMPTY); + global.man.positions[1][2] = EMPTY; + org2.destroy(); + }); + it('Check organism 4', () => { + const org2 = new OrganismDos('0', 0, 1, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + global.man.world.setDot(0,1,0xaabbcc); + global.man.positions[0][1] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('110111 00')].call(ops, 0, hex('110111 00'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3]); + global.man.world.setDot(0,1,EMPTY); + global.man.positions[0][1] = EMPTY; + org2.destroy(); + }); + + describe('check() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; }); - org.x = 1; - org.y = 2; - expect(ops.onCheckLeft(0x0c7fffff, 1, org)).toEqual(2); // v1=checkLeft() - expect(ops.vars).toEqual([0,9,2,3]); - }); - it('Checks left with 4 bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); + it('Check out of the world ', () => { + org.dir = DIRS.UP; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, EMPTY, 2, 3, 4, 5, 6, 7]); + }); + it('Check object 1', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + global.man.positions[1][0] = OBJECT_TYPES.TYPE_ENERGY0; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY0, 2, 3, 4, 5, 6, 7]); + global.man.positions[1][0] = EMPTY; + }); + it('Check object 2', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + global.man.positions[2][1] = OBJECT_TYPES.TYPE_ENERGY1; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY1, 2, 3, 4, 5, 6, 7]); + global.man.positions[2][1] = EMPTY; + }); + it('Check object 3', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + global.man.positions[1][2] = OBJECT_TYPES.TYPE_ENERGY3; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY3, 2, 3, 4, 5, 6, 7]); + global.man.positions[1][2] = EMPTY; + }); + it('Check object 4', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + global.man.positions[0][1] = OBJECT_TYPES.TYPE_ENERGY4; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, OBJECT_TYPES.TYPE_ENERGY4, 2, 3, 4, 5, 6, 7]); + global.man.positions[0][1] = EMPTY; + }); - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(0); - expect(y).toBe(2); - ret.ret = 9; + it('Check energy 1', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, ENERGY, 2, 3, 4, 5, 6, 7]); + global.man.world.setDot(1,0,EMPTY); + }); + it('Check energy 2', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + global.man.world.setDot(2,1,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 001')].call(ops, 0, hex('110111 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, ENERGY, 2, 3, 4, 5, 6, 7]); + global.man.world.setDot(2,1,EMPTY); + }); + it('Check energy 3', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + global.man.world.setDot(1,2,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 011')].call(ops, 0, hex('110111 011'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, ENERGY, 4, 5, 6, 7]); + global.man.world.setDot(1,2,EMPTY); + }); + it('Check energy 4', () => { + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + global.man.world.setDot(0,1,0xaabbcc); + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 000')].call(ops, 0, hex('110111 000'), org)).toEqual(1); + expect(ops.vars).toEqual([ENERGY, 1, 2, 3, 4, 5, 6, 7]); + global.man.world.setDot(0,1,EMPTY); }); - org.x = 1; - org.y = 2; - expect(ops1.onCheckLeft(0x0c7fffff, 1, org)).toEqual(2); // v7=checkLeft() - expect(ops1.vars).toEqual([0,1,2,3,4,5,6,9,8,9,10,11,12,13,14,15]); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it('Check organism 1', () => { + const org2 = new OrganismDos('0', 1, 0, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.UP; + global.man.world.setDot(1,0,0xaabbcc); + global.man.positions[1][0] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 000')].call(ops, 0, hex('110111 000'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3, 4, 5, 6, 7]); + global.man.world.setDot(1,0,EMPTY); + global.man.positions[1][0] = EMPTY; + org2.destroy(); + }); + it('Check organism 2', () => { + const org2 = new OrganismDos('0', 2, 1, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.RIGHT; + global.man.world.setDot(2,1,0xaabbcc); + global.man.positions[2][1] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 000')].call(ops, 0, hex('110111 000'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3, 4, 5, 6, 7]); + global.man.world.setDot(2,1,EMPTY); + global.man.positions[2][1] = EMPTY; + org2.destroy(); + }); + it('Check organism 3', () => { + const org2 = new OrganismDos('0', 1, 2, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.DOWN; + global.man.world.setDot(1,2,0xaabbcc); + global.man.positions[1][2] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 000')].call(ops, 0, hex('110111 000'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3, 4, 5, 6, 7]); + global.man.world.setDot(1,2,EMPTY); + global.man.positions[1][2] = EMPTY; + org2.destroy(); + }); + it('Check organism 4', () => { + const org2 = new OrganismDos('0', 0, 1, {}); + org.x = 1; + org.y = 1; + org.dir = DIRS.LEFT; + global.man.world.setDot(0,1,0xaabbcc); + global.man.positions[0][1] = org2; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('110111 000')].call(ops, 0, hex('110111 000'), org)).toEqual(1); + expect(ops.vars).toEqual([ORGANISM, 1, 2, 3, 4, 5, 6, 7]); + global.man.world.setDot(0,1,EMPTY); + global.man.positions[0][1] = EMPTY; + org2.destroy(); + }); }); }); - describe('onCheckRight() method', () => { - let org; - let ops; - - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - - it('Checks right, but nothing there', () => { - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(2); - expect(y).toBe(2); - ret.ret = 0; - }); - org.x = 1; - org.y = 2; - expect(ops.onCheckRight(0x0c7fffff, 0, org)).toEqual(1); // v1=checkRight() - expect(ops.vars).toEqual([0,0,2,3]); + describe('myEnergy() operator', () => { + it('Check energy of organism ', () => { + org.energy = 1; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('111000 01')].call(ops, 0, hex('111000 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 3]); }); - - it('Checks right and energy there', () => { - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(2); - expect(y).toBe(2); - ret.ret = 9; - }); - org.x = 1; - org.y = 2; - expect(ops.onCheckRight(0x0c7fffff, 1, org)).toEqual(2); // v1=checkRight() - expect(ops.vars).toEqual([0,9,2,3]); + it('Check zero energy of organism ', () => { + org.energy = 0; + expect(ops.vars).toEqual([0, 1, 2, 3]); + expect(ops.operators[hex('111000 01')].call(ops, 0, hex('111000 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 0, 2, 3]); }); - it('Checks right with 4 bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); - - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(2); - expect(y).toBe(2); - ret.ret = 9; + describe('myEnergy() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; }); - org.x = 1; - org.y = 2; - expect(ops1.onCheckRight(0x0c7fffff, 1, org)).toEqual(2); // v7=checkRight() - expect(ops1.vars).toEqual([0,1,2,3,4,5,6,9,8,9,10,11,12,13,14,15]); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it('Check energy of organism ', () => { + org.energy = 1; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('111000 001')].call(ops, 0, hex('111000 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + }); + it('Check zero energy of organism ', () => { + org.energy = 0; + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + expect(ops.operators[hex('111000 001')].call(ops, 0, hex('111000 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 0, 2, 3, 4, 5, 6, 7]); + }); }); }); - describe('onCheckUp() method', () => { - let org; - let ops; - - beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); - afterEach (() => {ops.destroy(); org.destroy()}); - - it('Checks up, but nothing there', () => { - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(1); - expect(y).toBe(1); - ret.ret = 0; - }); - org.x = 1; - org.y = 2; - expect(ops.onCheckUp(0x0c7fffff, 0, org)).toEqual(1); // v1=checkUp() - expect(ops.vars).toEqual([0,0,2,3]); + describe('myAge() operator', () => { + it('Check default age of organism ', () => { + expect(org.iterations).toEqual(-1); + expect(ops.operators[hex('111001 01')].call(ops, 0, hex('111001 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, -1, 2, 3]); }); + it('Check age increase', () => { + expect(org.iterations).toEqual(-1); + expect(ops.operators[hex('111001 01')].call(ops, 0, hex('111001 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, -1, 2, 3]); + org.run(); + expect(ops.operators[hex('111001 01')].call(ops, 0, hex('111001 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 0, 2, 3]); + org.run(); + org.run(); + org.run(); + expect(ops.operators[hex('111001 01')].call(ops, 0, hex('111001 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 3, 2, 3]); + }); + + describe('myAge() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); - it('Checks up and energy there', () => { - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(1); - expect(y).toBe(1); - ret.ret = 9; + it('Check default age of organism ', () => { + expect(org.iterations).toEqual(-1); + expect(ops.operators[hex('111001 001')].call(ops, 0, hex('111001 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, -1, 2, 3, 4, 5, 6, 7]); + }); + it('Check age increase', () => { + expect(org.iterations).toEqual(-1); + expect(ops.operators[hex('111001 001')].call(ops, 0, hex('111001 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, -1, 2, 3, 4, 5, 6, 7]); + org.run(); + expect(ops.operators[hex('111001 001')].call(ops, 0, hex('111001 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 0, 2, 3, 4, 5, 6, 7]); + org.run(); + org.run(); + org.run(); + expect(ops.operators[hex('111001 001')].call(ops, 0, hex('111001 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 3, 2, 3, 4, 5, 6, 7]); }); - org.x = 1; - org.y = 2; - expect(ops.onCheckUp(0x0c7fffff, 1, org)).toEqual(2); // v1=checkUp() - expect(ops.vars).toEqual([0,9,2,3]); }); + }); - it('Checks up with 4 bits per var', () => { - let bpv = OConfig.codeBitsPerVar; - OConfig.codeBitsPerVar = 4; - let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); - - org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(1); - expect(y).toBe(1); - ret.ret = 9; + describe('myDir() operator', () => { + it('Check default direction of organism ', () => { + org.dir = DIRS.UP; + expect(ops.operators[hex('111010 11')].call(ops, 0, hex('111010 11'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 0]); + org.dir = DIRS.RIGHT; + expect(ops.operators[hex('111010 10')].call(ops, 0, hex('111010 10'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 1, 0]); + org.dir = DIRS.DOWN; + expect(ops.operators[hex('111010 01')].call(ops, 0, hex('111010 01'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 2, 1, 0]); + org.dir = DIRS.LEFT; + expect(ops.operators[hex('111010 00')].call(ops, 0, hex('111010 00'), org)).toEqual(1); + expect(ops.vars).toEqual([3, 2, 1, 0]); + }); + + describe('myDir() operator with 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll(() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + OperatorsDos.compile(); + }); + afterAll(() => OperatorsDos.compile()); + beforeEach(() => { + vars = [0, 1, 2, 3, 4, 5, 6, 7]; + offs = new Array(10); + ops = new OperatorsDos(offs, vars, org); + }); + afterEach(() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; }); - org.x = 1; - org.y = 2; - expect(ops1.onCheckUp(0x0c7fffff, 1, org)).toEqual(2); // v7=checkUp() - expect(ops1.vars).toEqual([0,1,2,3,4,5,6,9,8,9,10,11,12,13,14,15]); - OConfig.codeBitsPerVar = bpv; - ops1.destroy(); + it('Check default direction of organism ', () => { + org.dir = DIRS.UP; + expect(ops.operators[hex('111010 011')].call(ops, 0, hex('111010 011'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 0, 4, 5, 6, 7]); + org.dir = DIRS.RIGHT; + expect(ops.operators[hex('111010 010')].call(ops, 0, hex('111010 010'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 1, 0, 4, 5, 6, 7]); + org.dir = DIRS.DOWN; + expect(ops.operators[hex('111010 001')].call(ops, 0, hex('111010 001'), org)).toEqual(1); + expect(ops.vars).toEqual([0, 2, 1, 0, 4, 5, 6, 7]); + org.dir = DIRS.LEFT; + expect(ops.operators[hex('111010 000')].call(ops, 0, hex('111010 000'), org)).toEqual(1); + expect(ops.vars).toEqual([3, 2, 1, 0, 4, 5, 6, 7]); + }); }); }); - describe('onCheckDown() method', () => { + xdescribe('onCheckLeft() method', () => { let org; let ops; beforeEach(() => {org = new OrganismDos('0', 0, 0, {}); ops = new OperatorsDos([], [0, 1, 2, 3], org)}); afterEach (() => {ops.destroy(); org.destroy()}); - it('Checks down, but nothing there', () => { + it('Checks left, but nothing there', () => { org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(1); - expect(y).toBe(3); + expect(x).toBe(0); + expect(y).toBe(2); ret.ret = 0; }); org.x = 1; org.y = 2; - expect(ops.onCheckDown(0x0c7fffff, 0, org)).toEqual(1); // v1=checkDown() + expect(ops.onCheckLeft(0x0c7fffff, 0, org)).toEqual(1); // v1=checkLeft() expect(ops.vars).toEqual([0,0,2,3]); }); - it('Checks down and energy there', () => { + it('Checks left and energy there', () => { org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(1); - expect(y).toBe(3); + expect(x).toBe(0); + expect(y).toBe(2); ret.ret = 9; }); org.x = 1; org.y = 2; - expect(ops.onCheckDown(0x0c7fffff, 1, org)).toEqual(2); // v1=checkDown() + expect(ops.onCheckLeft(0x0c7fffff, 1, org)).toEqual(2); // v1=checkLeft() expect(ops.vars).toEqual([0,9,2,3]); }); - it('Checks right with 4 bits per var', () => { + it('Checks left with 4 bits per var', () => { let bpv = OConfig.codeBitsPerVar; OConfig.codeBitsPerVar = 4; let ops1 = new OperatorsDos([1], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], org); org.on(EVENTS.CHECK_AT, (x, y, ret) => { - expect(x).toBe(1); - expect(y).toBe(3); + expect(x).toBe(0); + expect(y).toBe(2); ret.ret = 9; }); org.x = 1; org.y = 2; - expect(ops1.onCheckDown(0x0c7fffff, 1, org)).toEqual(2); // v7=checkDown() + expect(ops1.onCheckLeft(0x0c7fffff, 1, org)).toEqual(2); // v7=checkLeft() expect(ops1.vars).toEqual([0,1,2,3,4,5,6,9,8,9,10,11,12,13,14,15]); OConfig.codeBitsPerVar = bpv; @@ -1198,7 +3109,7 @@ describe("client/src/organism/OperatorsDos", () => { }); }); - describe('Checks complex DOS scripts for validness', () => { + xdescribe('Checks complex DOS scripts for validness', () => { const newWeights = [.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1]; const weights = OConfig.orgOperatorWeights.slice(); let ocfg; @@ -1212,8 +3123,7 @@ describe("client/src/organism/OperatorsDos", () => { beforeEach(() => { ocfg = new ConfigHelper(OConfig); ocfg.set('codeYieldPeriod', 2); - ocfg.set('codeBitsPerBlock', 8); - ocfg.set('codeBitsPerOperator', 8); + ocfg.set('CODE_BITS_PER_OPERATOR', 8); ocfg.set('codeBitsPerVar', 2); ocfg.set('codeConstBits', 16); ocfg.set('orgMemBits', 8); diff --git a/client/src/manager/plugins/organisms/dos/Organism.js b/client/src/manager/plugins/organisms/dos/Organism.js index db11544..9c395a4 100644 --- a/client/src/manager/plugins/organisms/dos/Organism.js +++ b/client/src/manager/plugins/organisms/dos/Organism.js @@ -14,7 +14,7 @@ class OrganismDos extends Organism { * @param {String} id Unique identifier of organism * @param {Number} x Unique X coordinate * @param {Number} y Unique Y coordinate - * @param {Object} item Reference to the Queue item, where + * @param {Object} item Reference to the item index, where * this organism is located * @param {Organism} parent Parent organism if cloning is needed */ diff --git a/client/src/manager/plugins/organisms/dos/OrganismSpec.js b/client/src/manager/plugins/organisms/dos/OrganismSpec.js index 460d1b0..f31bcbb 100644 --- a/client/src/manager/plugins/organisms/dos/OrganismSpec.js +++ b/client/src/manager/plugins/organisms/dos/OrganismSpec.js @@ -165,15 +165,6 @@ describe("client/src/organism/OrganismDos", () => { }); describe('Checks coordinates', () => { - it('posId() should return unique hash', () => { - org.x = 2; - org.y = 3; - expect(org.posId).toBe(Helper.posId(2, 3)); - org.x = 0; - org.y = 3; - expect(org.posId).toBe(Helper.posId(0, 3)); - }); - it('Checks coordinates setters getters', () => { org.x = 1; org.y = 2; diff --git a/client/src/manager/plugins/organisms/dos/Organisms.js b/client/src/manager/plugins/organisms/dos/Organisms.js index 659788d..4b1cbbf 100644 --- a/client/src/manager/plugins/organisms/dos/Organisms.js +++ b/client/src/manager/plugins/organisms/dos/Organisms.js @@ -17,19 +17,16 @@ const OConfig = require('./../Config'); const EVENTS = require('./../../../../share/Events').EVENTS; const Helper = require('./../../../../../../common/src/Helper'); const DIR = require('./../../../../../../common/src/Directions').DIR; -/** - * {Number} World object types - */ -const EMPTY = 0; -const ENERGY = 1; -const ORGANISM = 2; -const OBJECT = 3; +const OBJECT_TYPES = require('./../../../../view/World').OBJECT_TYPES; +// +// We have to add object types to global types storage +// +OBJECT_TYPES.POISON = -(Object.keys(OBJECT_TYPES).length + 1); /** * {Function} Is created to speed up this function call. constants are run * much faster, then Helper.normalize() */ const NORMALIZE = Helper.normalize; -const NORMALIZE_NO_DIR = Helper.normalizeNoDir; class Organisms extends BaseOrganisms { constructor(manager) { @@ -77,7 +74,7 @@ class Organisms extends BaseOrganisms { // // Clone percent is always 0.5 // - let energy = (orgEnergy * .5 + .5) << 0; // analog of Math.round() + let energy = (orgEnergy * .5 + .5) << 0 >>> 0; // analog of Math.round() // // This is very special/rare case, when organisms cheating by creating // ancestors and put all energy into them at the same time resetting @@ -96,10 +93,7 @@ class Organisms extends BaseOrganisms { addOrgHandlers(org) { super.addOrgHandlers(org); - org.on(EVENTS.GET_ENERGY, this._onGetEnergy.bind(this)); - org.on(EVENTS.EAT, this._onEat.bind(this)); org.on(EVENTS.STEP, this._onStep.bind(this)); - org.on(EVENTS.CHECK_AT, this._onCheckAt.bind(this)); } /** @@ -112,55 +106,6 @@ class Organisms extends BaseOrganisms { return new Organism(...args); } - _onGetEnergy(x, y, ret) { - if (this.positions[x][y] <= 0) { - ret.ret = this.world.getDot(x, y) - } else { - ret.ret = this.positions[x][y].energy; - } - } - - _onEat(org, x, y, ret) { - const eat = ret.ret; - [x, y] = NORMALIZE_NO_DIR(x, y); - const victimOrg = this.positions[x][y]; - // - // World object found. We can't eat objects - // - if (victimOrg < 0) {ret.ret = 0; return} - // - // Energy found - // - if (victimOrg === 0) { - if (eat >= 0) { - ret.ret = this.world.grabDot(x, y, eat); - this.parent.fire(EVENTS.EAT_ENERGY, ret.ret); - } else { - ret.ret = eat; - this.world.setDot(x, y, (((-eat + .5) << 0) >>> 0) + this.world.getDot(x, y)); - this.parent.fire(EVENTS.PUT_ENERGY, -eat); - } - return; - } - // - // Organism found - // - ret.ret = eat < 0 ? 0 : (eat > victimOrg.energy ? victimOrg.energy : eat); - if (victimOrg.energy <= ret.ret) { - this.parent.fire(EVENTS.KILL_EAT, victimOrg); - // - // IMPORTANT: - // We have to do destroy here, to have a possibility for current - // (winner) organism to clone himself after eating other organism. - // This is how organisms compete for an ability to clone - // - victimOrg.destroy(); - } else { - this.parent.fire(EVENTS.EAT_ORG, victimOrg, ret.ret); - victimOrg.energy -= ret.ret; - } - } - _onStep(org, x1, y1, x2, y2) { if (org.energy < 1) {return} const man = this.parent; @@ -203,18 +148,6 @@ class Organisms extends BaseOrganisms { this.move(x1, y1, x2, y2, org); } - _onCheckAt(x, y, ret) { - [x, y] = NORMALIZE_NO_DIR(x, y); - - if (this.positions[x][y] < 0) { // world object - ret.ret = OBJECT + -this.positions[x][y]; - } else if (this.positions[x][y] === 0) { // energy - ret.ret = this.world.getDot(x, y) > 0 ? ENERGY : EMPTY; - } else { // organism - ret.ret = ORGANISM; - } - } - /** * Is called if organism step in from the server or other client (Manager/World). * If step in position is not free or maximum organisms are in the world, then @@ -226,7 +159,7 @@ class Organisms extends BaseOrganisms { * @param {Object} ret Return object */ _onStepIn(x, y, orgJson, ret) { - if (ret.ret = this.world.isFree(x, y) && this.organisms.size < (OConfig.orgMaxOrgs + OConfig.orgMaxOrgs * OConfig.orgStepOverflowPercent)) { + if (ret.ret = this.world.isFree(x, y) && this.organisms.length < (OConfig.orgMaxOrgs + OConfig.orgMaxOrgs * OConfig.orgStepOverflowPercent)) { const item = this.createOrg(x, y); if (item === false) {return} const org = item.val; diff --git a/client/src/manager/plugins/organisms/garmin/Code2String.js b/client/src/manager/plugins/organisms/garmin/Code2String.js index 034b478..8a2dde7 100644 --- a/client/src/manager/plugins/organisms/garmin/Code2String.js +++ b/client/src/manager/plugins/organisms/garmin/Code2String.js @@ -16,7 +16,7 @@ const VAR2 = (n) => Num.getVar(n, 2); const BITS_AFTER_THREE_VARS = Num.BITS_PER_OPERATOR + Num.BITS_PER_VAR * 3; const HALF_OF_VAR = Num.MAX_VAR / 2; -class Code2String { +class Code2JS { constructor(manager) { this._manager = manager; /** @@ -53,7 +53,7 @@ class Code2String { // // API of the Manager for accessing outside. (e.g. from Console) // - manager.api.formatCode = (code) => this.format(code); + manager.api.toJS = (code) => this.format(code); } destroy() { @@ -130,4 +130,4 @@ class Code2String { } } -module.exports = Code2String; \ No newline at end of file +module.exports = Code2JS; \ No newline at end of file diff --git a/client/src/manager/plugins/organisms/garmin/Organisms.js b/client/src/manager/plugins/organisms/garmin/Organisms.js index 8a584f1..f8fd4bd 100644 --- a/client/src/manager/plugins/organisms/garmin/Organisms.js +++ b/client/src/manager/plugins/organisms/garmin/Organisms.js @@ -41,7 +41,7 @@ class Organisms extends BaseOrganisms { Console.warn('--------------------------------------------------'); Console.warn('Max energy: ', org.energy, ', org Id: ', org.id); Console.warn('[' + org.vm.code + ']'); - Console.warn(this.manager.api.formatCode(org.vm.code)); + Console.warn(this.manager.api.toJS(org.vm.code)); } if (org.changes > this._maxChanges) {this._maxChanges = org.changes} @@ -75,7 +75,7 @@ class Organisms extends BaseOrganisms { Console.warn('--------------------------------------------------'); Console.warn('org id: ', org.id, ', energy: ', org.energy); Console.warn('[' + org.vm.code + ']'); - Console.warn(this.manager.api.formatCode(org.vm.code)); + Console.warn(this.manager.api.toJS(org.vm.code)); } } diff --git a/client/src/manager/plugins/status/Status.js b/client/src/manager/plugins/status/Status.js index 0ed60b6..4c88ad8 100644 --- a/client/src/manager/plugins/status/Status.js +++ b/client/src/manager/plugins/status/Status.js @@ -133,29 +133,30 @@ class Status extends Configurable { } _onBeforeLoop(orgs) { - const size = orgs.size || 1; + const len = orgs.length || 1; let energy = 0; let startEnergy = 0; let iterations = 0; let fitness = 0; let changes = 0; let codeSize = 0; - let item = orgs.first; + const size = orgs.size; let org; - while(item && (org = item.val)) { + for (let i = 0; i < size; i++) { + if ((org = orgs.get(i)) === null) {continue} + energy += org.energy; startEnergy += org.startEnergy; iterations += org.iterations; changes += org.changes; fitness += org.fitness(); codeSize += org.vm.size; - item = item.next; } - this._energy = energy / size; - this._changes = changes / size; - this._fitness = fitness / size; + this._energy = energy / len; + this._changes = changes / len; + this._fitness = fitness / len; this._codeSize = codeSize; } @@ -166,7 +167,7 @@ class Status extends Configurable { if (stamp - this._stamp < this._statusCfg.period) {return} const orgs = this.parent.organisms; const status = this._status; - const orgAmount = orgs.size || 1; + const orgAmount = orgs.length || 1; const fix = Status._toFixed; this._onBeforeLoop(orgs); @@ -193,9 +194,9 @@ class Status extends Configurable { status.killin = fix(this._kill[7], 2); status.killclone = fix(this._kill[8], 2); - status.wenergy = fix(this._worldEnergy, 5); + status.wenergy = fix(this._worldEnergy, 7); - !this._firstCall && this.onStatus(status, orgs.size); + !this._firstCall && this.onStatus(status, orgs.length); this._onAfterLoop(stamp); this._firstCall = false; } diff --git a/client/src/manager/plugins/status/charts/Charts.js b/client/src/manager/plugins/status/charts/Charts.js index 3066047..60a5be2 100644 --- a/client/src/manager/plugins/status/charts/Charts.js +++ b/client/src/manager/plugins/status/charts/Charts.js @@ -90,7 +90,7 @@ class Charts extends Status { return document.body.appendChild(Helper.setStyles('DIV', { position : 'absolute', top : '7px', - left : '35px', + left : '60px', color : '#fff', fontSize : '18px', fontFamily: 'Consolas' @@ -184,7 +184,7 @@ class Charts extends Status { * @api */ _transparent(chart, t) { - if (typeof t === 'undefined') { + if (t === undefined) { _each(this._charts, (c, k) => this._setProperty(k, 'transparent', chart)); } else { this._setProperty(chart, 'transparent', t); @@ -234,8 +234,8 @@ class Charts extends Status { * @api */ _active(chart, a) { - const noA = typeof a === 'undefined'; - const noChart = typeof chart === 'undefined'; + const noA = a === undefined; + const noChart = chart === undefined; const bChart = typeof chart === 'boolean'; a = noA ? (bChart ? chart : true) : a; @@ -275,7 +275,7 @@ class Charts extends Status { * @api */ _reset(chart) { - if (typeof chart === 'undefined') { + if (chart === undefined) { _each(this._charts, c => {c.reset(); return true}); } else { _get(this, `_charts.${chart}`, {}).reset && this._charts[chart].reset(); @@ -283,7 +283,7 @@ class Charts extends Status { } _preset(name) { - if (typeof(PRESETS[name]) === 'undefined') {return} + if (PRESETS[name] === undefined) {return} _each(this._charts, (inst, chart) => this._off(chart)); _each(PRESETS[name], (pos, chart) => { @@ -293,7 +293,7 @@ class Charts extends Status { } _setProperty(chart, prop, val) { - if (typeof(this._charts[chart]) === 'undefined') {return} + if (this._charts[chart] === undefined) {return} _has(this.cfg, `charts.${chart}.${prop}`) && (this.cfg.charts[chart][prop] = val); this._charts[chart][prop] = val; } diff --git a/client/src/manager/plugins/stones/Config.js b/client/src/manager/plugins/stones/Config.js new file mode 100644 index 0000000..b9cdda7 --- /dev/null +++ b/client/src/manager/plugins/stones/Config.js @@ -0,0 +1,32 @@ +/** + * Configuration of Stones plugin + * + * @author flatline + */ +const Helper = require('./../../../../../common/src/Helper'); + +const STONES_COLOR = 1800; +const Config = { + /** + * {Number} size of one stone block in dots + */ + blockSize: 300, + /** + * {Number} Index of stone color. Starts from 0. Ends with Organism.MAX_COLORS + */ + colorIndex: STONES_COLOR, + /** + * {Number} Maximum amount of stone dots + */ + maxValue: 500000 * Helper.getColor(STONES_COLOR), + /** + * {Array|null} In case of array you may set sequence of four values: x,y,w,h. + * They means x,y coordinates, width, height of places with high stones concentration. + * Example: assume, that resolution 1920, 1080. [1920 / 2, 1080 / 2, 1920, 1080]. In this + * example all the screen will be filled by stones. As many values by four, you set as + * many places with stones will be created. In case of null, grouping will be disabled. + */ + groups: [1920 * 2, 1080 * 2, 1920 * 2, 1080 * 4] +}; + +module.exports = Config; \ No newline at end of file diff --git a/client/src/manager/plugins/stones/Stones.js b/client/src/manager/plugins/stones/Stones.js new file mode 100644 index 0000000..222a731 --- /dev/null +++ b/client/src/manager/plugins/stones/Stones.js @@ -0,0 +1,23 @@ +/** + * Manager's plugin, which add stones to the world + * + * @author flatline + */ +const Dots = require('./../../../share/Dots'); +const Config = require('./Config'); +const OBJECT_TYPES = require('../../../view/World').OBJECT_TYPES; +// +// We have to add stone type to global types storage +// +OBJECT_TYPES.TYPE_STONE = -(Object.keys(OBJECT_TYPES).length + 1); + +class Stones extends Dots{ + constructor(manager) { + super(manager, Config, { + addOnce: true, + setCb : (x, y) => manager.positions[x][y] = OBJECT_TYPES.TYPE_STONE + }); + } +} + +module.exports = Stones; \ No newline at end of file diff --git a/client/src/share/Config.js b/client/src/share/Config.js index d70a4bb..faaaa62 100644 --- a/client/src/share/Config.js +++ b/client/src/share/Config.js @@ -4,13 +4,16 @@ * * @author flatline */ -// TODO: this config should be refactored/moved to manager folder as it's part const Config = require('./../../../common/src/Config'); const QUIET_ALL = 0; const QUIET_IMPORTANT = 1; const QUIET_NO = 2; +const ENERGY_COLOR = 10000; +// +// Don't change it to window === undefined, it will trigger an error +// const IS_NODE_JS = typeof window === 'undefined'; class ClientConfig extends Config {} @@ -35,11 +38,12 @@ ClientConfig.init({ */ plugIncluded: [ 'organisms/dos/Organisms', - 'organisms/dos/Code2String', + 'organisms/dos/Code2JS', 'Config', 'client/Client', - 'Energy', - 'Stones', + 'energy/Energy', + 'stones/Stones', + 'objects/Objects', 'status/console/Console', IS_NODE_JS ? '' : 'status/charts/Charts', 'ips/Ips', @@ -53,11 +57,20 @@ ClientConfig.init({ /** * {Number} World width */ - worldWidth: 1920, + worldWidth: 1920 * 4, /** * {Number} World height */ - worldHeight: 1080, + worldHeight: 1080 * 4, + /** + * {Number} Color index of one energy dot + */ + worldEnergyColor: ENERGY_COLOR, + /** + * {Number} Amount of all energy in a world including organisms and all kinds + * of energy. Total energy should not be greater then this value. + */ + worldEnergy: 0x6d3b4 * 10000 * 20, // Helper.getColor(ENERGY_COLOR) === 0x6d3b4 /** * {Number} Turns on cyclic world mode. It means that organisms may go outside * it's border, but still be inside. For example, if the world has 10x10 @@ -68,42 +81,9 @@ ClientConfig.init({ */ worldCyclical: true, /** - * {Number} An amount of iteration, after which we have to check world energy - * percent. May be 0 if you want to disable energy generation - */ - worldEnergyCheckPeriod: 5000, - /** - * {Number} size of one clever energy block in dots - */ - worldEnergyBlockSize: 10, - /** - * {Number} Index of energy color. Starts from 0. Ends with 4000. See Organism.MAX_COLORS - * constant for details - */ - worldEnergyColorIndex: 0, - /** - * {Number} Percent from all energy in a world until clever energy will be added. - * After this value clever energy will be stopped to add until it's amount will - * be less then worldEnergyMinPercent. These two configs create cyclical - * energy adding to the world. - */ - worldEnergyMaxPercent: .3, - /** - * {Number} Opposite to worldEnergyMaxPercent. Sets minimum percent from - * all energy in a world after which clever energy will turn on (be added to the - * world again). - */ - worldEnergyMinPercent: .28, - /** - * {Number} Percent of stones in a world. Percent from world size: - * stoneAmount = worldStonesPercent * worldWidth * worldHeight - */ - worldStonesPercent: .25, - /** - * {Number} Color index for stones in a world. See Organism.MAX_COLORS - * constant for details + * {Number} Speed coefficient. Between 0..1. 1 - max speed, 0 - min. */ - worldStoneColorIndex: 1800, + worldSpeed: 1, /** * {Number} Zoom speed 0..1 */ diff --git a/client/src/share/Dots.js b/client/src/share/Dots.js new file mode 100644 index 0000000..d6f8d09 --- /dev/null +++ b/client/src/share/Dots.js @@ -0,0 +1,145 @@ +/** + * Manager's plugin. Base class for adding dots to the world. It may + * be energy, stones, etc... You have to inherit your class to have + * your custom object. + * + * @author flatline + */ +const Helper = require('../../../common/src/Helper'); +const Configurable = require('../../../common/src/Configurable'); +const Config = require('./Config').Config; + +class Dots extends Configurable { + /** + * Override of Manager.onLoop() method + * @param {Number} counter Global time analog + * @param {Number} timer UNIX time stamp + * @param {Object} sharedObj Shared object of the manager + * @abstract + */ + onLoop(counter, timer, sharedObj) {} + + /** + * Creates dots object. You have to inherit your class from this one + * to have specified type of object. For example: stone or energy. + * @param {Manager} manager Parent manager instance + * @param {Config} config Reference to configuration object + * @param {Object} cfg Configuration for current dots class + * @param {Object} api plugin's API + */ + constructor(manager, config, cfg, api = {}) { + super(manager, {Config, cfg: config}, api); + + this.manager = manager; + this._cfg = cfg; + this.config = config; + this._onIterationCb = this.onIteration.bind(this); + this._onLoopCb = this.onLoop.bind(this); + + Helper.override(manager, 'onIteration', this._onIterationCb); + Helper.override(manager, 'onLoop', this._onLoopCb); + } + + destroy() { + Helper.unoverride(this.manager, 'onLoop', this._onLoopCb); + Helper.unoverride(this.manager, 'onIteration', this._onIterationCb); + this._onLoopCb = null; + this._onIterationCb = null; + this._cfg = null; + this.config = null; + this.manager = null; + } + + onIteration(counter) { + const cfg = this._cfg; + const config = this.config; + // + // We have to add dots only once + // + if (cfg.addOnce && counter < 1 && config.maxValue !== 0) {this._addDots(); return false} + // + // We have to add dots every time, when minimum percent of dots is reached + // + if (config.checkPeriod === 0 || counter % config.checkPeriod !== 0) {return false} + + const dotsValue = this._getDotsValue(cfg.compareCb); + if (cfg.checkMin) { + const percent = cfg.checkMin(dotsValue); + if (!percent) {return dotsValue} + } else if (dotsValue > config.minValue) {return dotsValue} + this._addDots(); + + return this._getDotsValue(cfg.compareCb); + } + + /** + * Returns percent of object with specified type in a world. Percent is + * calculating comparing with all dots in a world (100%) + * @param {Function} compareCb Compare function. Returns true/false + * @return {Number} Percent + */ + _getDotsValue(compareCb) { + let dots = 0; + const width = Config.worldWidth; + const height = Config.worldHeight; + const world = this.manager.world; + + for (let x = 0; x < width; x++) { + for (let y = 0; y < height; y++) { + if (compareCb(x, y)) {dots += world.getDot(x, y)} + } + } + + return dots; + } + + _addDots() { + const dots = this.config.maxValue; + let amount = 0; + let attempts = 0; + while (amount < dots && attempts < 100) { + const startAmount = amount; + amount = this._addDotsBlock(amount, dots); + if (startAmount === amount) { + attempts++; + } else { + attempts = 0; + } + } + } + + _addDotsBlock(amount, dots) { + const width = Config.worldWidth; + const height = Config.worldHeight; + const color = Helper.getColor(this.config.colorIndex); + const man = this.manager; + const world = man.world; + const blockSize = this.config.blockSize; + const setCb = this._cfg.setCb; + const colorCb = this._cfg.colorCb; + const groups = this.config.groups; + const rand = Helper.rand; + const groupAmount = groups ? groups.length : 0; + const xCoord = rand(groupAmount / 4) * 4; + const yCoord = rand(groupAmount / 4) * 4; + let x = groups ? groups[xCoord] - rand(groups[xCoord + 2] / 2) + rand(groups[xCoord + 2] / 2) : rand(width); + let y = groups ? groups[yCoord + 1] - rand(groups[yCoord + 3] / 2) + rand(groups[yCoord + 3] / 2) : rand(height); + + for (let i = 0; i < blockSize; i++) { + x = x + Helper.rand(3) - 1; + y = y + Helper.rand(3) - 1; + if (x < 0 || x >= width || y < 0 || y >= height) {return amount} + if (world.isFree(x, y)) { + const c = colorCb ? colorCb(x, y) : color; + if (world.setDot(x, y, c)) { + setCb && setCb(x,y); + if ((amount += c) >= dots) {return amount} + } + } + } + + return amount; + } +} + +module.exports = Dots; \ No newline at end of file diff --git a/client/src/share/Events.js b/client/src/share/Events.js index d099c33..6ecde4f 100644 --- a/client/src/share/Events.js +++ b/client/src/share/Events.js @@ -38,7 +38,8 @@ const EVENTS = { RESET_CODE : 28, CHECK_AT : 29, WORLD_ENERGY : 30, - WORLD_ENERGY_UP : 31 + WORLD_ENERGY_UP : 31, + CONFIG_CHANGE : 32 }; diff --git a/client/src/view/Canvas.js b/client/src/view/Canvas.js index 20b405a..d1727e4 100644 --- a/client/src/view/Canvas.js +++ b/client/src/view/Canvas.js @@ -3,32 +3,51 @@ * * @author flatline */ -const Panzoom = require('panzoom'); -const Helper = require('./../../../common/src/Helper'); -const Config = require('./../share/Config').Config; +const Panzoom = require('panzoom'); +const Observer = require('./../../../common/src/Observer'); +const Helper = require('./../../../common/src/Helper'); +const Config = require('./../share/Config').Config; -class Canvas { +/** + * {Number} Amount of events in canvas + */ +const EVENT_AMOUNT = 1; +const EVENT_VISUALIZE = 0; +const EVENTS = { + VISUALIZE: EVENT_VISUALIZE +}; + +class Canvas extends Observer { constructor(width, height) { + super(EVENT_AMOUNT); const id = 'world'; const doc = document; doc.body.innerHTML += ``; - this._id = id; - this._width = width; - this._height = height; - this._canvasEl = doc.querySelector('#' + id); - this._ctx = this._canvasEl.getContext('2d'); - this._imgData = this._ctx.createImageData(this._width, this._height); - this._data = this._imgData.data; - this._animate = this._onAnimate.bind(this); - this._visualize = true; - this._panZoom = null; - this._fullEl = this._createFullScreen(); + this._id = id; + this._width = width; + this._height = height; + this._canvasEl = doc.querySelector('#' + id); + this._ctx = this._canvasEl.getContext('2d'); + this._imgData = this._ctx.createImageData(this._width, this._height); + this._data = this._imgData.data; + this._animate = this._onAnimate.bind(this); + this._visualize = true; + this._panZoom = null; + this._zoomObserver = null; + this._fullEl = this._createFullScreenBtn(); + this._visualizeEl = this._createVisualizeBtn(); + this._isVisualizeOn = true; + this._xDataOffs = 0; + this._yDataOffs = 0; + this._visibleWidth = Config.worldWidth; + this._visibleHeight = Config.worldHeight; this._prepareDom(); this._initPanZoomLib(); this.clear(); + this._onFullscreen(); window.requestAnimationFrame(this._animate); } @@ -38,11 +57,13 @@ class Canvas { this._panZoom.dispose(); parentNode.removeChild(this._canvasEl); parentNode.removeChild(this._fullEl); - this._canvasEl = null; - this._fullEl = null; - this._ctx = null; - this._imgData = null; - this._data = null; + parentNode.removeChild(this._visualizeEl); + this._canvasEl = null; + this._fullEl = null; + this._visualizeEl = null; + this._ctx = null; + this._imgData = null; + this._data = null; } visualize(visualize = true) { @@ -85,7 +106,7 @@ class Canvas { data[offs + 2] = color & 0xff; } - _createFullScreen() { + _createFullScreenBtn() { const el = document.body.appendChild(Helper.setStyles('DIV', { position : 'absolute', width : '20px', @@ -97,21 +118,63 @@ class Canvas { borderRadius : '6px', cursor : 'pointer' })); + // + // Inner div + // + const innerEl = document.body.appendChild(Helper.setStyles('DIV', { + position : 'absolute', + width : '10px', + height : '10px', + top : '12px', + left : '12px', + border : '1px #000 solid', + backgroundColor: '#f7ed0e', + borderRadius : '3px', + cursor : 'pointer' + })); + + el.title = 'fullscreen (Ctrl-F)'; + el.onclick = this._onFullscreen.bind(this); + innerEl.onclick = this._onFullscreen.bind(this); + + return el; + } + + _createVisualizeBtn() { + const el = document.body.appendChild(Helper.setStyles('DIV', { + position : 'absolute', + width : '20px', + height : '20px', + top : '7px', + left : '34px', + border : '1px #FFEB3B solid', + backgroundSize : '8px 8px', + borderRadius : '6px', + background : 'radial-gradient(#F44336 15%, transparent 16%) 0 0, radial-gradient(#F44336 15%, transparent 16%) 4px 4px, radial-gradient(rgba(255,255,253,.1) 15%, transparent 20%) 0 1px, radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 8px 8px', + backgroundColor: '#FFEB3B', + cursor : 'pointer' + })); - el.title = 'fullscreen'; - el.onclick = () => { - this._panZoom.zoomAbs(0, 0, 1.0); - this._panZoom.moveTo(0, 0); - this._canvasEl.style.width = '100%'; - this._canvasEl.style .height = '100%'; + el.title = 'visualize (Ctrl-V)'; + el.onclick = this._onVisualize.bind(this); - }; - return el; } + _onFullscreen() { + this._panZoom.zoomAbs(0, 0, 1.0); + this._panZoom.moveTo(0, 0); + this._canvasEl.style.width = '100%'; + this._canvasEl.style .height = '100%'; + } + + _onVisualize() { + this.fire(EVENT_VISUALIZE, this._isVisualizeOn = !this._isVisualizeOn); + this._visualizeEl.style.backgroundColor = this._isVisualizeOn ? '#FFEB3B' : '#000'; + } + _onAnimate() { - this._ctx.putImageData(this._imgData, 0, 0); + this._ctx.putImageData(this._imgData, 0, 0, this._xDataOffs, this._yDataOffs, this._visibleWidth, this._visibleHeight); if (this._visualize === true) { window.requestAnimationFrame(this._animate); @@ -140,6 +203,32 @@ class Canvas { // This style hides scroll bars on full screen 2d canvas // document.querySelector('html').style.overflow = 'hidden'; + // + // Adds listener to change of canvas transform matrix. We need it + // to handle zooming of the canvas + // + this._zoomObserver = new MutationObserver(this._onZoom.bind(this)); + this._zoomObserver.observe(this._canvasEl, { + attributes : true, + childList : false, + attributeFilter: ['style'] + }); + // + // Global keyup event handler + // + document.addEventListener('keydown', this._onKeyDown.bind(this)); + } + + _onKeyDown(event) { + if (event.ctrlKey && (event.key === 'V' || event.key === 'v')){ + this._onVisualize(); + event.preventDefault(); + return false; + } else if (event.ctrlKey && (event.key === 'F' || event.key === 'f')) { + this._onFullscreen(); + event.preventDefault(); + return false; + } } /** @@ -155,6 +244,32 @@ class Canvas { }); this._panZoom.zoomAbs(0, 0, 1.0); } + + /** + * Is called on canvas zoom/move change. This method improves rendering + * speed of big canvases. It copies only visible part of the canvas from + * memory (see this._imgData). + */ + _onZoom() { + const transform = window.getComputedStyle(this._canvasEl, null).getPropertyValue('transform'); + if (transform === 'none') {return} + const matrix = transform.split('(')[1].split(')')[0].split(','); + const dx = +matrix[4]; + const dy = +matrix[5]; + const coef = +matrix[0]; + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; + const viewWidth = windowWidth * coef; + const viewHeight = windowHeight * coef; + const xCoef = Config.worldWidth / windowWidth; + const yCoef = Config.worldHeight / windowHeight; + + this._xDataOffs = (dx < 0 ? (coef > 1 ? -dx / coef : -dx * coef) : 0) * xCoef; + this._yDataOffs = (dy < 0 ? (coef > 1 ? -dy / coef : -dy * coef) : 0) * yCoef; + + this._visibleWidth = (viewWidth + dx > windowWidth ? (coef > 1 ? (windowWidth - (dx > 0 ? dx : 0)) / coef : (windowWidth - (dx > 0 ? dx : 0)) * coef) : windowWidth) * xCoef; + this._visibleHeight = (viewHeight + dy > windowHeight ? (coef > 1 ? (windowHeight - (dy > 0 ? dy : 0)) / coef : (windowHeight - (dy > 0 ? dy : 0)) * coef) : windowWidth) * yCoef; + } } -module.exports = Canvas; \ No newline at end of file +module.exports = {Canvas, EVENTS}; \ No newline at end of file diff --git a/client/src/vm/Code2JS.js b/client/src/vm/Code2JS.js new file mode 100644 index 0000000..44e36ab --- /dev/null +++ b/client/src/vm/Code2JS.js @@ -0,0 +1,133 @@ +/** + * This class is used only for code translation from byte code to JavaScript + * in readable human like form. It translates only base operators. Generally + * all you need to do is create an instance and call format() method with a + * byte code as a parameter. + * + * @author flatline + */ +const Num = require('./../vm/Num'); +const OConfig = require('./../manager/plugins/organisms/Config'); +const Operators = require('./Operators'); + +class Code2JS { + constructor() { + /** + * @constant + * {Array} Available condition operations for if, while and math operators. Amount + * of operations should be 16 (4 bits). + */ + this._CONDITIONS = ['+','-','*','/','%','&','|','^','>>','<<','>>>','<','>','==','!=','<=']; + /** + * {Object} These operator handlers should return string representation + * of numeric based byte vm. + */ + this._operators = { + 0b100000: this._onVar.bind(this), + 0b100001: this._onConst.bind(this), + 0b100010: this._onIf.bind(this), + 0b100011: this._onLoop.bind(this), + 0b100100: this._onOperator.bind(this), + 0b100101: this._onFunc.bind(this), + 0b100110: this._onFuncCall.bind(this), + 0b100111: this._onReturn.bind(this), + 0b101000: this._onBracket.bind(this), + 0b101001: this._onToMem.bind(this), + 0b101010: this._onFromMem.bind(this), + 0b101011: this._onRand.bind(this) + }; + } + + get operators() { + return this._operators; + } + + format(code, separator = '\n', raw = false) { + const len = code.length; + const operators = this._operators; + let jsCode = new Array(len); + const offs = new Array(len); + const vars = new Array(Math.pow(2, OConfig.codeBitsPerVar)); + const ops = new Operators(offs, vars); + + ops.updateIndexes(code); + for (let line = 0; line < len; line++) { + jsCode[line] = operators[Num.getOperator(code[line])](code[line], line, ops); + } + ops.destroy(); + + return raw ? jsCode.join(separator) : js_beautify(jsCode.join(separator), {indent_size: 4}); + } + + destroy() { + this._operators = null; + this._CONDITIONS = null; + } + + /** + * Parses var operator. Format: var1 = var0. + * @param {Num} num Packed into number vm line + * @return {String} Parsed vm line string + */ + _onVar(num) { + return `v${Num.getVar0(num)}=v${Num.getVar1(num)}`; + } + + _onConst(num) { + return `v${Num.getVar0(num)}=${Num.getBits(num, Num.BITS_OF_VAR1, OConfig.codeConstBits)}`; + } + + _onIf(num, line, ops) { + const cond = Num.getBits(num, Num.BITS_OF_VAR2, Operators.CONDITION_BITS); + const bracket = ops.offs[line] === line ? '}' : ''; + return `if(v${Num.getVar0(num)}${this._CONDITIONS[cond]}v${Num.getVar1(num)}){${bracket}`; + } + + _onLoop(num, line, ops) { + const cond = Num.getBits(num, Num.BITS_OF_VAR2, Operators.CONDITION_BITS); + const bracket = ops.offs[line] === line ? '}' : ''; + return `while(v${Num.getVar0(num)}${this._CONDITIONS[cond]}v${Num.getVar1(num)}){${bracket}`; + } + + _onOperator(num) { + return `v${Num.getVar0(num)}=v${Num.getVar1(num)}${this._CONDITIONS[Num.getBits(num, Num.BITS_OF_VAR3, Operators.CONDITION_BITS)]}v${Num.getVar2(num)}`; + } + + _onFunc(num, line, ops) { + const bracket = ops.offs[line] === line ? '}' : ''; + return `function f${ops.funcs.indexOf(line + 1)}(){${bracket}`; + } + + _onFuncCall(num, line, ops) { + let fn; + + if (Num.getBits(num, Num.BITS_OF_VAR0, 1) === 1) { + fn = `f${Num.getBits(num, Num.BITS_OF_VAR0 + 1, Operators.FUNC_NAME_BITS) % (ops.funcAmount || 1)}`; + } else { + fn = `v${Num.getBits(num, Num.BITS_OF_VAR0 + 1, OConfig.codeBitsPerVar) % (ops.funcAmount || 1)}`; + } + return `${fn}()`; + } + + _onReturn() { + return 'return'; + } + + _onBracket(num, line, ops) { + return ops.offs[line] === undefined ? '// }' : '}'; + } + + _onToMem(num) { + return `toMem(v${Num.getVar0(num)},v${Num.getVar1(num)})`; + } + + _onFromMem(num) { + return `v${Num.getVar0(num)}=fromMem(v${Num.getVar1(num)})`; + } + + _onRand(num) { + return `v${Num.getVar0(num)}=rand(v${Num.getVar1(num)})`; + } +} + +module.exports = Code2JS; \ No newline at end of file diff --git a/client/src/vm/Code2JSSpec.js b/client/src/vm/Code2JSSpec.js new file mode 100644 index 0000000..5007ed2 --- /dev/null +++ b/client/src/vm/Code2JSSpec.js @@ -0,0 +1,37 @@ +const Operators = require('./Operators'); +const Code2JS = require('./Code2JS'); +const OConfig = require('./../manager/plugins/organisms/Config'); +const Helper = require('./../../../common/src/Helper'); + +describe("client/src/vm/Operators", () => { + const h = Helper.toHexNum; + let cbpv; + let ccb; + let ctjs; + let offs; + + beforeAll(() => { + cbpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 2; + ccb = OConfig.codeConstBits; + OConfig.codeConstBits = 3; + Operators.compile(); + }); + afterAll(() => { + OConfig.codeBitsPerVar = cbpv; + OConfig.codeConstBits = ccb; + }); + beforeEach(() => { + ctjs = new Code2JS(); + }); + afterEach(() => { + ctjs.destroy(); + ctjs = null; + }); + + describe('var() operator', () => { + it('Checks simplpe var assign', () => { + expect(ctjs.format([h('100000 00 01 0000000000000000000000')], '\n', true)).toEqual('v0=v1'); + }); + }); +}); \ No newline at end of file diff --git a/client/src/vm/Num.js b/client/src/vm/Num.js index 1a7c6a8..44d0170 100644 --- a/client/src/vm/Num.js +++ b/client/src/vm/Num.js @@ -14,30 +14,33 @@ class Num { * script implementation */ static init(operatorAmount) { - this.MAX_BITS = 32; - this.OPERATOR_AMOUNT = operatorAmount; - this.BITS_PER_VAR = OConfig.codeBitsPerVar; - this.BITS_PER_OPERATOR = OConfig.codeBitsPerOperator; - this.NO_OPERATOR_MASK = 0xffffffff >>> this.BITS_PER_OPERATOR; - this.BITS_OF_TWO_VARS = this.BITS_PER_VAR * 2; - this.BITS_OF_FIRST_VAR = this.MAX_BITS - this.BITS_PER_VAR; - this.MAX_VAR = 1 << this.BITS_PER_VAR; - this.VAR_BITS_OFFS = this.MAX_BITS - this.BITS_PER_OPERATOR; + this.MAX_BITS = 32; + this.OPERATOR_AMOUNT = operatorAmount; + this.BITS_PER_VAR = OConfig.codeBitsPerVar; + this.BITS_PER_OPERATOR = OConfig.CODE_BITS_PER_OPERATOR; + this.NO_OPERATOR_MASK = 0xffffffff >>> this.BITS_PER_OPERATOR; + this.OPERATOR_MASK_ON = 0x80000000; + this.OPERATOR_MASK_OFF = 0x7fffffff; + this.BITS_OF_TWO_VARS = this.BITS_PER_VAR * 2; + this.BITS_OF_FIRST_VAR = this.MAX_BITS - this.BITS_PER_VAR; + this.MAX_VAR = 1 << this.BITS_PER_VAR; + this.VAR_BITS_OFFS = this.MAX_BITS - this.BITS_PER_OPERATOR; - this.BITS_OF_VAR0 = this.BITS_PER_OPERATOR; - this.BITS_OF_VAR1 = this.BITS_PER_OPERATOR + this.BITS_PER_VAR; - this.BITS_OF_VAR2 = this.BITS_PER_OPERATOR + 2 * this.BITS_PER_VAR; - this.BITS_OF_VAR3 = this.BITS_PER_OPERATOR + 3 * this.BITS_PER_VAR; + this.BITS_OF_VAR0 = this.BITS_PER_OPERATOR; + this.BITS_OF_VAR1 = this.BITS_PER_OPERATOR + this.BITS_PER_VAR; + this.BITS_OF_VAR2 = this.BITS_PER_OPERATOR + 2 * this.BITS_PER_VAR; + this.BITS_OF_VAR3 = this.BITS_PER_OPERATOR + 3 * this.BITS_PER_VAR; } /** * Returns random number for byte code. We have to use >>> 0 at * the end, because << operator works with signed 32bit numbers, - * but not with unsigned like we need + * but not with unsigned like we need. We have to use mask - zero + * bit turned on to prevent duplication of operators+data values. * @returns {number} */ static rand() { - return (Helper.rand(this.OPERATOR_AMOUNT) << (this.VAR_BITS_OFFS) | Helper.rand(this.NO_OPERATOR_MASK)) >>> 0; + return ((Helper.rand(this.OPERATOR_AMOUNT) << (this.VAR_BITS_OFFS) | Helper.rand(this.NO_OPERATOR_MASK)) | this.OPERATOR_MASK_ON) >>> 0; } static getOperator(num) { @@ -45,7 +48,7 @@ class Num { } static setOperator(num, op) { - return (op << this.VAR_BITS_OFFS | (num & this.NO_OPERATOR_MASK)) >>> 0; + return ((op << this.VAR_BITS_OFFS | (num & this.NO_OPERATOR_MASK)) | this.OPERATOR_MASK_ON) >>> 0; } static getVar(num, index = 0) { diff --git a/client/src/vm/NumSpec.js b/client/src/vm/NumSpec.js index 5934658..d12401a 100644 --- a/client/src/vm/NumSpec.js +++ b/client/src/vm/NumSpec.js @@ -2,18 +2,17 @@ let Num = require('./Num'); let OConfig = require('./../manager/plugins/organisms/Config'); describe("client/src/organism/Num", () => { - const bpo = OConfig.codeBitsPerOperator; const bpv = OConfig.codeBitsPerVar; - beforeEach(() => {OConfig.codeBitsPerOperator = 8; OConfig.codeBitsPerVar = 2}); - afterEach( () => {OConfig.codeBitsPerOperator = bpo; OConfig.codeBitsPerVar = bpv}); + beforeEach(() => {OConfig.codeBitsPerVar = 2}); + afterEach( () => {OConfig.codeBitsPerVar = bpv}); describe('Checks initialization', () => { it ('Checks init() method', () => { Num.init(3); expect(Num.OPERATOR_AMOUNT).toBe(3); expect(Num.BITS_PER_VAR).toBe(OConfig.codeBitsPerVar); - expect(Num.BITS_PER_OPERATOR).toBe(OConfig.codeBitsPerOperator); + expect(Num.BITS_PER_OPERATOR).toBe(OConfig.CODE_BITS_PER_OPERATOR); }); }); diff --git a/client/src/vm/Operators.js b/client/src/vm/Operators.js index ffff580..cc3a785 100644 --- a/client/src/vm/Operators.js +++ b/client/src/vm/Operators.js @@ -4,34 +4,531 @@ * * @author flatline */ +const OConfig = require('./../manager/plugins/organisms/Config'); +const Num = require('./Num'); +const Helper = require('./../../../common/src/Helper'); + +const OPERATOR_AMOUNT = 12; +const MAX_STACK_SIZE = 30000; + class Operators { - constructor(offs, vars, obs) { + /** + * Compiles all variants of bite-code to speed up the execution. + * @param {Number} operators Amount of operators + */ + static compile(operators = OPERATOR_AMOUNT) { + const bitsPerOp = OConfig.CODE_BITS_PER_OPERATOR; + const MAX_BITS = 32; + /** + * {Object} Container for storing of dynamic operator handlers + */ + this.global = typeof window === 'undefined' ? global : window; + /** + * {Number} Bit related constants + */ + this.CONDITION_BITS = 4; + this.FUNC_NAME_BITS = 8; + /** + * {Array} Available conditions for if operator + */ + this._CONDITIONS = ['+', '-', '*', '/', '%', '&', '|', '^', '>>', '<<', '>>>', '<', '>', '===', '!==', '<=']; + /** + * {Array} Available operators for math calculations + */ + this._OPERATORS = [ + (a,b,c) => `const v=this.vars[${b}] + this.vars[${c}]; this.vars[${a}]=Number.isFinite(v) ? v : Number.MAX_VALUE`, + (a,b,c) => `const v=this.vars[${b}] - this.vars[${c}]; this.vars[${a}]=Number.isFinite(v) ? v :-Number.MAX_VALUE`, + (a,b,c) => `const v=this.vars[${b}] * this.vars[${c}]; this.vars[${a}]=Number.isFinite(v) ? v : Number.MAX_VALUE`, + (a,b,c) => `const v=this.vars[${b}] / this.vars[${c}]; this.vars[${a}]=Number.isFinite(v) ? v : Number.MAX_VALUE`, + (a,b,c) => `const v=this.vars[${b}] % this.vars[${c}]; this.vars[${a}]=Number.isNaN(v) ? 0 : v`, + (a,b,c) => `this.vars[${a}] = this.vars[${b}] & this.vars[${c}]`, + (a,b,c) => `this.vars[${a}] = this.vars[${b}] | this.vars[${c}]`, + (a,b,c) => `this.vars[${a}] = this.vars[${b}] ^ this.vars[${c}]`, + (a,b,c) => `this.vars[${a}] = this.vars[${b}] >> this.vars[${c}]`, + (a,b,c) => `this.vars[${a}] = this.vars[${b}] << this.vars[${c}]`, + (a,b,c) => `this.vars[${a}] = this.vars[${b}] >>> this.vars[${c}]`, + (a,b,c) => `this.vars[${a}] = +(this.vars[${b}] < this.vars[${c}])`, + (a,b,c) => `this.vars[${a}] = +(this.vars[${b}] > this.vars[${c}])`, + (a,b,c) => `this.vars[${a}] = +(this.vars[${b}] === this.vars[${c}])`, + (a,b,c) => `this.vars[${a}] = +(this.vars[${b}] !== this.vars[${c}])`, + (a,b,c) => `this.vars[${a}] = +(this.vars[${b}] <= this.vars[${c}])` + ]; + /** + * {Array} Array of lengths of commands. Every item of this array contains + * length of operator's data, which follows after len bits in byte-code number. + * The size of this array is equal to amount of operators. Child classes may + * append items to it. + */ + this.LENS = [ + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 2), // var + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar), // const + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 2 + this.CONDITION_BITS), // if + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 2 + this.CONDITION_BITS), // loop + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 3 + this.CONDITION_BITS), // math + MAX_BITS - (bitsPerOp), // func + MAX_BITS - (bitsPerOp), // func call + MAX_BITS - (bitsPerOp), // return + MAX_BITS - (bitsPerOp), // bracket + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 2), // toMem + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 2), // fromMem + MAX_BITS - (bitsPerOp + OConfig.codeBitsPerVar * 2) // rand + ]; + /** + * {Object} Map for all available operator handlers + */ + this._compiledOperators = {}; + // + // IMPORTANT: don't change callbacks order + // This section compiles all possible variants of operators and their + // arguments. This is how we dramatically speed up byte-code interpretation + // + Num.init(operators); + this._compileVar(); // 0 + this._compileConst(); // 1 + this._compileIf(); // 2 + this._compileLoop(); // 3 + this._compileOperator(); // 4 + this._compileFunc(); // 5 + this._compileFuncCall(); // 6 + this._compileReturn(); // 7 + this._compileBracket(); // 8 + this._compileToMem(); // 9 + this._compileFromMem(); // 10 + this._compileRand(); // 11 + } + + /** + * Compiles all variants of var operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx + * number: 100000 00 01... + * string: v0 = v1 + */ + static _compileVar() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + eval(`Operators.global.fn = function vars(line) { + this.vars[${v0}] = this.vars[${v1}]; + return ++line; + }`); + ops[h(`${'100000'}${b(v0, bpv)}${b(v1, bpv)}`)] = this.global.fn; + } + } + } + + /** + * Compiles all variants of const operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx + * number: 100001 00 01... + * string: v0 = 1 + */ + static _compileConst() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const bits = Num.MAX_BITS - OConfig.codeConstBits; + const bits1var = Num.BITS_OF_VAR1; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + eval(`Operators.global.fn = function consts(line, num) { + this.vars[${v0}] = num << ${bits1var} >>> ${bits}; + return ++line; + }`); + ops[h(`${'100001'}${b(v0, bpv)}`)] = this.global.fn; + } + } + + /** + * Compiles all variants of if operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx 4 + * number: 100010 00 01 0000... + * string: if (v0 + v1) { + */ + static _compileIf() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let c = 0; c < Math.pow(2, this.CONDITION_BITS); c++) { + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + eval(`Operators.global.fn = function cond(line) { + if (this.vars[${v0}] ${this._CONDITIONS[c]} this.vars[${v1}]) {return ++line} + return this.offs[line] === line ? ++line : this.offs[line]; + }`); + ops[h(`${'100010'}${b(v0, bpv)}${b(v1, bpv)}${b(c, this.CONDITION_BITS)}`)] = this.global.fn; + } + } + } + } + + /** + * Compiles all variants of while operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx 4 + * number: 100011 00 01 00... + * string: while (v0 < v1) { + */ + static _compileLoop() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let c = 0; c < Math.pow(2, this.CONDITION_BITS); c++) { + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + eval(`Operators.global.fn = function loop(line) { + if (this.vars[${v0}] ${this._CONDITIONS[c]} this.vars[${v1}]) {return ++line} + return this.offs[line] === line ? ++line : this.offs[line]; + }`); + ops[h(`${'100011'}${b(v0, bpv)}${b(v1, bpv)}${b(c, this.CONDITION_BITS)}`)] = this.global.fn; + } + } + } + } + + /** + * Compiles all variants of math operators and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx xx 4 + * number: 100100 00 01 00 0001... + * string: v0 = v1 - v0 + */ + static _compileOperator() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + const opsLen = Math.pow(2, this.CONDITION_BITS); + + for (let op = 0; op < opsLen; op++) { + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + for (let v2 = 0; v2 < vars; v2++) { + eval(`Operators.global.fn = function math(line) { + ${this._OPERATORS[op](v0, v1, v2)} + return ++line; + }`); + ops[h(`${'100100'}${b(v0, bpv)}${b(v1, bpv)}${b(v2, bpv)}${b(op, this.CONDITION_BITS)}`)] = this.global.fn; + } + } + } + } + } + + /** + * Compiles all variants of function operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Function name(index) consists of 8 bits. Example: + * + * bits : 6 + * number: 100101... + * string: func + */ + static _compileFunc() { + const ops = this._compiledOperators; + const h = Helper.toHexNum; + + eval(`Operators.global.fn = function func(line) {return this.offs[line] === line ? ++line : this.offs[line]}`); + ops[h(`${'100101'}`)] = this.global.fn; + } + + /** + * Compiles all variants of function call operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. If condition bit is set to 0, then variable value will be + * used as function name. If 1 then hard coded functions name will be + * get from byte-code. Function name consists of 10 bits. Example: + * + * bits : 6 1 8 + * number: 100110 1 00000001... + * string: call v1 + */ + static _compileFuncCall() { + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const ifBit = Num.MAX_BITS - 1; + const fnBits = Num.MAX_BITS - this.FUNC_NAME_BITS; + const varBits = Num.MAX_BITS - OConfig.codeBitsPerVar; + const opBits = Num.BITS_PER_OPERATOR; + + eval(`Operators.global.fn = function call(line, num, org) { + const data = num << ${opBits}; + const offs = this.funcs[data >>> ${ifBit} === 0 ? Math.round(this.vars[data << 1 >>> ${varBits}]) % this.funcAmount : (data << 1 >>> ${fnBits}) % this.funcAmount]; + if (offs !== undefined) { + // + // Maximum amount of functions were created + // + if (this.stack.length > ${MAX_STACK_SIZE}) { + org.energy -= org.vm.size; + return ++line; + } + // + // Function has no body, so we don't need to jump into it + // + if (this.offs[offs - 1] === offs - 1) {return ++line} + this.stack.push(line + 1, offs - 1, this.vars.slice()); + return offs; + } + return ++line; + }`); + ops[h(`${'100110'}`)] = this.global.fn; + } + + /** + * Compiles all variants of return operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Returns fro custom function. If returns appears outside + * the function, then interpreter jumps into zero line. Example: + * + * bits : 6 + * number: 100111 ... + * string: return + */ + static _compileReturn() { + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const vars = Math.pow(2, OConfig.codeBitsPerVar); + + eval(`Operators.global.fn = function ret(line) { + const stack = this.stack; + if (stack.length > 0) { + const stackVars = stack.pop(); + const vars = this.vars; + for (let i = 0; i < ${vars}; i++) {vars[i] = stackVars[i]} + stack.pop(); + return stack.pop(); + } + return 0; + }`); + ops[h(`${'100111'}`)] = this.global.fn; + } + + /** + * Compiles all variants of } operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 + * number: 101000... + * string: } + */ + static _compileBracket() { + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const vars = Math.pow(2, OConfig.codeBitsPerVar); + + eval(`Operators.global.fn = function bracket(line, num, org, lines) { + const startLine = this.offs[line]; + const operator = startLine === undefined ? -1 : lines[startLine] >>> Num.VAR_BITS_OFFS; + if (operator === 0b100011) {return this.offs[line]} // loop + if (operator === 0b100101) { // func + const stack = this.stack; + if (stack[stack.length - 2] === startLine) { + const stackVars = stack.pop(); + const vars = this.vars; + for (let i = 0; i < ${vars}; i++) {vars[i] = stackVars[i]} + stack.pop(); + return this.stack.pop(); + } + } + return ++line; + }`); + ops[h(`${'101000'}`)] = this.global.fn; + } + + /** + * Compiles all variants of toMem operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx + * number: 101001 00 01... + * string: toMem(v0, v1) + */ + static _compileToMem() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + const memLen = Math.pow(2, OConfig.orgMemBits) - 1; + + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + eval(`Operators.global.fn = function toMem(line, num, org) { + const offs = ((this.vars[${v0}] + .5) << 0) >>> 0; + org.mem[offs > ${memLen} ? ${memLen} : offs] = this.vars[${v1}]; + return ++line; + }`); + ops[h(`${'101001'}${b(v0, bpv)}${b(v1, bpv)}`)] = this.global.fn; + } + } + } + + /** + * Compiles all variants of toMem operator and stores they in + * this._compiledOperators map. 'xx' means, that amount of bits + * depends on configuration. '...' means, that all other bits are + * ignored. Example: + * + * bits : 6 xx xx + * number: 101010 00 01... + * string: v0 = fromMem(v1) + */ + static _compileFromMem() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + const memLen = Math.pow(2, OConfig.orgMemBits) - 1; + + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + eval(`Operators.global.fn = function fromMem(line, num, org) { + const offs = ((this.vars[${v1}] + .5) << 0) >>> 0; + this.vars[${v0}] = org.mem[offs > ${memLen} ? ${memLen} : offs]; + return ++line; + }`); + ops[h(`${'101010'}${b(v0, bpv)}${b(v1, bpv)}`)] = this.global.fn; + } + } + } + + /** + * Compiles all variants of 'rand' operator and stores they in + * this._compiledOperators map. '...' means, that all other bits are + * ignored. Step direction depends on active organism's direction. + * See Organism.dir property. Example: + * + * bits : 6 xx xx + * number: 101011 01 11... + * string: v1 = rand(v3) + */ + static _compileRand() { + const bpv = OConfig.codeBitsPerVar; + const ops = this._compiledOperators; + const h = Helper.toHexNum; + const b = Helper.toBinStr; + const vars = Math.pow(2, bpv); + + for (let v0 = 0; v0 < vars; v0++) { + for (let v1 = 0; v1 < vars; v1++) { + eval(`Operators.global.fn = function rand(line) { + this.vars[${v0}] = Helper.rand(((this.vars[${v1}] + .5) << 0 >>> 0)); + return ++line; + }`); + ops[h(`${'101011'}${b(v0, bpv)}${b(v1, bpv)}`)] = this.global.fn; + } + } + } + + constructor(offs, vars) { + this._MAX_FUNC_AMOUNT = Math.pow(2, Operators.FUNC_NAME_BITS); /** * {Array} Array of offsets for closing braces. For 'for', 'if' * and other operators. */ - this.offs = offs; + this.offs = offs; /** * {Array} Available variables */ - this.vars = vars; + this.vars = vars; /** - * {Observer} Observer for sending external events + * {Object} Handlers map, which handle every variant of byte-code */ - this.obs = obs; + this._OPERATORS_CB = Operators._compiledOperators; + this.stack = []; + this.funcs = new Array(this._MAX_FUNC_AMOUNT); + this.funcAmount = 0; + } + + /** + * Updates indexes of if, while, func,... in byte-code after any mutation + * @param {Array} code Byte-code + */ + updateIndexes(code) { + const len = code.length; + const varOffs = Num.VAR_BITS_OFFS; + const offs = this.offs; + const funcs = this.funcs = new Array(this._MAX_FUNC_AMOUNT); + const blocks = []; + + this.funcAmount = 0; + this.stack = []; + for (let i = 0; i < len; i++) { + const operator = code[i] >>> varOffs; + if (operator === 0b100010 || operator === 0b100011) { // if, while + offs[i] = i; + blocks.push(i); + continue; + } + if (operator === 0b100101) { // func + offs[i] = i; + funcs[this.funcAmount++] = i + 1; + blocks.push(i); + continue; + } + if (operator === 0b101000) { // bracket + if (blocks.length > 0) { + offs[i] = blocks.pop(); + offs[offs[i]] = i + 1; + } + } + } } destroy() { - this.offs = null; - this.vars = null; - this.obs = null; + this.funcs = null; + this.stack = null; + this._OPERATORS_CB = null; + this.offs = null; + this.vars = null; } /** * Returns operators array. Should be overridden in child class * @abstract */ - get operators() {return []} + get length() {return OPERATOR_AMOUNT} + + get operators() {return this._OPERATORS_CB} /** * Sets offsets array from outside diff --git a/client/src/vm/OperatorsSpec.js b/client/src/vm/OperatorsSpec.js new file mode 100644 index 0000000..d51e9bd --- /dev/null +++ b/client/src/vm/OperatorsSpec.js @@ -0,0 +1,1408 @@ +const Operators = require('./Operators'); +const OConfig = require('./../manager/plugins/organisms/Config'); +const Helper = require('./../../../common/src/Helper'); + +describe("client/src/vm/Operators", () => { + const h = Helper.toHexNum; + const MAX_NUM = Number.MAX_VALUE; + let cbpv; + let ccb; + let ops; + let offs; + let vars; + + beforeAll (() => { + cbpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 2; + ccb = OConfig.codeConstBits; + OConfig.codeConstBits = 3; + Operators.compile(); + }); + afterAll (() => { + OConfig.codeBitsPerVar = cbpv; + OConfig.codeConstBits = ccb; + }); + beforeEach(() => { + vars = [0,1,2,3]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + }); + + describe('Creation and destroy', () => { + it('Checks creation', () => { + expect(ops.offs).toEqual(offs); + expect(ops.vars).toEqual(vars); + expect(Array.isArray(ops.stack)).toEqual(true); + expect(Array.isArray(ops.funcs)).toEqual(true); + expect(Object.keys(ops.operators).length > 0).toEqual(true); + }); + + it('Checks simple destroy', () => { + const ops2 = new Operators(offs, vars); + const offs2 = offs; + const vars2 = vars; + expect(ops2.offs).toEqual(offs); + expect(ops2.vars).toEqual(vars); + expect(Array.isArray(ops2.stack)).toEqual(true); + expect(Array.isArray(ops2.funcs)).toEqual(true); + expect(Object.keys(ops.operators).length > 0).toEqual(true); + ops2.destroy(); + expect(ops2.offs).toEqual(null); + expect(ops2.vars).toEqual(null); + expect(ops2.stack).toEqual(null); + expect(ops2.funcs).toEqual(null); + expect(ops2.operators).toEqual(null); + expect(offs2).toEqual(offs); + expect(vars2).toEqual(vars); + }); + }); + + describe('vars 2bits per var', () => { + it('Checks v0=v1', () => { + expect(ops.operators[h('100000 00 01')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3]); + }); + it('Checks v0=v0', () => { + expect(ops.operators[h('100000 00 00')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3]); + }); + it('Checks v0=v3', () => { + expect(ops.operators[h('100000 00 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([3,1,2,3]); + }); + it('Checks v3=v3', () => { + expect(ops.operators[h('100000 11 11')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3]); + }); + it('Checks correct line return', () => { + expect(ops.operators[h('100000 11 11')].call(ops, 0)).toEqual(1); + expect(ops.operators[h('100000 11 01')].call(ops, 1)).toEqual(2); + expect(ops.operators[h('100000 01 11')].call(ops, 100)).toEqual(101); + }); + it('Garbage in a tail should not affect vars', () => { + expect(ops.operators[h('100000 00 01')].call(ops, 0, h('100000 00 01 111111111111111111111'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3]); + expect(ops.operators[h('100000 00 00')].call(ops, 0, h('100000 00 00 111111111111111111111'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3]); + }); + + describe('vars 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + Operators.compile(); + }); + afterAll (() => Operators.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + + it('Checks v0=v1', () => { + expect(ops.operators[h('100000 000 001')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3,4,5,6,7]); + }); + it('Checks v0=v0', () => { + expect(ops.operators[h('100000 000 000')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3,4,5,6,7]); + }); + it('Checks v0=v3', () => { + expect(ops.operators[h('100000 000 011')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([3,1,2,3,4,5,6,7]); + }); + it('Checks v7=v7', () => { + expect(ops.operators[h('100000 111 111')].call(ops, 0)).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3,4,5,6,7]); + }); + it('Checks correct line return', () => { + expect(ops.operators[h('100000 011 011')].call(ops, 0)).toEqual(1); + expect(ops.operators[h('100000 011 001')].call(ops, 1)).toEqual(2); + expect(ops.operators[h('100000 001 011')].call(ops, 100)).toEqual(101); + }); + it('Garbage in a tail should not affect vars', () => { + expect(ops.operators[h('100000 000 001')].call(ops, 0, h('100000 000 001 11111111111111111111'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3,4,5,6,7]); + expect(ops.operators[h('100000 000 000')].call(ops, 0, h('100000 000 000 11111111111111111111'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3,4,5,6,7]); + }); + }); + }); + + describe('consts 2bits per var', () => { + it('Checks v0=1', () => { + expect(ops.operators[h('100001 00')].call(ops, 0, h('100001 00 001 000000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3]); + }); + it('Checks v0=0', () => { + expect(ops.operators[h('100001 00')].call(ops, 0, h('100001 00 000 000000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3]); + }); + it('Checks v0=3', () => { + expect(ops.operators[h('100001 00')].call(ops, 0, h('100001 00 011 000000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([3,1,2,3]); + }); + it('Checks v3=3', () => { + expect(ops.operators[h('100001 11')].call(ops, 0, h('100001 11 011 000000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3]); + }); + it('Checks correct line return', () => { + expect(ops.operators[h('100001 11')].call(ops, 0, h('100001 11 011 000000000000000000000'))).toEqual(1); + expect(ops.operators[h('100001 11')].call(ops, 1, h('100001 11 001 000000000000000000000'))).toEqual(2); + expect(ops.operators[h('100001 01')].call(ops, 100, h('100001 01 011 000000000000000000000'))).toEqual(101); + }); + it('Garbage in a tail should not affect vars', () => { + expect(ops.operators[h('100001 00')].call(ops, 0, h('100001 00 001 111111111111111111111'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3]); + expect(ops.operators[h('100001 00')].call(ops, 1, h('100001 00 000 111111111111111111111'))).toEqual(2); + expect(ops.vars).toEqual([0,1,2,3]); + }); + + describe('consts 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + Operators.compile(); + }); + afterAll (() => Operators.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + + it('Checks v0=1', () => { + expect(ops.operators[h('100001 000')].call(ops, 0, h('100001 000 001 00000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3,4,5,6,7]); + }); + it('Checks v0=0', () => { + expect(ops.operators[h('100001 000')].call(ops, 0, h('100001 000 000 00000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3,4,5,6,7]); + }); + it('Checks v0=3', () => { + expect(ops.operators[h('100001 000')].call(ops, 0, h('100001 000 011 00000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([3,1,2,3,4,5,6,7]); + }); + it('Checks v7=7', () => { + expect(ops.operators[h('100001 111')].call(ops, 0, h('100001 000 111 00000000000000000000'))).toEqual(1); + expect(ops.vars).toEqual([0,1,2,3,4,5,6,7]); + }); + it('Checks correct line return', () => { + expect(ops.operators[h('100001 011')].call(ops, 0, h('100001 011 011 00000000000000000000'))).toEqual(1); + expect(ops.operators[h('100001 011')].call(ops, 1, h('100001 011 001 00000000000000000000'))).toEqual(2); + expect(ops.operators[h('100001 001')].call(ops, 100, h('100001 001 011 00000000000000000000'))).toEqual(101); + }); + it('Garbage in a tail should not affect vars', () => { + expect(ops.operators[h('100001 000')].call(ops, 0, h('100001 000 001 11111111111111111111'))).toEqual(1); + expect(ops.vars).toEqual([1,1,2,3,4,5,6,7]); + expect(ops.operators[h('100001 000')].call(ops, 1, h('100001 000 000 11111111111111111111'))).toEqual(2); + expect(ops.vars).toEqual([0,1,2,3,4,5,6,7]); + }); + }); + }); + + describe('ifs 2bits per var', () => { + it('if(v3!==v3) should be false', () => { + const code = [ + h('100010 11 11 1110 000000000000000000'), // if (v3!==v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 1110')].call(ops, 0)).toEqual(3); + }); + it('if(v0+v1) should be true', () => { + const code = [ + h('100010 00 01 0000 000000000000000000'), // if (v0+v1) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 0000')].call(ops, 0)).toEqual(1); + }); + it('if(v0<=v0) should be false', () => { + const code = [ + h('100010 00 00 1111 000000000000000000'), // if (v0<=v0) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 00 00 1111')].call(ops, 0)).toEqual(1); + }); + it('Garbage in a tail should not affect if', () => { + const code = [ + h('100010 00 01 0000 111111111111111111'), // if (v0+v1) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 11111111111111111111111111') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 0000')].call(ops, 0)).toEqual(1); + }); + it('Test of one if and two closed brackets', () => { + const code = [ + h('100010 11 11 1110 000000000000000000'), // if (v3!==v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 1110')].call(ops, 0)).toEqual(3); + }); + it('Test of one if and two closed brackets 2', () => { + const code = [ + h('101000 00000000000000000000000000'), // } + h('100010 11 11 1110 000000000000000000'), // if (v3!==v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 1110')].call(ops, 1)).toEqual(4); + }); + it('if(false) without closed bracket should go to the next line', () => { + const code = [ + h('100010 11 11 1110 000000000000000000'), // if (v3!==v3) { + h('100000 00 01 1111111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 1110')].call(ops, 0)).toEqual(1); + }); + it('if(true) without closed bracket should go to the next line', () => { + const code = [ + h('100010 11 11 1101 000000000000000000'), // if (v3===v3) { + h('100000 00 01 1111111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 1101')].call(ops, 0)).toEqual(1); + }); + it('Garbage in a tail should not affect if', () => { + const code = [ + h('100010 00 00 1101 111111111111111111'), // if (v0===v0) { + h('100000 00 01 1111111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 00 00 1101')].call(ops, 0)).toEqual(1); + }); + + describe('ifs 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + Operators.compile(); + }); + afterAll (() => Operators.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + + it('if(v3!==v3) should be false', () => { + const code = [ + h('100010 011 011 1110 0000000000000000'), // if (v3!==v3) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 011 011 1110')].call(ops, 0)).toEqual(3); + }); + it('if(v0+v1) should be true', () => { + const code = [ + h('100010 000 001 0000 0000000000000000'), // if (v0+v1) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 011 011 0000')].call(ops, 0)).toEqual(1); + }); + it('if(v0<=v0) should be false', () => { + const code = [ + h('100010 000 000 1111 0000000000000000'), // if (v0<=v0) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 000 000 1111')].call(ops, 0)).toEqual(1); + }); + it('Garbage in a tail should not affect if', () => { + const code = [ + h('100010 000 001 0000 1111111111111111'), // if (v0+v1) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 11111111111111111111111111') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 011 011 0000')].call(ops, 0)).toEqual(1); + }); + it('Test of one if and two closed brackets', () => { + const code = [ + h('100010 011 011 1110 0000000000000000'), // if (v3!==v3) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 011 011 1110')].call(ops, 0)).toEqual(3); + }); + it('Test of one if and two closed brackets 2', () => { + const code = [ + h('101000 00000000000000000000000000'), // } + h('100010 011 011 1110 0000000000000000'), // if (v3!==v3) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('101000')].call(ops, 0, code[0])).toEqual(1); + expect(ops.operators[h('100010 011 011 1110')].call(ops, 1)).toEqual(4); + }); + it('if(false) without closed bracket should go to the next line', () => { + const code = [ + h('100010 011 011 1110 0000000000000000'), // if (v3!==v3) { + h('100000 000 001 11111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 011 011 1110')].call(ops, 0)).toEqual(1); + }); + it('if(true) without closed bracket should go to the next line', () => { + const code = [ + h('100010 011 011 1101 0000000000000000'), // if (v3===v3) { + h('100000 000 001 11111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 011 011 1101')].call(ops, 0)).toEqual(1); + }); + it('Garbage in a tail should not affect if', () => { + const code = [ + h('100010 000 000 1101 1111111111111111'), // if (v0===v0) { + h('100000 000 001 11111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 000 000 1101')].call(ops, 0)).toEqual(1); + }); + }); + }); + + describe('loops 2bits per var', () => { + it('while() with false condition should go outside the closed bracket', () => { + const code = [ + h('100011 11 11 1110 000000000000000000'), // while (v3!==v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 11 11 1110')].call(ops, 0)).toEqual(3); + }); + it('while() with true condition should go to the next line', () => { + const code = [ + h('100011 11 11 1101 000000000000000000'), // while (v3===v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 11 11 1101')].call(ops, 0)).toEqual(1); + }); + it('while() with true at the beginning and false after', () => { + const code = [ + h('100011 00 01 1110 000000000000000000'), // while (v0!==v1) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 00 01 1110')].call(ops, 0)).toEqual(1); + expect(ops.operators[h('100000 00 01')].call(ops, 1)).toEqual(2); + expect(ops.operators[h('101000')].call(ops, 2, code[2], {}, code)).toEqual(0); + expect(ops.operators[h('100011 00 01 1110')].call(ops, 0)).toEqual(3); + }); + it('Garbage in a tail of command should not affect values', () => { + const code = [ + h('100011 11 11 1110 111111111111111111'), // while (v3!==v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 11111111111111111111111111') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 11 11 1110')].call(ops, 0)).toEqual(3); + }); + it('Garbage in a tail of command should not affect values 2', () => { + const code = [ + h('100011 11 11 1101 111111111111111111'), // while (v3===v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 11111111111111111111111111') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 11 11 1101')].call(ops, 0)).toEqual(1); + }); + + describe('loops 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + Operators.compile(); + }); + afterAll (() => Operators.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + + it('while() with false condition should go outside the closed bracket', () => { + const code = [ + h('100011 011 011 1110 0000000000000000'), // while (v3!==v3) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 011 011 1110')].call(ops, 0)).toEqual(3); + }); + it('while() with true condition should go to the next line', () => { + const code = [ + h('100011 011 011 1101 0000000000000000'), // while (v3===v3) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 011 011 1101')].call(ops, 0)).toEqual(1); + }); + it('while() with true at the beginning and false after', () => { + const code = [ + h('100011 000 001 1110 0000000000000000'), // while (v0!==v1) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 000 001 1110')].call(ops, 0)).toEqual(1); + expect(ops.operators[h('100000 000 001')].call(ops, 1)).toEqual(2); + expect(ops.operators[h('101000')].call(ops, 2, code[2], {}, code)).toEqual(0); + expect(ops.operators[h('100011 000 001 1110')].call(ops, 0)).toEqual(3); + }); + it('Garbage in a tail of command should not affect values', () => { + const code = [ + h('100011 011 011 1110 1111111111111111'), // while (v3!==v3) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 11111111111111111111111111') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 011 011 1110')].call(ops, 0)).toEqual(3); + }); + it('Garbage in a tail of command should not affect values 2', () => { + const code = [ + h('100011 011 011 1101 1111111111111111'), // while (v3===v3) { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 11111111111111111111111111') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100011 011 011 1101')].call(ops, 0)).toEqual(1); + }); + }); + }); + + describe('operators 2bits per var', () => { + it('Checks + operator', () => { + expect(ops.operators[h('100100 00 01 10 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([3,1,2,3]); + }); + it('Checks + operator', () => { + ops.vars[1] = ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 00 01 10 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([MAX_NUM,MAX_NUM,MAX_NUM,3]); + }); + it('Checks + operator', () => { + ops.vars[1] = ops.vars[2] = 0; + expect(ops.operators[h('100100 00 01 10 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([0,0,0,3]); + }); + it('Checks + operator', () => { + ops.vars[1] = ops.vars[2] = -1; + expect(ops.operators[h('100100 00 01 10 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([-2,-1,-1,3]); + }); + it('Checks - operator', () => { + expect(ops.operators[h('100100 00 01 10 0001')].call(ops, 0)).toEqual(1); // v0 = v1 - v2 + expect(ops.vars).toEqual([-1,1,2,3]); + }); + it('Checks - operator', () => { + ops.vars[1] = ops.vars[2] = -1; + expect(ops.operators[h('100100 00 01 10 0001')].call(ops, 0)).toEqual(1); // v0 = v1 - v2 + expect(ops.vars).toEqual([0,-1,-1,3]); + }); + it('Checks - operator', () => { + ops.vars[1] = -MAX_NUM; + ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 00 01 10 0001')].call(ops, 0)).toEqual(1); // v0 = v1 - v2 + expect(ops.vars).toEqual([-MAX_NUM,-MAX_NUM,MAX_NUM,3]); + }); + it('Checks * operator', () => { + expect(ops.operators[h('100100 00 01 10 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([2,1,2,3]); + }); + it('Checks * operator', () => { + ops.vars[1] = ops.vars[2] = -1; + expect(ops.operators[h('100100 00 01 10 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([1,-1,-1,3]); + }); + it('Checks * operator', () => { + ops.vars[1] = -1; + expect(ops.operators[h('100100 00 01 10 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([-2,-1,2,3]); + }); + it('Checks * operator', () => { + ops.vars[1] = ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 00 01 10 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([MAX_NUM,MAX_NUM,MAX_NUM,3]); + }); + it('Checks * operator', () => { + ops.vars[1] = MAX_NUM; + ops.vars[2] = -MAX_NUM; + expect(ops.operators[h('100100 00 01 10 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([MAX_NUM,MAX_NUM,-MAX_NUM,3]); + }); + it('Checks / operator', () => { + expect(ops.operators[h('100100 00 01 10 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([.5,1,2,3]); + }); + it('Checks / operator', () => { + ops.vars[1] = 1; + ops.vars[2] = -1; + expect(ops.operators[h('100100 00 01 10 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([-1,1,-1,3]); + }); + it('Checks / operator', () => { + ops.vars[1] = -1; + ops.vars[2] = -1; + expect(ops.operators[h('100100 00 01 10 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([1,-1,-1,3]); + }); + it('Checks / operator', () => { + ops.vars[1] = -MAX_NUM; + ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 00 01 10 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([-1,-MAX_NUM,MAX_NUM,3]); + }); + it('Checks / operator', () => { + expect(ops.operators[h('100100 01 10 00 0011')].call(ops, 0)).toEqual(1); // v1 = v2 / v0 + expect(ops.vars).toEqual([0,MAX_NUM,2,3]); + }); + it('Checks % operator', () => { + expect(ops.operators[h('100100 00 01 10 0100')].call(ops, 0)).toEqual(1); // v0 = v1 % v2 + expect(ops.vars).toEqual([1,1,2,3]); + }); + it('Checks % operator', () => { + ops.vars[2] = 0; + expect(ops.operators[h('100100 00 01 10 0100')].call(ops, 0)).toEqual(1); // v0 = v1 % v2 + expect(ops.vars).toEqual([0,1,0,3]); + }); + it('Checks & operator', () => { + expect(ops.operators[h('100100 00 01 10 0101')].call(ops, 0)).toEqual(1); // v0 = v1 & v2 + expect(ops.vars).toEqual([0,1,2,3]); + }); + it('Checks & operator', () => { + expect(ops.operators[h('100100 10 01 00 0101')].call(ops, 0)).toEqual(1); // v2 = v1 & v0 + expect(ops.vars).toEqual([0,1,0,3]); + }); + it('Checks & operator', () => { + expect(ops.operators[h('100100 10 01 01 0101')].call(ops, 0)).toEqual(1); // v2 = v1 & v1 + expect(ops.vars).toEqual([0,1,1,3]); + }); + it('Checks < operator', () => { + expect(ops.operators[h('100100 10 01 01 1011')].call(ops, 0)).toEqual(1); // v2 = v1 < v1 + expect(ops.vars).toEqual([0,1,0,3]); + }); + it('Checks < operator', () => { + expect(ops.operators[h('100100 00 01 10 1011')].call(ops, 0)).toEqual(1); // v0 = v1 < v2 + expect(ops.vars).toEqual([1,1,2,3]); + }); + it('Checks < operator', () => { + ops.vars[1] = -1; + ops.vars[2] = -2; + expect(ops.operators[h('100100 00 01 10 1011')].call(ops, 0)).toEqual(1); // v0 = v1 < v2 + expect(ops.vars).toEqual([0,-1,-2,3]); + }); + + describe('operators 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + Operators.compile(); + }); + afterAll (() => Operators.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + + it('Checks + operator', () => { + expect(ops.operators[h('100100 000 001 010 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([3,1,2,3,4,5,6,7]); + }); + it('Checks + operator', () => { + ops.vars[1] = ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 000 001 010 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([MAX_NUM, MAX_NUM, MAX_NUM, 3,4,5,6,7]); + }); + it('Checks + operator', () => { + ops.vars[1] = ops.vars[2] = 0; + expect(ops.operators[h('100100 000 001 010 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([0, 0, 0, 3,4,5,6,7]); + }); + it('Checks + operator', () => { + ops.vars[1] = ops.vars[2] = -1; + expect(ops.operators[h('100100 000 001 010 0000')].call(ops, 0)).toEqual(1); // v0 = v1 + v2 + expect(ops.vars).toEqual([-2, -1, -1, 3,4,5,6,7]); + }); + it('Checks - operator', () => { + expect(ops.operators[h('100100 000 001 010 0001')].call(ops, 0)).toEqual(1); // v0 = v1 - v2 + expect(ops.vars).toEqual([-1, 1, 2, 3,4,5,6,7]); + }); + it('Checks - operator', () => { + ops.vars[1] = ops.vars[2] = -1; + expect(ops.operators[h('100100 000 001 010 0001')].call(ops, 0)).toEqual(1); // v0 = v1 - v2 + expect(ops.vars).toEqual([0, -1, -1, 3,4,5,6,7]); + }); + it('Checks - operator', () => { + ops.vars[1] = -MAX_NUM; + ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 000 001 010 0001')].call(ops, 0)).toEqual(1); // v0 = v1 - v2 + expect(ops.vars).toEqual([-MAX_NUM, -MAX_NUM, MAX_NUM, 3,4,5,6,7]); + }); + it('Checks * operator', () => { + expect(ops.operators[h('100100 000 001 010 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([2, 1, 2, 3,4,5,6,7]); + }); + it('Checks * operator', () => { + ops.vars[1] = ops.vars[2] = -1; + expect(ops.operators[h('100100 000 001 010 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([1, -1, -1, 3,4,5,6,7]); + }); + it('Checks * operator', () => { + ops.vars[1] = -1; + expect(ops.operators[h('100100 000 001 010 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([-2, -1, 2, 3,4,5,6,7]); + }); + it('Checks * operator', () => { + ops.vars[1] = ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 000 001 010 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([MAX_NUM, MAX_NUM, MAX_NUM, 3,4,5,6,7]); + }); + it('Checks * operator', () => { + ops.vars[1] = MAX_NUM; + ops.vars[2] = -MAX_NUM; + expect(ops.operators[h('100100 000 001 010 0010')].call(ops, 0)).toEqual(1); // v0 = v1 * v2 + expect(ops.vars).toEqual([MAX_NUM, MAX_NUM, -MAX_NUM, 3,4,5,6,7]); + }); + it('Checks / operator', () => { + expect(ops.operators[h('100100 000 001 010 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([.5, 1, 2, 3,4,5,6,7]); + }); + it('Checks / operator', () => { + ops.vars[1] = 1; + ops.vars[2] = -1; + expect(ops.operators[h('100100 000 001 010 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([-1, 1, -1, 3,4,5,6,7]); + }); + it('Checks / operator', () => { + ops.vars[1] = -1; + ops.vars[2] = -1; + expect(ops.operators[h('100100 000 001 010 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([1, -1, -1, 3,4,5,6,7]); + }); + it('Checks / operator', () => { + ops.vars[1] = -MAX_NUM; + ops.vars[2] = MAX_NUM; + expect(ops.operators[h('100100 000 001 010 0011')].call(ops, 0)).toEqual(1); // v0 = v1 / v2 + expect(ops.vars).toEqual([-1, -MAX_NUM, MAX_NUM, 3,4,5,6,7]); + }); + it('Checks / operator', () => { + expect(ops.operators[h('100100 001 010 000 0011')].call(ops, 0)).toEqual(1); // v1 = v2 / v0 + expect(ops.vars).toEqual([0, MAX_NUM, 2, 3,4,5,6,7]); + }); + it('Checks % operator', () => { + expect(ops.operators[h('100100 000 001 010 0100')].call(ops, 0)).toEqual(1); // v0 = v1 % v2 + expect(ops.vars).toEqual([1, 1, 2, 3,4,5,6,7]); + }); + it('Checks % operator', () => { + ops.vars[2] = 0; + expect(ops.operators[h('100100 000 001 010 0100')].call(ops, 0)).toEqual(1); // v0 = v1 % v2 + expect(ops.vars).toEqual([0, 1, 0, 3,4,5,6,7]); + }); + it('Checks & operator', () => { + expect(ops.operators[h('100100 000 001 010 0101')].call(ops, 0)).toEqual(1); // v0 = v1 & v2 + expect(ops.vars).toEqual([0, 1, 2, 3,4,5,6,7]); + }); + it('Checks & operator', () => { + expect(ops.operators[h('100100 010 001 000 0101')].call(ops, 0)).toEqual(1); // v2 = v1 & v0 + expect(ops.vars).toEqual([0, 1, 0, 3,4,5,6,7]); + }); + it('Checks & operator', () => { + expect(ops.operators[h('100100 010 001 001 0101')].call(ops, 0)).toEqual(1); // v2 = v1 & v1 + expect(ops.vars).toEqual([0, 1, 1, 3,4,5,6,7]); + }); + it('Checks < operator', () => { + expect(ops.operators[h('100100 010 001 001 1011')].call(ops, 0)).toEqual(1); // v2 = v1 < v1 + expect(ops.vars).toEqual([0, 1, 0, 3,4,5,6,7]); + }); + it('Checks < operator', () => { + expect(ops.operators[h('100100 000 001 010 1011')].call(ops, 0)).toEqual(1); // v0 = v1 < v2 + expect(ops.vars).toEqual([1, 1, 2, 3,4,5,6,7]); + }); + it('Checks < operator', () => { + ops.vars[1] = -1; + ops.vars[2] = -2; + expect(ops.operators[h('100100 000 001 010 1011')].call(ops, 0)).toEqual(1); // v0 = v1 < v2 + expect(ops.vars).toEqual([0, -1, -2, 3,4,5,6,7]); + }); + }); + }); + + describe('function declaration 2bits per var', () => { + it('Func declaration should be skipped during run', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1() { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(3); + }); + it('Two func declaration should be skipped during run', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000'), // } + h('100101 00000001 000000000000000000'), // func 2() { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(3); + expect(ops.operators[h('100101')].call(ops, 3)).toEqual(6); + }); + it('func inside func should work', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('100101 00000001 000000000000000000'), // func 2 { + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 1)).toEqual(3); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(4); + }); + it('func without closed bracket should work', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('100000 00 01 1111111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(1); + }); + it('one line func should work', () => { + const code = [ + h('100101 00000001 000000000000000000') // func 1 { + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(1); + }); + it('func with two closed brackets should work', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(2); + }); + it('func with two closed brackets should work', () => { + const code = [ + h('101000 00000000000000000000000000'), // } + h('100101 00000001 000000000000000000'), // func 1 { + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 1)).toEqual(3); + }); + + describe('function declaration 3bits per var', () => { + let bpv; + let ops; + let vars; + let offs; + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + OConfig.codeBitsPerVar = 3; + Operators.compile(); + }); + afterAll (() => Operators.compile()); + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + + it('Func declaration should be skipped during run', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1() { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(3); + }); + it('Two func declaration should be skipped during run', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000'), // } + h('100101 00000001 000000000000000000'), // func 2() { + h('100000 000 001 11111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(3); + expect(ops.operators[h('100101')].call(ops, 3)).toEqual(6); + }); + it('func inside func should work', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('100101 00000001 000000000000000000'), // func 2 { + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 1)).toEqual(3); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(4); + }); + it('func without closed bracket should work', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('100000 000 001 11111111111111111111') // v0 = v1 + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(1); + }); + it('one line func should work', () => { + const code = [ + h('100101 00000001 000000000000000000') // func 1 { + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(1); + }); + it('func with two closed brackets should work', () => { + const code = [ + h('100101 00000001 000000000000000000'), // func 1 { + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(2); + }); + it('func with two closed brackets should work', () => { + const code = [ + h('101000 00000000000000000000000000'), // } + h('100101 00000001 000000000000000000'), // func 1 { + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 1)).toEqual(3); + }); + }); + }); + + describe('function call', () => { + it('Func call should work', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0() { + h('101000 00000000000000000000000000'), // } + h('100110 1 00000000 00000000000000000') // call 0() + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(2); + expect(ops.operators[h('100110')].call(ops, 2, code[2], {}, code)).toEqual(1); + expect(ops.operators[h('101000')].call(ops, 1, code[1], {}, code)).toEqual(3); + }); + it('Vars within function should not affect outside vars', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0() { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000'), // } + h('100110 1 00000000 00000000000000000') // call 0() + ]; + ops.updateIndexes(code); + expect(ops.vars).toEqual([0,1,2,3]); + expect(ops.operators[h('100110')].call(ops, 3, code[3], {}, code)).toEqual(1); + expect(ops.operators[h('100000 00 01')].call(ops, 1, code[1], {}, code)).toEqual(2); + expect(ops.vars).toEqual([1,1,2,3]); + expect(ops.operators[h('101000')].call(ops, 2, code[2], {}, code)).toEqual(4); + expect(ops.vars).toEqual([0,1,2,3]); + }); + it('func inside func should be callable', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0 { + h('100101 00000000000000000000000000'), // func 1 { + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000'), // } + h('100110 1 00000001 00000000000000000') // call 1() + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100110')].call(ops, 4, code[4], {}, code)).toEqual(2); + expect(ops.operators[h('101000')].call(ops, 2, code[2], {}, code)).toEqual(5); + }); + it('func should be callable through var', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0 { + h('100101 00000000000000000000000000'), // func 1 { + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000'), // } + h('100110 0 01 00000000000000000000000') // call v1() + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100110')].call(ops, 4, code[4], {}, code)).toEqual(2); + expect(ops.operators[h('101000')].call(ops, 2, code[2], {}, code)).toEqual(5); + }); + it('undefined func should not be callable through var', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0 { + h('100101 00000000000000000000000000'), // func 1 { + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000'), // } + h('100110 0 10 00000000000000000000000') // call v1() + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100110')].call(ops, 4, code[4], {}, code)).toEqual(5); + }); + }); + + describe('return', () => { + it('One return should create infinite loop', () => { + const code = [ + h('100111 00000000000000000000000000') // return + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100111')].call(ops, 0)).toEqual(0); + }); + it('return outside the func should jump to zero line', () => { + const code = [ + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('100111 11111111111111111111111111') // return + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100111')].call(ops, 1)).toEqual(0); + }); + it('return inside func should jump outside of it', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0() { + h('100111 00000000000000000000000000'), // return + h('101000 00000000000000000000000000'), // } + h('100110 1 00000000 00000000000000000') // call 0() + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(3); + expect(ops.operators[h('100110')].call(ops, 3, code[3], {}, code)).toEqual(1); + expect(ops.operators[h('100111')].call(ops, 1, code[1], {}, code)).toEqual(4); + }); + it('func inside func should be callable', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0 { + h('100101 00000000000000000000000000'), // func 1 { + h('100111 00000000000000000000000000'), // return + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000'), // } + h('100110 1 00000001 00000000000000000') // call 1() + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100110')].call(ops, 5, code[5], {}, code)).toEqual(2); + expect(ops.operators[h('100111')].call(ops, 2, code[2], {}, code)).toEqual(6); + }); + it('Two return inside func should jump outside of it', () => { + const code = [ + h('100101 00000000000000000000000000'), // func 0() { + h('100111 00000000000000000000000000'), // return + h('100111 00000000000000000000000000'), // return + h('101000 00000000000000000000000000'), // } + h('100110 1 00000000 00000000000000000') // call 0() + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100101')].call(ops, 0)).toEqual(4); + expect(ops.operators[h('100110')].call(ops, 4, code[4], {}, code)).toEqual(1); + expect(ops.operators[h('100111')].call(ops, 1, code[1], {}, code)).toEqual(5); + }); + }); + + describe('bracket', () => { + it('Script of one bracket should work', () => { + const code = [ + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('101000')].call(ops, 0)).toEqual(1); + }); + it('Script of two brackets should work', () => { + const code = [ + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('101000')].call(ops, 0)).toEqual(1); + expect(ops.operators[h('101000')].call(ops, 1)).toEqual(2); + }); + it('if(v3!==v3) should be false', () => { + const code = [ + h('100010 11 11 1110 000000000000000000'), // if (v3!==v3) { + h('100000 00 01 1111111111111111111111'), // v0 = v1 + h('101000 00000000000000000000000000'), // } + h('101000 00000000000000000000000000') // } + ]; + ops.updateIndexes(code); + expect(ops.operators[h('100010 11 11 1110')].call(ops, 0)).toEqual(3); + expect(ops.operators[h('101000')].call(ops, 3)).toEqual(4); + }); + }); + + describe('toMem 2bits per var', () => { + const memBits = OConfig.orgMemBits; + beforeAll(() => { + OConfig.orgMemBits = 2; + Operators.compile(); + }); + afterAll (() => { + OConfig.orgMemBits = memBits; + Operators.compile(); + }); + + it('toMem operator should work', () => { + const code = [ + h('101001 00 01 0000000000000000000000') // toMem(v0, v1) + ]; + const org = {mem: [0,1,2,3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101001 00 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([1,1,2,3]); + }); + it('toMem operator should work 2', () => { + const code = [ + h('101001 00 00 0000000000000000000000') // toMem(v0, v0) + ]; + const org = {mem: [0,1,2,3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101001 00 00')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,3]); + }); + it('Garbage in a tail should not affect toMem operator', () => { + const code = [ + h('101001 00 00 1111111111111111111111') // toMem(v0, v0) + ]; + const org = {mem: [0,1,2,3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101001 00 00')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,3]); + }); + it('toMem operator should have offset limit', () => { + const code = [ + h('101001 00 01 0000000000000000000000') // toMem(v0, v1) + ]; + const org = {mem: [0,1,2,3]}; + ops.vars[0] = 100; + ops.updateIndexes(code); + expect(ops.operators[h('101001 00 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,1]); + }); + it('toMem operator should have offset limit (minus value)', () => { + const code = [ + h('101001 00 01 0000000000000000000000') // toMem(v0, v1) + ]; + const org = {mem: [0,1,2,3]}; + ops.vars[0] = -10; + ops.vars[1] = -1; + ops.updateIndexes(code); + expect(ops.operators[h('101001 00 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,-1]); + }); + + describe('toMem 3bits per var', () => { + let memBits; + let bpv; + let ops; + let vars; + let offs; + + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + memBits = OConfig.orgMemBits; + OConfig.codeBitsPerVar = 3; + OConfig.orgMemBits = 2; + Operators.compile(); + }); + afterAll (() => { + OConfig.orgMemBits = memBits; + Operators.compile(); + }); + + it('toMem operator should work', () => { + const code = [ + h('101001 000 001 00000000000000000000') // toMem(v0, v1) + ]; + const org = {mem: [0,1,2,3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101001 000 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([1,1,2,3]); + }); + it('toMem operator should work 2', () => { + const code = [ + h('101001 000 000 00000000000000000000') // toMem(v0, v0) + ]; + const org = {mem: [0,1,2,3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101001 000 000')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,3]); + }); + it('Garbage in a tail should not affect toMem operator', () => { + const code = [ + h('101001 000 000 11111111111111111111') // toMem(v0, v0) + ]; + const org = {mem: [0,1,2,3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101001 000 000')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,3]); + }); + it('toMem operator should have offset limit', () => { + const code = [ + h('101001 000 001 00000000000000000000') // toMem(v0, v1) + ]; + const org = {mem: [0,1,2,3]}; + ops.vars[0] = 100; + ops.updateIndexes(code); + expect(ops.operators[h('101001 000 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,1]); + }); + it('toMem operator should have offset limit (minus value)', () => { + const code = [ + h('101001 000 001 00000000000000000000') // toMem(v0, v1) + ]; + const org = {mem: [0,1,2,3]}; + ops.vars[0] = -10; + ops.vars[1] = -1; + ops.updateIndexes(code); + expect(ops.operators[h('101001 000 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(org.mem).toEqual([0,1,2,-1]); + }); + }); + }); + + describe('fromMem 2bits per var', () => { + const memBits = OConfig.orgMemBits; + beforeAll(() => { + OConfig.orgMemBits = 2; + Operators.compile(); + }); + afterAll (() => { + OConfig.orgMemBits = memBits; + Operators.compile(); + }); + + it('fromMem operator should work', () => { + const code = [ + h('101010 00 01 0000000000000000000000') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101010 00 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3]); + }); + it('fromMem operator should work', () => { + const code = [ + h('101010 01 01 0000000000000000000000') // v1 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101010 01 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 3]); + }); + it('fromMem operator should have offset limit', () => { + const code = [ + h('101010 00 01 0000000000000000000000') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.vars[1] = 100; + ops.updateIndexes(code); + expect(ops.operators[h('101010 00 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([3, 100, 2, 3]); + }); + it('fromMem operator should have offset limit 2', () => { + const code = [ + h('101010 00 01 0000000000000000000000') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.vars[1] = -100; + ops.updateIndexes(code); + expect(ops.operators[h('101010 00 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([3, -100, 2, 3]); + }); + it('Garbage in a tail should not affect fromMem operator', () => { + const code = [ + h('101010 00 01 1111111111111111111111') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101010 00 01')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3]); + }); + + describe('fromMem 3bits per var', () => { + let memBits; + let bpv; + let ops; + let vars; + let offs; + + beforeEach(() => { + vars = [0,1,2,3,4,5,6,7]; + offs = new Array(10); + ops = new Operators(offs, vars); + }); + afterEach (() => { + ops.destroy(); + ops = null; + offs = null; + vars = null; + OConfig.codeBitsPerVar = bpv; + }); + beforeAll (() => { + bpv = OConfig.codeBitsPerVar; + memBits = OConfig.orgMemBits; + OConfig.codeBitsPerVar = 3; + OConfig.orgMemBits = 2; + Operators.compile(); + }); + afterAll (() => { + OConfig.orgMemBits = memBits; + Operators.compile(); + }); + + it('fromMem operator should work', () => { + const code = [ + h('101010 000 001 00000000000000000000') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101010 000 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3, 4, 5, 6, 7]); + }); + it('fromMem operator should work', () => { + const code = [ + h('101010 001 001 00000000000000000000') // v1 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101010 001 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([0, 1, 2, 3, 4, 5, 6, 7]); + }); + it('fromMem operator should have offset limit', () => { + const code = [ + h('101010 000 001 00000000000000000000') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.vars[1] = 100; + ops.updateIndexes(code); + expect(ops.operators[h('101010 000 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([3, 100, 2, 3, 4, 5, 6, 7]); + }); + it('fromMem operator should have offset limit 2', () => { + const code = [ + h('101010 000 001 00000000000000000000') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.vars[1] = -100; + ops.updateIndexes(code); + expect(ops.operators[h('101010 000 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([3, -100, 2, 3, 4, 5, 6, 7]); + }); + it('Garbage in a tail should not affect fromMem operator', () => { + const code = [ + h('101010 000 001 11111111111111111111') // v0 = fromMem(v1) + ]; + const org = {mem: [0, 1, 2, 3]}; + ops.updateIndexes(code); + expect(ops.operators[h('101010 000 001')].call(ops, 0, code[0], org)).toEqual(1); + expect(ops.vars).toEqual([1, 1, 2, 3, 4, 5, 6, 7]); + }); + }); + }); +}); \ No newline at end of file diff --git a/client/src/vm/VM.js b/client/src/vm/VM.js index df075bd..73da490 100644 --- a/client/src/vm/VM.js +++ b/client/src/vm/VM.js @@ -12,6 +12,7 @@ const Observer = require('./../../../common/src/Observer'); const OConfig = require('./../manager/plugins/organisms/Config'); const EVENTS = require('./../../src/share/Events').EVENTS; const EVENT_AMOUNT = require('./../../src/share/Events').EVENT_AMOUNT; +const Operators = require('./Operators'); const Num = require('./Num'); /** * {Number} Maximum stack size, which may be used for recursion or function parameters @@ -45,7 +46,7 @@ class VM extends Observer { * to return if first line appears. second - line number, where ends * closing block '}' of block operator (e.g. for, if,...). */ - this._offsets = [this._code.length]; + this._offsets = parent ? parent.offsets.slice() : new Array(OConfig.codeMaxSize); /** * {Function} Class, which implement all supported operators */ @@ -56,6 +57,7 @@ class VM extends Observer { get code() {return this._code} get size() {return this._code.length} get operators() {return this._operators} + get offsets() {return this._offsets} get vars() {return this._vars} get line() {return this._line} @@ -88,46 +90,37 @@ class VM extends Observer { * @return {Number} Amount of run lines */ run(org) { - const code = this._code; - const lines = code.length; - if (lines < 1) {return 0} - const ops = this._ops; - const offs = this._offsets; - const period = OConfig.codeYieldPeriod; - const OFFS = Num.VAR_BITS_OFFS; - const WEIGHTS = this._weights; - let len = period; - let line = this._line; - let operator; + const code = this._code; + const codeLen = code.length; + if (codeLen < 1) {return 0} + const opHandlers = this._ops; + const operators = this._operators; + const yieldPeriod = OConfig.codeYieldPeriod; + const opOffs = Num.VAR_BITS_OFFS; + const opMask = Num.OPERATOR_MASK_OFF; + const weights = this._weights; + const lens = Operators.LENS; + const sizeEnergy = OConfig.orgSizeAffectsEnergy; + let linesRun = yieldPeriod; + let line = this._line; - while (len > 0 && org.energy > 0) { - operator = code[line] >>> OFFS; - line = ops[operator](code[line], line, org, lines); + while (linesRun > 0 && org.energy > 0) { + const num = code[line]; + const op = (num & opMask) >>> opOffs; + line = opHandlers[num >>> lens[op]].call(operators, line, num, org, code); // - // This is very important peace of logic. As big the organism is - // as more energy he spends + // Every operator has it's own weight // - org.energy -= WEIGHTS[operator]; - // - // We found closing bracket '}' of some loop and have to return - // to the beginning of operator (e.g.: for) - // - while (offs.length > 1 && line === offs[offs.length - 1]) { - offs.pop(); - line = offs.pop(); - } + org.vm && (org.energy -= (weights[op] * (sizeEnergy ? org.vm.size : 1))); // // We reach the end of the script and have to run it from the beginning // - if (line >= lines && org.energy > 0) { - line = 0; - this._operators.offsets = this._offsets = [code.length]; - } - len--; + line >= codeLen && (line = 0); + linesRun--; } this._line = line; - return period - len; + return yieldPeriod - linesRun; } destroy() { @@ -262,7 +255,7 @@ class VM extends Observer { _reset() { this.fire(EVENTS.RESET_CODE); this._line = 0; - this._operators.offsets = (this._offsets = [this._code.length]); + this._operators.updateIndexes(this._code); } /** diff --git a/common/src/Config.js b/common/src/Config.js index 666857a..4c6d318 100644 --- a/common/src/Config.js +++ b/common/src/Config.js @@ -5,8 +5,9 @@ * * @author flatline */ -const _get = require('lodash/get'); -const _set = require('lodash/set'); +const _get = require('lodash/get'); +const _set = require('lodash/set'); +const EVENTS = require('./../../client/src/share/Events').EVENTS; class Config { static init(cfg) { @@ -22,6 +23,7 @@ class Config { */ static set(key, val) { _set(this._cfg, key, val); + man.fire(EVENTS.CONFIG_CHANGE, key, val); return this; } diff --git a/common/src/Configurable.js b/common/src/Configurable.js index dd516c9..0aa7c14 100644 --- a/common/src/Configurable.js +++ b/common/src/Configurable.js @@ -64,7 +64,7 @@ class Configurable { const config = cfg.Config; const cls = Helper.loverCase(this.constructor.name); - if (typeof config[cls] !== 'undefined') { + if (config[cls] !== undefined) { throw `Looks like there are two plugins with the same name try to set their configuration. Name: ${cls}`; } @@ -83,7 +83,7 @@ class Configurable { const api = this._parent.api; const cls = Helper.loverCase(this.constructor.name); - if (typeof api[cls] !== 'undefined') { + if (api[cls] !== undefined) { throw `Looks like there are two plugins with the same name try to set their configuration. Name: ${cls}`; } diff --git a/common/src/Directions.js b/common/src/Directions.js index 07604e9..d752024 100644 --- a/common/src/Directions.js +++ b/common/src/Directions.js @@ -10,6 +10,16 @@ const DIR = { LEFT : 3, NO : 4 }; +/** + * {Array} Array of flipped directions. Is used for connecting with nearest + * servers: left -> right, up -> down, right -> left, down -> up + */ +const FLIP_DIR = [ + DIR.DOWN, + DIR.LEFT, + DIR.UP, + DIR.RIGHT +]; const NAMES = { 0: 'Up', @@ -19,9 +29,20 @@ const NAMES = { 4: 'No' }; /** - * {Array} Array of flipped directions. Is used for connecting with nearest - * servers: left -> right, up -> down, right -> left, down -> up + * {Array} X, Y Offsets values for directions. Are used to change current + * coordinates of some object (organism or world object) */ -const FLIP_DIR = [DIR.DOWN, DIR.LEFT, DIR.UP, DIR.RIGHT]; +const OFFSX = [ + 0, // Up + 1, // Right + 0, // Down + -1 // Left +]; +const OFFSY = [ + -1, // Up + 0, // Right + 1, // Down + 0 // Left +]; -module.exports = {DIR, FLIP_DIR, NAMES}; \ No newline at end of file +module.exports = {DIR, FLIP_DIR, NAMES, OFFSX, OFFSY}; \ No newline at end of file diff --git a/common/src/FastArray.js b/common/src/FastArray.js index 24b5122..4628136 100644 --- a/common/src/FastArray.js +++ b/common/src/FastArray.js @@ -1,14 +1,18 @@ /** - * Implementation of fast array. First assumption of this class is in fixed array - * size. Second that get() method will be called must more times, then set() or + * Implementation of fast array. Meaning of this class is in fast access to custom + * element of array, ability to add/remove elements. First, this class uses fixed + * array size. Second, get() method will be called must of the time, then set() or * del() or resize(). Resize is possible, but should be rare to keep it fast. Is - * used for storing organisms population. This class doesn't check size overflow - * due performance issue. Removing element means setting 0 to specified index. - * This class should not be used for storing numbers! + * used for storing organisms population. Removing element means setting null to + * specified index. * * @author flatline */ class FastArray { + /** + * Creates fast array instance. Size is maximum amount of elements you may access to + * @param {Number} size Max elements in a array + */ constructor(size) { /** * {Array} Source container for custom objects @@ -32,20 +36,22 @@ class FastArray { for (let i = 0; i < size; i++) { this._freeIndexes[i] = i; - this._arr[i] = 0; + this._arr[i] = null; } } destroy() { this._arr = null; this._freeIndexes = null; - this._size = 0; + this._size = null; + this._index = -1; } /** * Analog of Array.length - * @returns {Number} Amount of not empty elements in FastArray. - * Not all cells in an array may be filled by values. + * @returns {Number} Amount of not empty elements in FastArray. + * Not all cells in an array may be filled by values. 0 or less + * then zero means no items in an array. */ get length() {return this._size - this._index - 1} @@ -56,8 +62,8 @@ class FastArray { get size() {return this._size} /** - * Returns next free index in FastArray - * @returns {Number} + * Returns next free index in FastArray or undefined if there is no free index + * @returns {Number|undefined} */ get freeIndex() { return this._freeIndexes[this._index]; @@ -66,47 +72,49 @@ class FastArray { /** * Sets value to FastArray. You can't set value index due to * optimization reason. Only a value - * @param {*} v Any value except number + * @param {*|false} v Any value or false if value hasn't added */ - set(v) {this._arr[this._freeIndexes[this._index--]] = v} + add(v) {this._index > -1 && (this._arr[this._freeIndexes[this._index--]] = v)} /** * Returns a value by index * @param {Number} i Value index - * @returns {*} + * @returns {null|undefined|*} null - if cell is empty, undefined - if index out of bounds, * - value */ get(i) {return this._arr[i]} /** - * Removes a value by index + * Removes(sets it to null) a value by index. * @param {Number} i Value index */ del(i) { - if (this._arr !== 0) - this._arr[i] = 0; - this._freeIndexes[++this._index] = i; + if (this._arr[i] !== null) { + this._arr[i] = null; + this._freeIndexes[++this._index] = i; + } } /** * Returns last added value by set() method - * @returns {*} Value + * @returns {*|undefined} Value or undefined if there is no value */ - lastAdded() { + added() { return this._arr[this._freeIndexes[this._index + 1]]; } /** - * Resizes an array. Values will not be removed during resize. + * Resize an array. Values will not be removed during resize. * This method is very slow and should be called not often. * @param {Number} size New array size */ resize(size) { - const indexes = this._freeIndexes; - const arr = this._arr; - this._index = -1; - arr.length = indexes.length = (this._size = size); - for (let i = 0; i < size; i++) { - (arr[i] === 0) && (indexes[++this._index] = i); + if (size <= 0) {return} + const oldArr = this._arr.slice(); + const oldSize = Math.min(this._size, size); + + this.constructor(size); + for (let i = 0; i < oldSize; i++) { + oldArr[i] !== null && this.add(oldArr[i]); } } } diff --git a/common/src/FastArraySpec.js b/common/src/FastArraySpec.js new file mode 100644 index 0000000..f4e9f9b --- /dev/null +++ b/common/src/FastArraySpec.js @@ -0,0 +1,132 @@ +describe('common/src/FastArray', () => { + const FastArray = require('./FastArray'); + const size = 10; + let fa; + + beforeEach(() => fa = new FastArray(size)); + afterEach (() => {fa.destroy(); fa = null}); + + it('Checking creation', () => { + expect(fa.size).toEqual(size); + expect(fa.length).toEqual(0); + expect(fa.freeIndex).toEqual(size - 1); + expect(fa.added()).toEqual(undefined); + }); + + it('Checking destroy', () => { + const fSize = 3; + const fa1 = new FastArray(fSize); + expect(fa1.size).toEqual(fSize); + fa1.destroy(); + expect(fa1.length <= 0).toBe(true); + expect(fa1.size).toEqual(null); + fa1.add({}); + expect(fa1.length <= 0).toBe(true); + }); + + it('Checking length getter', () => { + fa.add({}); + expect(fa.length).toEqual(1); + fa.get(size - 1); + expect(fa.length).toEqual(1); + fa.del(size - 1); + expect(fa.length).toEqual(0); + fa.resize(size * 2); + expect(fa.length).toEqual(0); + fa.add({}); + expect(fa.length).toEqual(1); + fa.resize(size); + expect(fa.length).toEqual(0); + fa.add({}); + fa.add({}); + expect(fa.length).toEqual(2); + }); + + it('Checking size getter', () => { + expect(fa.size).toEqual(size); + fa.add({}); + expect(fa.size).toEqual(size); + fa.del(size - 1); + expect(fa.size).toEqual(size); + + for (let i = 0; i < size; i++) {fa.add({})} + expect(fa.size).toEqual(size); + fa.add([]); + expect(fa.size).toEqual(size); + + fa.resize(size - 1); + expect(fa.size).toEqual(size - 1); + }); + + it('Checking freeIndex getter', () => { + fa.add({}); + expect(fa.freeIndex).toEqual(size - 2); + fa.del(size - 1); + expect(fa.freeIndex).toEqual(size - 1); + for (let i = 0; i < size; i++) {fa.add({})} + expect(fa.freeIndex).toEqual(undefined); + fa.del(size - 1); + expect(fa.freeIndex).toEqual(size - 1); + }); + + it('Checking add() method', () => { + const obj = {}; + fa.add(obj); + expect(fa.length).toEqual(1); + expect(fa.added()).toEqual(obj); + fa.del(size - 1); + expect(fa.get(size - 1)).toEqual(null); + + for (let i = 0; i < size; i++) {fa.add({})} + expect(fa.length).toEqual(size); + }); + + it('Checking get() method', () => { + const obj = {}; + expect(fa.get(size - 1)).toEqual(null); + expect(fa.get(size - 1)).toEqual(null); + fa.add(obj); + expect(fa.get(size - 1)).toEqual(obj); + expect(fa.get(size - 1)).toEqual(obj); + fa.del(size - 1); + expect(fa.get(size - 1)).toEqual(null); + fa.add(obj); + expect(fa.get(size - 1)).toEqual(obj); + fa.resize(size - 1); + expect(fa.get(size - 1)).toEqual(undefined); + }); + + it('Checking del() method', () => { + const obj = {}; + fa.add(obj); + expect(fa.added()).toEqual(obj); + fa.del(size - 1); + expect(fa.added()).toEqual(undefined); + fa.del(size - 1); + expect(fa.added()).toEqual(undefined); + fa.del(size); + expect(fa.added()).toEqual(undefined); + }); + + it('Checking added() method', () => { + const obj = {}; + expect(fa.added()).toEqual(undefined); + fa.add(obj); + expect(fa.added()).toEqual(obj); + expect(fa.added()).toEqual(obj); + fa.del(size - 1); + expect(fa.added()).toEqual(undefined); + fa.add(obj); + expect(fa.added()).toEqual(obj); + fa.resize(size + 1); + expect(fa.added()).toEqual(obj); + }); + + it('Checking resize() method', () => { + const obj = {}; + fa.add(obj); + expect(fa.added()).toEqual(obj); + fa.resize(size + 1); + expect(fa.added()).toEqual(obj); + }); +}); \ No newline at end of file diff --git a/common/src/Helper.js b/common/src/Helper.js index 21e8d2d..aeee238 100644 --- a/common/src/Helper.js +++ b/common/src/Helper.js @@ -8,6 +8,31 @@ const Config = require('./../../client/src/share/Config').Config; const DIR = require('./Directions').DIR; class Helper { + /** + * Returns color by index. Index may be increased without limit + * @param {Number} index Color index. Starts from 0 till 10000 + * @returns {Number} RGB value + */ + static getColor(index) { + // + // Maximum possible colors for this value is MAX_COLORS + // + const frequency = 0.0005; + + const r = Math.sin(frequency * index ) * 127 + 128; + const g = Math.sin(frequency * index + 2) * 127 + 128; + const b = Math.sin(frequency * index + 4) * 127 + 128; + + return r << 16 | g << 8 | b; + } + /** + * Makes synchronous delay + * @param {Number} ms Amount of milliseconds to delay + */ + static delay(ms) { + const ts = Date.now(); + while (Date.now() - ts < ms) {} + } /** * Calculates unique id for world's coordinates. For the same x,y * id will be the same. @@ -36,7 +61,7 @@ class Helper { // reference to fn.fn and this code crashes on line `fn.fn.apply(obj, args)` // const oldFn = fn.fn = obj[fnName]; - if (typeof oldFn === 'undefined') {throw `Helper.override: Parent object doesn't contain method '${fnName}'`} + if (oldFn === undefined) {throw `Helper.override: Parent object doesn't contain method '${fnName}'`} if (!hard) { obj[fnName] = (...args) => { const ret = fn(...args); @@ -209,7 +234,7 @@ class Helper { const xMap = {0: Config.worldWidth - 1, [Config.worldWidth - 1]: 0}; const yMap = {0: Config.worldHeight - 1, [Config.worldHeight - 1]: 0}; - return [(dir === DIR.LEFT || dir === DIR.RIGHT) && typeof(xMap[x]) !== 'undefined' ? xMap[x] : x, (dir === DIR.UP || dir === DIR.DOWN) && typeof(yMap[y]) !== 'undefined' ? yMap[y] : y]; + return [(dir === DIR.LEFT || dir === DIR.RIGHT) && xMap[x] !== undefined ? xMap[x] : x, (dir === DIR.UP || dir === DIR.DOWN) && yMap[y] !== undefined ? yMap[y] : y]; } /** @@ -242,6 +267,27 @@ class Helper { return typeof v === 'function'; } + + /** + * Converts string BIN number representation into number. Removes spaces. + * @param {String} s BIN string. e.g.: 'aa bb cc' -> 0xaabbcc + * @param {Number} width Amount of digits in binary number + * @returns {Number} + */ + static toHexNum(s, width = 0) { + return parseInt(s.split(' ').join('').padStart(width, '0'), 2) + } + + /** + * Converts number to binary string + * @param {Number} n Number to convert + * @param {Number} width Amount of digits in binary number + * @return {String} Binary string + */ + static toBinStr(n, width = 0) { + return n.toString(2).padStart(width, '0'); + } + /** * Generates unique numeric ids * @returns {Number} diff --git a/common/src/Observer.js b/common/src/Observer.js index 6e525a0..15c11fc 100644 --- a/common/src/Observer.js +++ b/common/src/Observer.js @@ -28,7 +28,7 @@ class Observer { on(event, handler) { const eventObj = this._handlers[event]; - if (typeof(eventObj) === 'undefined') {return false} + if (eventObj === undefined) {return false} eventObj[eventObj.amount++] = handler; return true; @@ -53,8 +53,9 @@ class Observer { /** * This method is a most frequently called one. So we have to * optimize it as much as possible - * @param {Number} event Event number. Not string + * @param {Number} event Event number * @param {*} args List of arguments + * @param args */ fire(event, ...args) { const handlers = this._handlers[event] || {}; diff --git a/common/src/plugins/Request.js b/common/src/plugins/Request.js index 949aabb..f5c0433 100644 --- a/common/src/plugins/Request.js +++ b/common/src/plugins/Request.js @@ -111,7 +111,7 @@ class Request { * @param {String} error Error message */ _onSendErr(error) { - if (typeof error !== 'undefined') { + if (error !== undefined) { Console.error(`Request.send() error: ${error}`); } } diff --git a/package.json b/package.json index 5c98a7e..5631b07 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,11 @@ }, "dependencies": { "babel-cli": "^6.24.1", - "babel-preset-env": "^1.6.0", - "jasmine": "^2.6.0", + "babel-preset-env": "^1.7.0", + "jasmine": "^2.99.0", "js-beautify": "^1.6.14", - "panzoom": "^4.1.0", - "webpack": "^3.9.1", + "panzoom": "^4.5.0", + "webpack": "^3.12.0", "ws": "^3.3.2" }, "scripts": { @@ -41,7 +41,7 @@ "homepage": "https://github.com/tmptrash/jevo.js#readme", "devDependencies": { "babel-register": "latest", - "clean-webpack-plugin": "^0.1.17", + "clean-webpack-plugin": "^0.1.19", "ignore-loader": "^0.1.2" } } diff --git a/tests/jasmine.json b/tests/jasmine.json index dead352..2acbb88 100644 --- a/tests/jasmine.json +++ b/tests/jasmine.json @@ -1,9 +1,13 @@ { "spec_dir": "./", "spec_files": [ - "client/src/**/*[sS]pec.js", - "common/src/**/*[sS]pec.js", - "server/src/**/*[sS]pec.js" + "1client/src/**/*[sS]pec.js", + "1common/src/**/*[sS]pec.js", + "1server/src/**/*[sS]pec.js", + "1common/src/FastArraySpec.js", + "1client/src/vm/OperatorsSpec.js", + "1client/src/manager/plugins/organisms/dos/OperatorsSpec.js", + "client/src/vm/Code2JSSpec.js" ], "stopSpecOnExpectationFailure": false, "random": false