summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Paul <n@nc0.fr>2024-03-07 11:49:36 +0100
committerNicolas Paul <n@nc0.fr>2024-03-08 23:12:55 +0100
commit39748129f1f6c142857fa12a815c08aa31931953 (patch)
treeec0636cf68c25cf4adf963f3da86ac34c4f26002
parent7c4e400fe9991af8f395dec12a047a450558178e (diff)
game: Define game Rules
Rules a simple mathematical fuctions that determine the next state of the cells they are applied to. By their nature, rules either return a new state, or do not apply. Our implementation of rules allow modularity, where a rules either return a state (Cell type) to notice a change, or nothing (the null object) to notice no change. The merit of such an implementation is that the last choice is ultimately done by the board, which increases the flexibility of downstream users. For instance, a standard implementation of the game could determine the final new state of a cell by reducing all the rules output on the given cell and choosing the most occuring one: const cell = ...; const neighbors = board.getNeighbors(cell); const states = rules.map(r => r.execute(cell, neighbords)) .filter(s => s != null); const empty = states.filter(s => s === Cell.EMPTY) .length; const teamA = states.filter(s => s === Cell.TEAM_A) .length; const teamB = states.filter(s => s === Cell.TEAM_B) .length; const newState = Math.max(empty, teamA, teamB); boad.changeCell(cell, newState); Note that this is some fake code, the final API may not look like this. Signed-off-by: Nicolas Paul <n@nc0.fr>
-rw-r--r--life2/game/index.js1
-rw-r--r--life2/game/rule.js93
2 files changed, 94 insertions, 0 deletions
diff --git a/life2/game/index.js b/life2/game/index.js
index f60d7ec..ce6b355 100644
--- a/life2/game/index.js
+++ b/life2/game/index.js
@@ -22,3 +22,4 @@
*/
export {Cell} from './cell';
+export {Rule, RuleFunction} from './rule';
diff --git a/life2/game/rule.js b/life2/game/rule.js
new file mode 100644
index 0000000..5a2b1f2
--- /dev/null
+++ b/life2/game/rule.js
@@ -0,0 +1,93 @@
+/**
+ * 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.
+ * @license
+ */
+
+/**
+ * @fileoverview Rules are mathematical expressions that are used to determine
+ * the next state of a cell based on its current state within a board.
+ * Rules are functions that either return a new cell state, or nothing if they
+ * don't apply to the cell.
+ * The final choice for a cell state is made by the board, using all the results
+ * from the multiple rules being applied to the cell.
+ *
+ * @package life2
+ */
+
+import {Cell} from './index';
+
+/**
+ * The mathematical function bodies that is applied to a cell.
+ * The function takes two arguments, the current cell the rule is being applied
+ * on, and a list of its neighbors. It returns either a new cell state, or
+ * null if the rule should not be applied to the cell.
+ * The neighbors list is provided by the board, and may not be ordered in any
+ * meaningful way, care should be taken when relying on its order. The same
+ * applies for its contents, which may be only the eight adjacent cells in a
+ * rectangular board, or a different set of cells for other board shapes.
+ * @typedef {!function(!Cell, !Array<!Cell>): ?Cell}
+ * @const
+ */
+export let RuleFunction;
+
+/**
+ * A rule is a function that can apply a transformation to a cell based on its
+ * current state and the state of its neighbors.
+ */
+export class Rule {
+ /**
+ * Creates a new rule that can be consumed by a board.
+ * @param {string} name The name of the rule, may be used for debugging in
+ * visual interfaces.
+ * @param {string} details A human-readable description of the rule, may be
+ * used for debugging in visual interfaces.
+ * @param {!RuleFunction} expression The function body of the rule, which will
+ * be called by the game manager to apply the rule to a cell.
+ */
+ constructor(name, details, expression) {
+ /**
+ * The name of the rule, may be used for debugging in visual interfaces.
+ * @type {string}
+ * @const
+ */
+ this.name = name;
+ /**
+ * A human-readable description of the rule, may be used for debugging in
+ * visual interfaces.
+ * @type {string}
+ * @const
+ */
+ this.details = details;
+ /**
+ * The function body of the rule, which will be called by the game manager
+ * to apply the rule to a cell.
+ * @type {!RuleFunction}
+ * @const @private
+ */
+ this.expression_ = expression;
+ }
+
+ /**
+ * Applies the rule to a cell and its neighbors, returning the new state of
+ * the cell, or null if the rule does not apply to the cell.
+ * @param {!Cell} cell The cell to apply the rule to.
+ * @param {!Array<!Cell>} neighbors The list of neighbors of the cell.
+ * @return {?Cell} The new state of the cell, or null if the rule does not
+ * apply to the cell.
+ */
+ execute(cell, neighbors) {
+ return this.expression_(cell, neighbors);
+ }
+}