summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mind/topology.js4
-rw-r--r--src/world/agent.js55
-rw-r--r--src/world/agent.test.js49
-rw-r--r--src/world/proposal.js2
4 files changed, 107 insertions, 3 deletions
diff --git a/src/mind/topology.js b/src/mind/topology.js
index 127351e..9c5ecf7 100644
--- a/src/mind/topology.js
+++ b/src/mind/topology.js
@@ -46,7 +46,7 @@ function is_hidden(n, index) {
// returns a new network with an edge between the given nodes
// with the given weight
-function network_connect(n, source, sink, weight) {
+export function network_connect(n, source, sink, weight) {
if (is_input(n, sink)) {
// inputs cannot be sinks
throw new Error("attempt to use input as sink");
@@ -146,7 +146,7 @@ function get_value(n, index, input, prev, cache) {
// compute a network's output and new hidden state
// given the input and previous hidden state
-function network_compute(n, input, state) {
+export function network_compute(n, input, state) {
// validate input
if (input.length !== n.input_count) {
throw new Error("incorrect number of input elements");
diff --git a/src/world/agent.js b/src/world/agent.js
new file mode 100644
index 0000000..d91481b
--- /dev/null
+++ b/src/world/agent.js
@@ -0,0 +1,55 @@
+'use strict';
+
+import { sense_read } from './sense.js';
+import { proposal_merge } from './proposal.js';
+
+
+export function agent_decide(lattice, agent, senses, actions) {
+ const inputs = senses.map(s => sense_read(lattice, agent, s)).flat();
+ const [result, state] = agent.net.compute(inputs, agent.state);
+
+ const new_agent = { ...agent, state };
+ const [proposals, _] = actions.reduce(
+ ([proposals, result], action) => {
+ const head = result.slice(0, action.size);
+ const tail = result.slice(action.size);
+
+ const props = action
+ .propose(new_agent, head)
+ .reduce(
+ (acc, proposal) => proposal_merge(acc, proposal),
+ proposals
+ );
+
+ return [props, tail];
+ },
+ [[], result]
+ );
+
+ return [new_agent, proposals];
+}
+
+
+function change_apply(agent, ch) {
+ const { x, y, flags } = ch;
+ return {
+ ...agent,
+ x: (x || agent.x), y: (y || agent.y),
+ flags: { ...agent.flags, ...flags },
+ };
+}
+
+
+export function agent_apply(agent, proposals) {
+ return proposals
+ .filter(p => p.agent_changes)
+ .reduce(
+ (acc, p) => p.agent_changes
+ .filter(ch => ch.agent_id === agent.id)
+ .reduce(
+ (acc_, ch) => change_apply(acc_, ch),
+ acc
+ ),
+ agent
+ );
+}
diff --git a/src/world/agent.test.js b/src/world/agent.test.js
new file mode 100644
index 0000000..5d5828f
--- /dev/null
+++ b/src/world/agent.test.js
@@ -0,0 +1,49 @@
+import { agent_decide, agent_apply } from './agent.js';
+
+
+test("simple agent decisions", () => {
+ const lattice = null;
+ const agent = {
+ id: 3,
+ net: { compute: () => [[0, 1], 'state'] },
+ state: null,
+ x: 0, y: 0,
+ flags: {},
+ };
+
+ const senses = [];
+ const actions = [
+ { size: 1, propose: (agent, head) => [{ agent_changes: [{ agent_id: 3, flags: { act1: head[0] } }] }] },
+ { size: 1, propose: (agent, head) => [{ agent_changes: [{ agent_id: 3, flags: { act2: head[0] } }] }] },
+ ];
+
+ expect(agent_decide(lattice, agent, senses, actions)).toEqual([
+ { ...agent, state: 'state' },
+ actions.map((a, idx) => a.propose(null, [idx])).flat(),
+ ]);
+});
+
+
+test("apply proposals to agent", () => {
+ const props = [
+ { agent_changes: [{ agent_id: 14, x: 4, y: 3 }] },
+ { agent_changes: [{ agent_id: 16, x: 5, y: 3 }] },
+ { agent_changes: [{ agent_id: 14, flags: { frozen: true } }] },
+ ];
+
+ const agent = {
+ id: 14,
+ net: null,
+ state: null,
+ x: 0, y: 0,
+ flags: { frozen: false, emit: 6 }
+ };
+
+ expect(agent_apply(agent, props)).toEqual({
+ id: 14,
+ net: null,
+ state: null,
+ x: 4, y: 3,
+ flags: { frozen: true, emit: 6 }
+ });
+});
diff --git a/src/world/proposal.js b/src/world/proposal.js
index 9f98fd4..0969540 100644
--- a/src/world/proposal.js
+++ b/src/world/proposal.js
@@ -21,7 +21,7 @@ import { pairs, deepEqual } from '../util.js';
/* action structure:
* {
* name: string,
- * propose: (agent) => proposal[]
+ * propose: (agent, output) => proposal[]
* }
*/