diff options
author | sanine <sanine.not@pm.me> | 2023-11-08 23:09:41 -0600 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-11-08 23:09:41 -0600 |
commit | 5f2683c0aff63880794b2f3262e0d6abc76bd80a (patch) | |
tree | c896e6af6143c77b05f359d7ba988875f241934c | |
parent | f78493e192daf4639b91352909e4029b9970fcdb (diff) |
export extra topology functions & add agent.js
-rw-r--r-- | src/mind/topology.js | 4 | ||||
-rw-r--r-- | src/world/agent.js | 55 | ||||
-rw-r--r-- | src/world/agent.test.js | 49 | ||||
-rw-r--r-- | src/world/proposal.js | 2 |
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[] * } */ |