summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-11-09 16:46:33 -0600
committersanine <sanine.not@pm.me>2023-11-09 16:46:33 -0600
commit55b714abf83e01aa0ff513ad6ba4978f4b4da6cd (patch)
tree71518c2a6546681724f095424b755c2ab1ec8ecd
parent819d8a51c5ba8b1bec06163dba9c3e0212f1120a (diff)
add basic game of life lattice rules
-rw-r--r--src/simulation/lattice_rules.js53
-rw-r--r--src/simulation/lattice_rules.test.js25
-rw-r--r--src/world/lattice.test.js9
-rw-r--r--src/world/world.js19
4 files changed, 102 insertions, 4 deletions
diff --git a/src/simulation/lattice_rules.js b/src/simulation/lattice_rules.js
new file mode 100644
index 0000000..0f46d0d
--- /dev/null
+++ b/src/simulation/lattice_rules.js
@@ -0,0 +1,53 @@
+import { pairs } from '../util.js';
+
+function mod(k, n) {
+ return ((k % n) + n) % n;
+}
+
+function pos_wrap(lattice, x, y) {
+ const height = lattice.length;
+ const width = lattice[0].length;
+ return [mod(x, width), mod(y, height)];
+}
+
+
+function neighbors(lattice, x, y) {
+ const offsets = [-1, 0, 1];
+ const positions = pairs(offsets, offsets)
+ .filter(([dx, dy]) => dx !== 0 || dy !== 0)
+ .map(([dx, dy]) => pos_wrap(lattice, x+dx, y+dy));
+ const neighbors = positions
+ .map(([x, y]) => [x, y, lattice[y][x]]);
+ return neighbors;
+}
+
+
+export const lattice_rules = {
+
+ empty: (lattice, x, y) => {
+ const num_active_neighbors = neighbors(lattice, x, y)
+ .map(([x, y, cell]) => cell.type)
+ .filter(type => type === 'mutable' || type === 'active')
+ .length;
+ if (num_active_neighbors === 3) {
+ return { world_updates: [{ x, y, from: 'empty', to: 'active' }] };
+ }
+ },
+
+ active: (lattice, x, y) => {
+ const num_active_neighbors = neighbors(lattice, x, y)
+ .map(([x, y, cell]) => cell.type)
+ .filter(type => type === 'mutable' || type === 'active')
+ .length;
+ if (num_active_neighbors < 2) {
+ return { world_updates: [{ x, y, from: 'active', to: 'empty' }] };
+ } else if (num_active_neighbors > 3) {
+ return { world_updates: [{ x, y, from: 'active', to: 'empty' }] };
+ }
+ },
+
+ mutable: () => {},
+ immutable: () => {},
+ flag: () => {},
+
+};
diff --git a/src/simulation/lattice_rules.test.js b/src/simulation/lattice_rules.test.js
new file mode 100644
index 0000000..a91400e
--- /dev/null
+++ b/src/simulation/lattice_rules.test.js
@@ -0,0 +1,25 @@
+import { world_update } from '../world/world.js';
+import { lattice_rules } from './lattice_rules.js';
+
+
+test("blinker", () => {
+ const L = { type: 'active', flags: {} };
+ const D = { type: 'empty', flags: {} };
+ const lattice = [
+ [ D, D, D, D, D ],
+ [ D, D, D, D, D ],
+ [ D, L, L, L, D ],
+ [ D, D, D, D, D ],
+ [ D, D, D, D, D ],
+ ];
+
+ const world = { lattice, lattice_rules, agents: [], senses: [], actions: [] };
+ expect(world_update(world).lattice).toEqual([
+ [ D, D, D, D, D ],
+ [ D, D, L, D, D ],
+ [ D, D, L, D, D ],
+ [ D, D, L, D, D ],
+ [ D, D, D, D, D ],
+ ]);
+ expect(world_update(world_update(world)).lattice).toEqual(lattice);
+});
diff --git a/src/world/lattice.test.js b/src/world/lattice.test.js
index 7c71d04..c2fdb6b 100644
--- a/src/world/lattice.test.js
+++ b/src/world/lattice.test.js
@@ -34,6 +34,15 @@ test("growth update rule", () => {
});
+//test("agents cannot move into non-empty tiles", () => {
+// const lattice = [[ {type: 'empty', flags: {}}, {type: 'filled', flags: {}} ]];
+// const bad_prop = [{ agent_updates: [{ agent_id: 14, x: 1, y: 0 }] }];
+// expect(lattice_valid(lattice, bad_prop)).toBe(false);
+// const good_prop = [{ agent_updates: [{ agent_id: 14, x: 0, y: 0 }] }];
+// expect(lattice_valid(lattice, bad_prop)).toBe(true);
+//});
+
+
test("growth update rule applied", () => {
const lattice = [[
{ type: 'empty', flags: {} },
diff --git a/src/world/world.js b/src/world/world.js
index c824e91..87d0aed 100644
--- a/src/world/world.js
+++ b/src/world/world.js
@@ -1,19 +1,30 @@
+import { lattice_update, lattice_valid, lattice_apply } from './lattice.js';
+import { agent_decide, agent_apply } from './agent.js';
+import { proposal_merge } from './proposal.js';
+
+
export function world_update(world, postprocess=[]) {
const lattice_props = lattice_update(world.lattice, world.lattice_rules);
const intermediate_lattice = lattice_apply(world.lattice, lattice_props);
- const agent_props = world.agents
+ const decisions = world.agents
.map(a => agent_decide(world.lattice, agent, world.senses, world.actions))
+ .reduce(
+ ([agents, props], [agent, prop]) => [[...agents, agent], [...props, prop]],
+ [[], []]
+ );
+ const intermediate_agents = decisions[0];
+ const agent_props = decisions[1]
.flat()
.reduce((acc, prop) => proposal_merge(acc, prop), [])
.filter(prop => lattice_valid(intermediate_lattice, prop))
const lattice = lattice_apply(intermediate_lattice, agent_props);
- const agents = world.agents.map(a => agent_apply(a, agent_props));
+ const agents = intermediate_agents.map(a => agent_apply(a, agent_props));
- const world = {...world, lattice, agents};
+ const new_world = {...world, lattice, agents};
return postprocess.reduce(
(acc, f) => f(acc),
- world
+ new_world
);
}