diff --git a/src/components/Cell.js b/src/components/Cell.js index c40941d..dcb3fb9 100644 --- a/src/components/Cell.js +++ b/src/components/Cell.js @@ -11,17 +11,57 @@ class Cell { } setLiving() { if (this.living && this.liveNeighbors !== 2 && this.liveNeighbors !== 3) { + this.liveNeighbors = 0; return (this.living = false); } if (this.liveNeighbors === 3) { + this.liveNeighbors = 0; return (this.living = true); } + this.liveNeighbors = 0; } } +class Stream { + constructor(head, next) { + this.head = head; + this.tail = next; + this.memo = false; + } + get next() { + if (!this.memo) { + this.tail = this.tail(); + this.memo = true; + } + return this.tail; + } +} + +class CellStream extends Stream { + constructor(head, next) { + super(head, next); + } + get living() { + return this.head.living; + } + set liveNeighbors(liveNeighbors) { + this.head.liveNeighbors = liveNeighbors; + } +} + +const cellStream = (living = false, liveNeighbors = 0) => { + return new CellStream(new Cell(living, liveNeighbors), function () { + this.head.setLiving(); + return this; + }); +}; + // as a stream -> cellStream = Stream(Cell, () => Cell(cellStream.isLiving())) // in this case GameField = { [x-y]: cellStream } // communicating with neighbors = filter for (Boolean(Cell.living)) -> Cell neighbors.addLivingNeighbor // controlling whether to call or not: filter for (Boolean(Cell.living) || Boolean(cell.liveNeighbors)) -> cellStream.next -module.exports = Cell; +module.exports = { + Cell, + cellStream, +}; diff --git a/src/components/GameField.js b/src/components/GameField.js index ce43565..7e7e0f3 100644 --- a/src/components/GameField.js +++ b/src/components/GameField.js @@ -1,4 +1,4 @@ -import Cell from "./Cell"; +import { cellStream } from "./Cell"; export default class GameField { constructor({ fieldArray = [], fieldMap = {} }) { @@ -7,12 +7,12 @@ export default class GameField { fieldArray.forEach((subArray, majorIndex) => subArray.forEach((value, minorIndex) => { if (value > 0) { - this.map[`${majorIndex}-${minorIndex}`] = new Cell(true); + this.map[`${majorIndex}-${minorIndex}`] = cellStream(true, 0); } }) ); for (let key in fieldMap) { - this.map[key] = new Cell(true); + this.map[key] = cellStream(true, 0); } // 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) @@ -20,3 +20,7 @@ export default class GameField { } // 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({})); +// const container = document.getElementById("game-field"); diff --git a/src/index.js b/src/index.js index ff83a9c..3f7d44f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,8 @@ -import css from "./styles/reset.css"; -require("./components/Cell")(() => console.log("hello world!"))(); +import reset from "./styles/reset.css"; +import css from "./styles/style.css"; +// import Controls from './components/Controls'; +import GameField from "./components/GameField"; +(() => console.log("hello world!"))(); // controls // -- state=idle ? diff --git a/src/styles/style.css b/src/styles/style.css new file mode 100644 index 0000000..02c915a --- /dev/null +++ b/src/styles/style.css @@ -0,0 +1,19 @@ +* { + background: #333; + color: #eee; +} + +body { + display: flex; + flex-flow: column nowrap; + align-items: center; +} + +main, aside { + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; +} \ No newline at end of file diff --git a/src/test/Cell.test.js b/src/test/Cell.test.js index 3772976..666b242 100644 --- a/src/test/Cell.test.js +++ b/src/test/Cell.test.js @@ -1,4 +1,4 @@ -import Cell from "../components/Cell"; +import { Cell, cellStream } from "../components/Cell"; describe("Cell functionality", () => { test("dispatch toggleLiving state should mark living cell dead", () => { @@ -42,4 +42,27 @@ describe("Cell functionality", () => { expect(cell.living).toEqual(state); }); }); + + const cellStreamNextCalls = [ + [2, false], + [3, true], + [0, false], + [5, false], + ]; + + cellStreamNextCalls.forEach(([liveNeighbors, livingResult]) => { + test(`cellStream advances cell state for ${liveNeighbors} live Neighbors (from dead cell)`, () => { + const cell = cellStream(false, liveNeighbors).next; + expect(cell.living).toEqual(livingResult); + }); + }); + + [[2, true], ...cellStreamNextCalls.slice(1)].forEach( + ([liveNeighbors, livingResult]) => { + test(`cellStream advances cell state for ${liveNeighbors} live Neighbors (from living cell)`, () => { + const cell = cellStream(true, liveNeighbors).next; + expect(cell.living).toEqual(livingResult); + }); + } + ); }); diff --git a/src/test/GameField.test.js b/src/test/GameField.test.js index 71605be..54db8e7 100644 --- a/src/test/GameField.test.js +++ b/src/test/GameField.test.js @@ -1,11 +1,5 @@ import GameField from "../components/GameField"; -describe("Game Field", () => { - test("smoke test", () => { - expect(new GameField([])).toEqual({ map: {} }); - }); -}); - describe("Game Field seeds living Cells with array", () => { const gameArraySeed = new GameField({ fieldArray: [