diff --git a/src/components/CanvasBoard.vue b/src/components/CanvasBoard.vue
index 8d1c16b..4362795 100644
--- a/src/components/CanvasBoard.vue
+++ b/src/components/CanvasBoard.vue
@@ -2,9 +2,6 @@
import { onMounted, watch } from "vue";
import { globalStore } from "../stores/index.js";
import {
- create1dState,
- create1dStateOneCell,
- create2dState,
createBoard,
conwayRules,
overpopulationRules,
@@ -13,8 +10,11 @@
highLifeRules,
servietteRules,
evolve2d,
- } from "../modules/automata.js";
- import { getRandomInt } from "../modules/common.js";
+ } from "../modules/core.js";
+ import {
+ create1dInitialState,
+ create2dRandomGrid,
+ } from "../modules/board.js";
import { boardToPic, picToBoard } from "../modules/picture.js";
const store = globalStore();
@@ -36,7 +36,7 @@
// used to determine the dimensions of the board
const max = () => {
- return Math.max(store.boardWidth, store.boardHeight);
+ return Math.max(store.board.width, store.board.height);
};
const selectedRules = () => {
@@ -92,10 +92,10 @@
});
// draws the board on the canvas
- const drawCanvas = async (board, width, height) => {
- const d = store.cellProperties.size;
+ const drawCanvas = async (board) => {
+ const d = board.cellProperties.size;
// bool to RGBA colors
- const img = await boardToPic(board, width, height, store.cellProperties);
+ const img = await boardToPic(board);
// rescale and draw
ctx.save();
ctx.clearRect(0, 0, store.canvasWidth, store.canvasHeight);
@@ -106,39 +106,22 @@
ctx.restore();
};
- // create a first state, either a single living cell
- // at the center or random ones
- const compute1dInitialState = () => {
- if (store.initial1dState === "onecell")
- return create1dStateOneCell(store.boardWidth);
- return create1dState(store.boardWidth, getRandomInt, [0, 2]);
- };
-
- // initialize board with random cells
- const randomInitialState = () => {
- return create2dState(
- store.boardWidth,
- store.boardHeight,
- getRandomInt,
- [0, 2]
- );
- };
-
// draw elementary automaton on the canvas based on selected ruleset
const draw1d = () => {
- const initialState = compute1dInitialState();
+ const initialState = create1dInitialState(
+ store.board,
+ store.initial1dState
+ );
const board = createBoard(initialState, store.ruleset1d.rules, max());
- store.lastBoard = Object.freeze(board);
- // TODO: the board clearly could be an object
- drawCanvas(store.lastBoard, store.boardWidth, store.boardHeight);
+ store.board.grid = Object.freeze(board);
+ drawCanvas(store.board);
store.toggleStop();
};
// draw 2D automaton on the canvas in a loop
const draw2d = (board) => {
- const newBoard = evolve2d(board, selectedRules());
- drawCanvas(newBoard, store.boardWidth, store.boardHeight);
- store.lastBoard = Object.freeze(newBoard);
+ store.board.grid = Object.freeze(evolve2d(board.grid, selectedRules()));
+ drawCanvas(store.board);
};
// draw 2d automaton in a loop, starting from passed state
@@ -147,7 +130,7 @@
requestAnimationFrame(() => {
if (!store.canDraw) return;
draw2d(board);
- return draw2dNext(store.lastBoard);
+ return draw2dNext(store.board);
});
}, store.refreshRate);
};
@@ -155,18 +138,21 @@
// draw 2d automaton from a new state
const draw2dNew = async () => {
if (!store.canDraw) return;
- const initialState = randomInitialState();
- const board = evolve2d(initialState, selectedRules());
- if (store.loop) return draw2dNext(board);
- else draw2d(board);
+ const initialGrid = create2dRandomGrid(
+ store.board.width,
+ store.board.height
+ );
+ store.board.grid = Object.freeze(evolve2d(initialGrid, selectedRules()));
+ if (store.loop) return draw2dNext(store.board);
+ else draw2d(store.board);
store.toggleStop();
};
// draw 2d automaton from the last known generated board
const draw2dLast = async () => {
if (!store.canDraw) return;
- if (store.loop) return draw2dNext(store.lastBoard);
- else draw2d(store.lastBoard);
+ if (store.loop) return draw2dNext(store.board);
+ else draw2d(store.board);
store.toggleStop();
};
@@ -185,19 +171,25 @@
);
// draw the image back on the work canvas with the dimensions of the board
- workCtx.drawImage(store.picture, 0, 0, store.boardWidth, store.boardHeight);
+ workCtx.drawImage(
+ store.picture,
+ 0,
+ 0,
+ store.board.width,
+ store.board.height
+ );
// get the resized image data from work canvas
const resized = workCtx.getImageData(
0,
0,
- store.boardWidth,
- store.boardHeight
+ store.board.width,
+ store.board.height
);
// convert the image into a 2D board of boolean based on pixel value
- store.lastBoard = Object.freeze(
- picToBoard(resized.data, store.boardWidth, store.boardHeight)
+ store.board.grid = Object.freeze(
+ picToBoard(resized.data, store.board.width, store.board.height)
);
store.toggleStop();
@@ -206,7 +198,7 @@
// stop drawing routines and clear the canvas
const reset = () => {
store.toggleStop();
- store.lastBoard = null;
+ store.board.grid = null;
ctx.clearRect(0, 0, store.canvasWidth, store.canvasHeight);
store.reset = false;
};
diff --git a/src/components/MenuCellProperties.vue b/src/components/MenuCellProperties.vue
index 3f9f4a1..4a5d7d3 100644
--- a/src/components/MenuCellProperties.vue
+++ b/src/components/MenuCellProperties.vue
@@ -5,17 +5,14 @@
const store = globalStore();
const updateCellProperties = (event) => {
- const elem = event.target;
- store.cellProperties[elem.name] = elem.value;
+ const { name, value } = event.target;
+ store.setCellProperties(name, value);
store.setBoardWidth();
store.setBoardHeight();
};
const switchColor = () => {
- [store.cellProperties["liveColor"], store.cellProperties["deadColor"]] = [
- store.cellProperties["deadColor"],
- store.cellProperties["liveColor"],
- ];
+ store.switchColor();
};
@@ -27,7 +24,7 @@
@@ -36,7 +33,7 @@
@@ -49,7 +46,7 @@
name="size"
type="number"
min="1"
- :value="store.cellProperties.size"
+ :value="store.board.cellProperties.size"
@click="updateCellProperties"
/>
diff --git a/src/modules/board.js b/src/modules/board.js
new file mode 100644
index 0000000..6e663c0
--- /dev/null
+++ b/src/modules/board.js
@@ -0,0 +1,28 @@
+import { create2dState, create1dStateOneCell, create1dState } from "./core.js";
+
+import { getRandomInt } from "./common.js";
+
+function Board(width, height, grid = []) {
+ this.width = width;
+ this.height = height;
+ (this.cellProperties = {
+ size: 3,
+ liveColor: "#000000",
+ deadColor: "#F5F5F5",
+ }),
+ (this.grid = grid);
+}
+
+// create a first state, either a single living cell
+// at the center or random ones
+const create1dInitialState = (board, type = "onecell") => {
+ if (type === "onecell") return create1dStateOneCell(board.width);
+ return create1dState(board.width, getRandomInt, [0, 2]);
+};
+
+// initialize 2d board with random cells
+const create2dRandomGrid = (width, height) => {
+ return create2dState(width, height, getRandomInt, [0, 2]);
+};
+
+export { Board, create1dInitialState, create2dRandomGrid };
diff --git a/src/modules/picture.js b/src/modules/picture.js
index 06e9ea3..c037b8f 100644
--- a/src/modules/picture.js
+++ b/src/modules/picture.js
@@ -44,19 +44,21 @@ export function picToBoard(pixels, width, height) {
acc[index] = value;
return acc;
}, []);
-
+ // TODO: The representation has to be 2D, not the data structure
+ // (change to flat)
return toMatrix(flat, width, height);
}
// convert board to ImageData
// TODO : different cell to color functions
// (binary, intermediate states, camaieux, etc)
-export function boardToPic(board, width, height, cellProperties) {
- const live = cellProperties.liveColor;
- const dead = cellProperties.deadColor;
- const img = new ImageData(width, height);
+export function boardToPic(board) {
+ const live = board.cellProperties.liveColor;
+ const dead = board.cellProperties.deadColor;
+ const img = new ImageData(board.width, board.height);
const colors = [hexToRGB(live), hexToRGB(dead)];
- board.flat().reduce((acc, cell, index) => {
+ board.grid.flat().reduce((acc, cell, index) => {
+ // TODO : bitshift operation instead of ternary
const color = cell === 1 ? colors[0] : colors[1];
const i = index * 4;
acc[i] = color[0];
diff --git a/src/stores/index.js b/src/stores/index.js
index 1be37a4..07bae1f 100644
--- a/src/stores/index.js
+++ b/src/stores/index.js
@@ -1,4 +1,5 @@
import { defineStore } from "pinia";
+import { Board } from "../modules/board.js";
export const globalStore = defineStore("globalStore", {
state: () => {
@@ -21,19 +22,12 @@ export const globalStore = defineStore("globalStore", {
name: "Conway's Game of Life",
description: "The most popular 2d automata",
},
- cellProperties: {
- size: 3,
- liveColor: "#000000",
- deadColor: "#F5F5F5",
- },
canvasWidth: 0,
canvasHeight: 0,
- boardWidth: 0,
- boardHeight: 0,
refreshRate: 300,
initial1dState: "onecell",
drawingDirection: "y",
- lastBoard: null,
+ board: new Board(),
draw1d: false,
draw2d: false,
draw2dLast: false,
@@ -49,13 +43,27 @@ export const globalStore = defineStore("globalStore", {
},
actions: {
setBoardWidth() {
- this.boardWidth = Math.floor(this.canvasWidth / this.cellProperties.size);
+ this.board.width = Math.floor(
+ this.canvasWidth / this.board.cellProperties.size
+ );
},
setBoardHeight() {
- this.boardHeight = Math.floor(
- this.canvasHeight / this.cellProperties.size
+ this.board.height = Math.floor(
+ this.canvasHeight / this.board.cellProperties.size
);
},
+ setCellProperties(name, value) {
+ this.board.cellProperties[name] = value;
+ },
+ switchColor() {
+ [
+ this.board.cellProperties["liveColor"],
+ this.board.cellProperties["deadColor"],
+ ] = [
+ this.board.cellProperties["deadColor"],
+ this.board.cellProperties["liveColor"],
+ ];
+ },
toggleDraw1d() {
this.setActiveSubMenu("");
this.setMainMenu(false);
diff --git a/tests/board.test.js b/tests/board.test.js
new file mode 100644
index 0000000..84227c9
--- /dev/null
+++ b/tests/board.test.js
@@ -0,0 +1,28 @@
+import { describe, expect, test } from "vitest";
+import {
+ Board,
+ create1dInitialState,
+ create2dRandomGrid,
+} from "src/modules/board.js";
+
+describe("Board", () => {
+ const board = new Board(503, 301);
+ test("create1dInitialState onecell", () => {
+ const stateOne = create1dInitialState(board);
+ expect(stateOne.length).toBe(board.width);
+ expect(stateOne).toContain(1);
+ });
+
+ test("create1dInitialState random", () => {
+ const stateRandom = create1dInitialState(board, "random");
+ expect(stateRandom.length).toBe(board.width);
+ expect(stateRandom).toContain(1);
+ });
+
+ test("create2dRandomGrid", () => {
+ const board = new Board(503, 301);
+ const got = create2dRandomGrid(board.width, board.height);
+ expect(got.length).toBe(board.height);
+ expect(got[0].length).toBe(board.width);
+ });
+});