diff options
| -rw-r--r-- | life2/simulator/README | 21 | ||||
| -rw-r--r-- | life2/simulator/package.json | 79 | ||||
| -rw-r--r-- | life2/simulator/src/l2sf.js | 102 | ||||
| -rw-r--r-- | life2/simulator/src/life2.js | 233 | ||||
| -rw-r--r-- | package-lock.json | 8 |
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 }, |
