summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--life2/simulator/README21
-rw-r--r--life2/simulator/package.json79
-rw-r--r--life2/simulator/src/l2sf.js102
-rw-r--r--life2/simulator/src/life2.js233
-rw-r--r--package-lock.json8
5 files changed, 52 insertions, 391 deletions
diff --git a/life2/simulator/README b/life2/simulator/README
index 5843682..e69de29 100644
--- a/life2/simulator/README
+++ b/life2/simulator/README
@@ -1,21 +0,0 @@
-
-
- __ __ _______ _______ ___
- | | | | | ____|| ____| |__ \
- | | | | | |__ | |__ ) |
- | | | | | __| | __| / /
- | `----.| | | | | |____ / /_
- |_______||__| |__| |_______| |____|
-
-
-
-SIMULATOR
-
- Simulator est une application permettant de simuler un automate cellulaire
- inspiré du jeu de la vie de Conway permettant la modification des règles du
- jeu et de l'état des cellules en temps réel.
-
-
-LIFE2
-
- Simulator est une application faisant partie du projet Life 2.
diff --git a/life2/simulator/package.json b/life2/simulator/package.json
index 8961931..d03355e 100644
--- a/life2/simulator/package.json
+++ b/life2/simulator/package.json
@@ -1,35 +1,48 @@
{
- "name": "@nc0/life2-simulator",
- "version": "1.0.0",
- "description": "An evolved and complete simulation program for variants of Conway's Game of Life, allowing dynamic rules and custom cell states.",
- "keywords": [
- "game of life",
- "simulation",
- "cellular automata",
- "conway",
- "evolved",
- "complete",
- "dynamic rules",
- "custom cell states"
- ],
- "homepage": "https://github.com/nc0fr/life2/tree/master/life2/simulator#readme",
- "bugs": {
- "url": "https://github.com/nc0fr/life2/issues",
- "email": "n@nc0.fr"
- },
- "license": "Apache-2.0",
- "author": "Nicolas Paul <n@nc0.fr> (https://nc0.fr)",
- "funding": {
- "type": "individual",
- "url": "https://github.com/sponsors/nc0fr"
- },
- "browser": true,
- "repository": {
- "type": "git",
- "url": "https://github.com/nc0fr/life2.git",
- "directory": "life2/simulator"
- },
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "name": "@life2/simulator",
+ "version": "1.0.0",
+ "description": "Web application to simulate Life2 games",
+ "keywords": [
+ "cellular",
+ "automaton",
+ "game",
+ "life"
+ ],
+ "homepage": "https://github.com/nc0fr/life2/tree/master/life2/simulator#readme",
+ "bugs": {
+ "url": "https://github.com/nc0fr/life2/issues",
+ "email": "n@nc0.fr"
+ },
+ "license": "Apache-2.0",
+ "author": "The Life2 Authors",
+ "contributors": [
+ "Nicolas Paul <n@nc0.fr> (https://nc0.fr)"
+ ],
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/nc0fr"
+ },
+ "browser": true,
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/nc0fr/life2.git",
+ "directory": "life2/simulator"
+ },
+ "files": [
+ "LICENSE",
+ "README"
+ ],
+ "main": "./index.js",
+ "readme": "README",
+ "scripts": {
+ "test": "exit 0",
+ "lint": "eslint .",
+ "lint-fix": "eslint --fix .",
+ "fmt": "clang-format -i *.js",
+ "fmt-check": "clang-format --dry-run --Werror *.js"
+ },
+ "dependencies": {
+ "@life2/game": "1.0.0",
+ "@life2/format": "1.0.0"
+ }
}
-}
diff --git a/life2/simulator/src/l2sf.js b/life2/simulator/src/l2sf.js
deleted file mode 100644
index 4f9918c..0000000
--- a/life2/simulator/src/l2sf.js
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2024 The Life2 Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {CellStates} from './life2.js';
-
-/**
- * @fileoverview Life2 Simulator Format.
- * A board in the Life2 game can be represented in a specific file format to
- * share various states with a team easily.
- * @package l2sf
- */
-
-/**
- * String representation of a cell.
- * @enum {string}
- * @const
- */
-export const CellChar = {
- /** An empty cell. */
- EMPTY: '.',
- /** A cell belonging to team A. */
- TEAM_A: 'a',
- /** A cell belonging to team B. */
- TEAM_B: 'b',
- /** A barrier cell. */
- BARRIER: '#',
-};
-
-/**
- * Converts a cell state to a cell char.
- * @param {!CellStates} cell The cell state.
- * @return {!CellChar} The cell character.
- * @throws {TypeError} If the cell state is not recognized.
- * @final
- */
-export function cellStateToCellChar(cell) {
- switch (cell) {
- case CellStates.EMPTY:
- return CellChar.EMPTY;
- case CellStates.TEAM_A:
- return CellChar.TEAM_A;
- case CellStates.TEAM_B:
- return CellChar.TEAM_B;
- case CellStates.BARRIER:
- return CellChar.BARRIER;
- default:
- throw new TypeError(`Unknown cell state: ${cell}`);
- }
-}
-
-/**
- * Converts a cell char to a cell state.
- * @param {!CellChar} cell The cell character.
- * @return {!CellStates} The cell state.
- * @throws {TypeError} If the cell character is not recognized.
- * @final
- */
-export function cellCharToCellState(cell) {
- switch (cell) {
- case CellChar.EMPTY:
- return CellStates.EMPTY;
- case CellChar.TEAM_A:
- return CellStates.TEAM_A;
- case CellChar.TEAM_B:
- return CellStates.TEAM_B;
- case CellChar.BARRIER:
- return CellStates.BARRIER;
- default:
- throw new TypeError(`Unknown cell character: ${cell}`);
- }
-}
-
-/**
- * Converts a simulation board to an array of strings.
- * @param {!Board} board The simulation board.
- * @return {!Array<string>} The array of strings.
- * @final
- */
-export function boardToFile(board) {
- return board.map(row => row.map(cellStateToCellChar).join('')).join('\n');
-}
-
-/**
- * Converts an array of strings to a simulation board.
- * @param {!Array<string>} file The array of strings.
- * @return {!Board} The simulation board.
- * @final
- */
-export function fileToBoard(file) {
- return file.map(row => row.split('').map(cellCharToCellState));
-}
diff --git a/life2/simulator/src/life2.js b/life2/simulator/src/life2.js
deleted file mode 100644
index 20e061e..0000000
--- a/life2/simulator/src/life2.js
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright 2024 The Life2 Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-/**
- * @fileoverview Life2.
- * The Life2 cellular automaton game logic, ready to be used within Web
- * applications.
- * @package life2
- */
-
-/**
- * A cell in the Life2 game.
- * @enum {number}
- * @const
- */
-export const CellStates = {
- /** An empty cell. */
- EMPTY: 0,
- /** A cell belonging to team A. */
- TEAM_A: 1,
- /** A cell belonging to team B. */
- TEAM_B: 2,
- /** A barrier cell. */
- BARRIER: 3,
-};
-
-/**
- * A board in the Life2 game.
- * @typedef {!Array<!Array<!CellStates>>}
- */
-export let Board;
-
-/**
- * The entry point of any simulation of the Life2 game.
- * @final
- */
-export class World {
- /**
- * Initializes a Life2 game.
- * @param {number} width The board width.
- * @param {number} height The board height.
- * @throws
- */
- constructor(width, height) {
- if (width <= 0) throw new RangeError('width must be a positive number');
- if (height <= 0) throw new RangeError('height must be a positive number');
-
- /**
- * The width of the board.
- * @type {number}
- * @final @const
- */
- this.width = width;
- /**
- * The height of the board.
- * @type {number}
- * @final @const
- */
- this.height = height;
- /**
- * The Life2 simulation board.
- * @type {!Board}
- * @private
- */
- this.board_ = this.initializeBoard_(width, height);
- }
-
- /**
- * Initializes an empty board filled with empty cells.
- * @param {number} width The width of the board.
- * @param {number} height The height of the board.
- * @return {!Array<!Board>} The empty board.
- * @throws {RangeError}
- * @final @private
- */
- initializeBoard_(width, height) {
- if (width <= 0) throw new RangeError('width must be a positive number');
- if (height <= 0) throw new RangeError('height must be a positive number');
- return Array(width).fill(Array(width).fill(CellStates.EMPTY));
- }
-
- /**
- * Get the cell at a specific position.
- * @param {number} x The x position of the cell.
- * @param {number} y The y position of the cell.
- * @return {!CellStates} The cell at the position.
- * @throws {RangeError}
- * @final
- */
- getCell(x, y) {
- if (this.isOutOfBounds_(x, y)) throw RangeError('position out of bounds');
-
- return this.board_[x][y];
- }
-
- /**
- * Set the cell at a specific position to the given cell.
- * This should usually not be used directly, as the state of the cell must be
- * determined by the rules. However, it is useful for setting up the initial
- * state of the board, or changing the board during execution.
- * @param {number} x The x position of the cell.
- * @param {number} y The y position of the cell.
- * @param {!CellStates} cell The cell state to set.
- * @throws {RangeError | TypeError}
- * @final
- */
- setCell(x, y, cell) {
- if (this.isOutOfBounds_(x, y)) {
- throw new RangeError('position out of bounds');
- }
-
- if (cell < CellStates.EMPTY || cell > CellStates.BARRIER) {
- throw new TypeError('cell must be a valid Cell');
- }
-
- this.board_[x][y] = cell;
- }
-
- /**
- * Get the board.
- * @return {!Board} The board.
- * @final
- */
- getBoard() {
- return this.board_;
- }
-
- /**
- * Check if a position is out of bounds.
- * @param {number} x The x position of the cell.
- * @param {number} y The y position of the cell.
- * @return {boolean} True if the position is out of bounds, false otherwise.
- * @final @private
- */
- isOutOfBounds_(x, y) {
- return x < 0 || x >= this.width || y < 0 || y >= this.height;
- }
-
- /**
- * Get the adjacent neighbors of a cell.
- * @param {number} x The x position of the cell.
- * @param {number} y The y position of the cell.
- * @return {!Array<!CellStates>} The neighbors of the cell.
- * @throws {RangeError}
- * @final @private
- */
- getNeighbors_(x, y) {
- if (this.isOutOfBounds_(x, y)) {
- throw new RangeError('position out of bounds');
- }
-
- return [
- (x - 1, y - 1),
- (x, y - 1),
- (x + 1, y - 1),
- (x - 1, y),
- (x + 1, y),
- (x - 1, y + 1),
- (x, y + 1),
- (x + 1, y + 1),
- ].filter((x, y) => !this.isOutOfBounds_(x, y))
- .map((x, y) => this.getCell(x, y));
- }
-
- /**
- * Execute the rules of the game to determine the next state of a cell.
- * @param {!CellStates} cell The current state of the cell.
- * @param {number} x The x position of the cell.
- * @param {number} y The y position of the cell.
- * @return {!CellStates} The next state of the cell.
- * @final @private
- */
- nextCellState_(cell, x, y) {
- // 1. Barrier cells don't change.
- // 2. Empty cells become the team with the most neighbors.
- // 3. Filled cells with less than 2 neighbors of the same team become empty.
- // 4. Filled cells with 2 or 3 neighbors of the same team stay the same.
- // 5. Filled cells with more than 3 neighbors of the same team become empty.
-
- // (1)
- if (cell === CellStates.BARRIER) return CellStates.BARRIER;
-
- const neighbors = this.getNeighbors_(x, y);
- const neighborsA = neighbors.filter((cell) => cell === CellStates.TEAM_A);
- const neighborsB = neighbors.filter((cell) => cell === CellStates.TEAM_B);
-
- // (2)
- if (cell === CellStates.EMPTY) {
- if (neighborsA.length > neighborsB.length) return CellStates.TEAM_A;
- return CellStates.TEAM_B;
- }
-
- // (3)
- if ((cell === CellStates.TEAM_A && neighborsA.length < 2) ||
- (cell === CellStates.TEAM_B && neighborsB.length < 2)) {
- return CellStates.EMPTY;
- }
-
- // (4)
- if ((cell === CellStates.TEAM_A && neighborsA.length <= 3) ||
- (cell === CellStates.TEAM_B && neighborsB.length <= 3)) {
- return cell;
- }
-
- // (5)
- return CellStates.EMPTY;
- }
-
- /**
- * Calculate the next state of the board and update it.
- * @final
- */
- nextState() {
- const nextBoard =
- this.initializeBoard_(this.width, this.height).forEach((row, x) => {
- row.map((cell, y) => {
- this.nextCellState_(cell, x, y);
- });
- });
- this.board_ = nextBoard;
- }
-}
diff --git a/package-lock.json b/package-lock.json
index a4c63c2..3382694 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -41,9 +41,13 @@
}
},
"life2/simulator": {
- "name": "@nc0/life2-simulator",
+ "name": "@life2/simulator",
"version": "1.0.0",
"license": "Apache-2.0",
+ "dependencies": {
+ "@life2/format": "1.0.0",
+ "@life2/game": "1.0.0"
+ },
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/nc0fr"
@@ -179,7 +183,7 @@
"resolved": "life2/game",
"link": true
},
- "node_modules/@nc0/life2-simulator": {
+ "node_modules/@life2/simulator": {
"resolved": "life2/simulator",
"link": true
},