From 32762e6ca8b932d3bbabb24136a6c3c134a4bb6d Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sun, 24 May 2020 15:54:41 -0700 Subject: [PATCH] refactor fieldStream, handles oscillators --- src/components/Cell.js | 3 + src/components/GameField.js | 47 ++++++++--- src/test/GameField.test.js | 160 +++++++++++++++++++++++++++++++----- 3 files changed, 180 insertions(+), 30 deletions(-) diff --git a/src/components/Cell.js b/src/components/Cell.js index af60f89..debd7cf 100644 --- a/src/components/Cell.js +++ b/src/components/Cell.js @@ -40,6 +40,9 @@ class CellStream extends Stream { addLiveNeighbor() { this.head.addLiveNeighbor(); } + setLiving() { + this.head.setLiving(); + } } const cellStream = (living = false, liveNeighbors = 0) => { diff --git a/src/components/GameField.js b/src/components/GameField.js index 1e3133f..59f3d13 100644 --- a/src/components/GameField.js +++ b/src/components/GameField.js @@ -12,9 +12,10 @@ class GameField { } }) ); - for (let key in fieldMap) { - this.map[key] = cellStream(true, 0); - } + Object.entries(fieldMap).forEach( + ([key, [live, neighbors]]) => + (this.map[key] = cellStream(live, neighbors)) + ); // instead of implementing multiple GameFields, clear irrelevant keys and expand Game Field as needed // discrete Field expansion should only happen in View (to keep view fields centered) } @@ -35,19 +36,43 @@ class FieldStream extends Stream { } } +const seedMap = (map, [key, seed]) => { + map[key] = seed; + return map; +}; +const isLiving = ([key, cell]) => cell.living === true; +const incrementLiveNeighbors = (field) => ([key]) => + getNeighbors(key).forEach((neighbor) => field.addLiveNeighbor(neighbor)); +const makeSeed = ([key, cell]) => [key, [cell.living, cell.liveNeighbors]]; +const makeSeedNextGen = ([key, cell]) => { + cell.setLiving(); + return [key, [cell.living, 0]]; +}; + const fieldStream = ({ fieldArray, fieldMap }) => { return new FieldStream(new GameField({ fieldArray, fieldMap }), function () { // calculate liveNeighbors for all cells on first next call - for (const key in this.map) { - getNeighbors(key).forEach((neighbor) => this.addLiveNeighbor(neighbor)); - } - this.tail = function () { - // call .next on all Cells on second next call - }; - return this; + Object.entries(this.map) + .filter(isLiving) + .forEach(incrementLiveNeighbors(this)); + // generate seed for next Stream with liveNeighbors + const mapWithLiveNeighbors = Object.entries(this.map) + .map(makeSeed) + .reduce(seedMap, {}); + // return next stream + return new FieldStream( + new GameField({ fieldMap: mapWithLiveNeighbors }), + function () { + // determine living cells for next generation + const nextGeneration = Object.entries(this.map) + .map(makeSeedNextGen) + .reduce(seedMap, {}); + // seed next Stream + return fieldStream({ fieldMap: nextGeneration }); + } + ); }); }; -// as a stream -> fieldStream => Stream(GameField, () => Stream(fieldStream.computeNeighbors(), () => Stream(fieldStream.setLiving())) // instantiate table (orientation of major and minor axis dependent on viewport) // const gameFields = new Array(1).fill(new GameField({})); diff --git a/src/test/GameField.test.js b/src/test/GameField.test.js index c60ef44..8bec979 100644 --- a/src/test/GameField.test.js +++ b/src/test/GameField.test.js @@ -7,9 +7,9 @@ describe("Game Field seeds living Cells with array", () => { ]; const fieldMap = { - "0,0": true, - "0,2": true, - "1,1": true, + "0,0": [true, 0], + "0,2": [true, 0], + "1,1": [true, 0], }; const gameArraySeed = new GameField({ fieldArray }); const gameMapSeed = new GameField({ fieldMap }); @@ -85,13 +85,13 @@ describe("fieldStream.next calculates liveNeighbors", () => { }); const fieldMap = { - "0,1": true, - "0,2": true, - "1,0": true, - "1,1": true, - "1,3": true, - "2,1": true, - "2,2": true, + "0,1": [true, 0], + "0,2": [true, 0], + "1,0": [true, 0], + "1,1": [true, 0], + "1,3": [true, 0], + "2,1": [true, 0], + "2,2": [true, 0], }; const testStream2 = fieldStream({ fieldMap }); [ @@ -128,27 +128,149 @@ describe("fieldStream.next calculates liveNeighbors", () => { }); }); -describe.skip("fieldStream.next tests still lifes", () => { +describe("fieldStream.next tests still lifes", () => { const blockArray = [ [1, 1], [1, 1], ]; - streamBlock = fieldStream({ fieldArray: blockArray }); - ["0,0", "0,1", "1,0", "1,1"].forEach((key) => {}); + const streamBlock = fieldStream({ fieldArray: blockArray }); + [ + ["-1,-1", false], + ["-1,0", false], + ["-1,1", false], + ["-1,2", false], + ["0,-1", false], + ["0,0", true], + ["0,1", true], + ["0,2", false], + ["1,-1", false], + ["1,0", true], + ["1,1", true], + ["1,2", false], + ["2,-1", false], + ["2,0", false], + ["2,1", false], + ["2,2", false], + ].forEach(([key, live]) => { + test(`after one generation of Block, ${key} alive: ${live}`, () => { + expect(streamBlock.next.next.map[key].living).toEqual(live); + }); + }); const beehiveMap = { - "0,1": true, - "0,2": true, - "1,0": true, - "1,3": true, - "2,1": true, - "2,2": true, + "0,1": [true, 0], + "0,2": [true, 0], + "1,0": [true, 0], + "1,3": [true, 0], + "2,1": [true, 0], + "2,2": [true, 0], }; + const streamBeehive = fieldStream({ fieldMap: beehiveMap }); + [ + ["-1,0", false], + ["-1,1", false], + ["-1,2", false], + ["-1,3", false], + ["0,-1", false], + ["0,0", false], + ["0,1", true], + ["0,2", true], + ["0,3", false], + ["0,4", false], + ["1,-1", false], + ["1,0", true], + ["1,1", false], + ["1,2", false], + ["1,3", true], + ["1,4", false], + ["2,-1", false], + ["2,0", false], + ["2,1", true], + ["2,2", true], + ["2,3", false], + ["2,4", false], + ["3,0", false], + ["3,1", false], + ["3,2", false], + ["3,3", false], + ].forEach(([key, live]) => { + test(`after one generation of Beehive, ${key} alive: ${live}`, () => { + expect(streamBeehive.next.next.map[key].living).toEqual(live); + }); + }); const boatArray = [ [1, 1, 0], [1, 0, 1], [0, 1, 0], ]; + const streamBoat = fieldStream({ fieldArray: boatArray }); + [ + ["-1,-1", false], + ["-1,0", false], + ["-1,1", false], + ["-1,2", false], + ["0,-1", false], + ["0,0", true], + ["0,1", true], + ["0,2", false], + ["1,-1", false], + ["1,0", true], + ["1,1", false], + ["1,2", true], + ["1,3", false], + ["2,-1", false], + ["2,0", false], + ["2,1", true], + ["2,2", false], + ["2,3", false], + ["3,0", false], + ["3,1", false], + ["3,2", false], + ].forEach(([key, live]) => { + test(`after one generation of Beehive, ${key} alive: ${live}`, () => { + expect(streamBoat.next.next.map[key].living).toEqual(live); + }); + }); +}); + +describe("fieldStream.next tests oscillators", () => { + const blinkerArray = [ + [0, 1, 0], + [0, 1, 0], + [0, 1, 0], + ]; + const streamBlinker = fieldStream({ fieldArray: blinkerArray }); + [ + ["0,0", false], + ["0,1", false], + ["0,2", false], + ["1,0", true], + ["1,1", true], + ["1,2", true], + ["2,0", false], + ["2,1", false], + ["2,2", false], + ].forEach(([key, live]) => { + test(`after one generation of blinker, ${key} alive: ${live}`, () => { + expect(streamBlinker.next.next.map[key].living).toEqual(live); + }); + }); + [ + ["0,0", false], + ["0,1", true], + ["0,2", false], + ["1,0", false], + ["1,1", true], + ["1,2", false], + ["2,0", false], + ["2,1", true], + ["2,2", false], + ].forEach(([key, live]) => { + test(`after two generations of blinker, ${key} alive: ${live}`, () => { + console.log(streamBlinker.next.next.map); + expect(streamBlinker.next.next.next.next.map[key].living).toEqual(live); + }); + }); });