summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/genome/README.md12
-rw-r--r--src/genome/genome.js218
-rw-r--r--src/genome/genome.test.js282
-rw-r--r--src/genome/trial.js37
-rw-r--r--src/index.html14
-rw-r--r--src/mind/README.md37
-rw-r--r--src/mind/topology.js183
-rw-r--r--src/mind/topology.test.js235
-rw-r--r--src/simulation/actions.js260
-rw-r--r--src/simulation/actions.test.js294
-rw-r--r--src/simulation/game.js255
-rw-r--r--src/simulation/game.test.js59
-rw-r--r--src/simulation/lattice_rules.js77
-rw-r--r--src/simulation/lattice_rules.test.js147
-rw-r--r--src/simulation/postprocess.js18
-rw-r--r--src/simulation/postprocess.test.js37
-rw-r--r--src/simulation/senses.js182
-rw-r--r--src/simulation/senses.test.js499
-rw-r--r--src/simulation/trial.js12
-rw-r--r--src/simulation/validity.js14
-rw-r--r--src/simulation/validity.test.js43
-rw-r--r--src/ui/canvas.js11
-rw-r--r--src/ui/index.js75
-rw-r--r--src/util.js62
-rw-r--r--src/world/README.md53
-rw-r--r--src/world/agent.js56
-rw-r--r--src/world/agent.test.js49
-rw-r--r--src/world/lattice.js54
-rw-r--r--src/world/lattice.test.js111
-rw-r--r--src/world/proposal.js183
-rw-r--r--src/world/proposal.test.js197
-rw-r--r--src/world/sense.js17
-rw-r--r--src/world/sense.test.js33
-rw-r--r--src/world/world.js47
34 files changed, 0 insertions, 3863 deletions
diff --git a/src/genome/README.md b/src/genome/README.md
deleted file mode 100644
index 1135ed0..0000000
--- a/src/genome/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-src/genome
-==========
-
-Genomes represent the neural network that underlies a creature. They are an array of tuples of the form `[ source, sink, weight ]`, where:
-
- * `source` is an integer in the range `[0, N - num_outputs)`, representing the index of the source neuron
- * `sink` is an integer in the range `[num_inputs, N)`, representing the index of the sink neuron
- * `weight` is a floating-point value in the range `[-4, 4]`, representing the weight of the connection
-
-`num_input` and `num_output` are fixed by the environment (as they are the input senses and output actions of the creature, respectively).
-`N` is not fixed, but is instead determined by the maximum index present in the genome. As long as the maximum index is greater than or
-equal to `num_input + num_output`, the genome is considered valid.
diff --git a/src/genome/genome.js b/src/genome/genome.js
deleted file mode 100644
index 20974fc..0000000
--- a/src/genome/genome.js
+++ /dev/null
@@ -1,218 +0,0 @@
-'use strict';
-
-import { random_choice } from '../util.js';
-import { network } from '../mind/topology.js';
-
-
-// check if a given genome is valid and compute its size
-export function validate_genome(genome) {
- const { n_input, n_internal, n_output } = genome;
- console.log(n_input + n_internal);
- return genome.genes.reduce(
- (acc, [source, sink, weight]) => acc && (
- (source < n_input+n_internal) &&
- (sink >= n_input)
- ),
- true
- );
-}
-
-// parse a genome into a useable neural net
-export function parse_genome(genome) {
- const { n_input, n_internal, n_output } = genome;
-
- const n = genome.genes.reduce(
- (acc, [source, sink, weight]) => acc.connect(source, sink, weight),
- network(n_input, n_internal, n_output)
- );
-
- return n;
-}
-
-
-// --===== mutations =====--
-
-function clamp(value, min, max) {
- if (value > max) { return max; }
- if (value < min) { return min; }
- return value;
-}
-
-// adjust the source input of a gene
-export function mut_gene_source(n_input, n_internal, n_output, gene, r) {
- const [source, sink, weight] = gene;
-
- const new_source = r < 0.5 ? source-1 : source+1;
-
- return [
- clamp(new_source, 0, n_input+n_internal-1),
- sink,
- weight,
- ];
-}
-
-
-// adjust the sink target of a gene
-export function mut_gene_sink(n_input, n_internal, n_output, gene, r) {
- const [source, sink, weight] = gene;
-
- const new_sink = r < 0.5 ? sink-1 : sink+1;
-
- return [
- source,
- clamp(new_sink, n_input+n_internal, n_input+n_internal+n_output-1),
- weight,
- ];
-}
-
-
-// modify a gene's weight
-// only adjusts the weight by performing a weighted average, so as to
-// more gently modify the generated net
-export function mut_gene_weight(weight_max, gene, r) {
- const [source, sink, weight] = gene;
-
- const rr = (2*r)-1;
- const move = weight_max * rr;
- const new_weight = (2*weight + move)/3;
-
- return [
- source,
- sink,
- clamp(new_weight, -weight_max, weight_max),
- ];
-}
-
-
-
-// expand the size of the neural net encoded by the genome
-// relabels internal indices so that there is one extra internal neuron
-export function mut_genome_expand(genome, r) {
- const expand_index = Math.floor(genome.n_internal * r) + genome.n_input;
- const new_genes = genome.genes.map(([source, sink, weight]) => [
- source >= expand_index ? source+1 : source,
- sink >= expand_index ? sink+1 : sink,
- weight,
- ]);
-
- return {
- ...genome,
- n_internal: genome.n_internal+1,
- genes: new_genes,
- };
-}
-
-
-// contract the size of the neural net encoded by the genome
-// relabels internal indices so that there is one less internal neuron
-export function mut_genome_contract(genome, r) {
- const { n_input, n_internal, n_output } = genome;
- const contract_idx = Math.floor(n_internal * r) + n_input;
-
- // decrement sources on the contract index too, to prevent invalid genomes
- const new_source = (source) => source >= contract_idx ? source-1 : source;
- // decrement sinks only after the contract index
- const new_sink = (sink) => sink > contract_idx ? sink-1 : sink;
-
- const new_genes = genome.genes.map(([source, sink, weight]) => [
- new_source(source),
- new_sink(sink),
- weight,
- ]);
-
- return {
- ...genome,
- n_internal: n_internal-1,
- genes: new_genes,
- };
-}
-
-
-// append a newly generated gene to the end of the genome
-export function mut_genome_insert(genome, weight_max, r1=Math.random(), r2=Math.random(), r3=Math.random()) {
- const { n_input, n_internal, n_output } = genome;
- const source = Math.floor((n_input + n_internal) * r1);
- const sink = Math.floor((n_internal + n_output) * r2) + n_input;
- const weight = weight_max * ((2*r3)-1);
-
- return {
- ...genome,
- genes: [...genome.genes, [source, sink, weight]],
- };
-}
-
-
-// delete a gene from the genome
-export function mut_genome_delete(genome, r) {
- const del_idx = Math.floor(r * genome.genes.length);
- const genes = genome.genes.filter((_, idx) => idx != del_idx);
- return { ...genome, genes };
-}
-
-
-function mut_gene(
- [n_input, n_internal, n_output, genome],
- weight_max, r1, r2, r3
-) {
- const gene_idx = Math.floor(genome.length * r1);
- const mod = random_choice(['source', 'sink', 'weight'], r2);
- let new_gene;
- if (mod == 'source') {
- new_gene = mut_gene_source(
- n_input, n_internal, n_output,
- genome[gene_idx],
- r3
- );
- } else if (mod == 'sink') {
- new_gene = mut_gene_sink(
- n_input, n_internal, n_output,
- genome[gene_idx],
- r3
- );
- } else {
- new_gene = mut_gene_weight(
- weight_max, genome[gene_idx], r3
- );
- }
-
- const new_genome = genome.map((gene, idx) => {
- if (idx == gene_idx) { return new_gene; }
- return gene;
- });
-
- return [
- n_input, n_internal, n_output, new_genome
- ];
-}
-
-
-export function mutate_genome(obj, weight_max) {
- const mut = random_choice([
- 'gene', 'gene', 'gene',
- 'gene', 'gene', 'gene',
- 'gene', 'gene', 'gene',
- 'insert', 'delete',
- 'insert', 'delete',
- 'expand', 'contract',
- ], Math.random());
-
- if (mut == 'gene') {
- return mut_gene(
- obj, weight_max,
- Math.random(), Math.random(), Math.random()
- );
- } else if (mut == 'insert') {
- return mut_genome_insert(
- obj, weight_max,
- Math.random(), Math.random(), Math.random()
- );
- } else if (mut == 'delete') {
- return mut_genome_delete(obj, Math.random());
- } else if (mut == 'expand') {
- return mut_genome_expand(obj, Math.random());
- } else if (mut == 'contract') {
- return mut_genome_contract(obj, Math.random());
- } else {
- throw new Error(`bad mut value: ${mut}`);
- }
-}
diff --git a/src/genome/genome.test.js b/src/genome/genome.test.js
deleted file mode 100644
index bc64e4e..0000000
--- a/src/genome/genome.test.js
+++ /dev/null
@@ -1,282 +0,0 @@
-'use strict';
-
-// genome structure
-// {
-// genes: gene[]
-// n_input, n_internal, n_output
-// }
-
-
-import {
- validate_genome,
- parse_genome,
- mut_gene_source,
- mut_gene_sink,
- mut_gene_weight,
- mut_genome_expand,
- mut_genome_contract,
- mut_genome_insert,
- mut_genome_delete,
-} from './genome';
-
-
-test('genome validation', () => {
- expect(validate_genome({
- n_input: 0, n_internal: 1, n_output: 0,
- genes: [[0, 0, 1.0]],
- })).toBe(true);
- expect(validate_genome({
- n_input: 2, n_internal: 0, n_output: 1,
- genes: [[0, 2, 1]],
- })).toBe(true);
- expect(validate_genome({
- n_input: 2, n_internal: 0, n_output: 1,
- genes: [[2, 0, 1]],
- })).toBe(false);
- expect(validate_genome({
- n_input: 2, n_internal: 0, n_output: 1,
- genes: [[2, 2, 1]],
- })).toBe(false);
- expect(validate_genome({
- n_input: 2, n_internal: 1, n_output: 1,
- genes: [[3, 2, 1]],
- })).toBe(false);
-});
-
-
-test('parse a genome into a neural net', () => {
- const n = parse_genome({
- n_input: 1, n_internal: 1, n_output: 1,
- genes: [
- [0, 1, 1],
- [1, 1, 1],
- [1, 2, 1]
- ]
- });
-
- expect(n.input_count).toBe(1);
- expect(n.output_count).toBe(1);
- expect(n.compute([2], [-1])).toEqual([
- [ Math.tanh( Math.tanh( 2-1 ) ) ],
- [ Math.tanh( 2-1 ) ],
- ]);
-});
-
-
-test('mutate gene source', () => {
- const n_input = 3;
- const n_internal = 4;
- const n_output = 5;
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [0, 4, 0],
- 0.0
- )).toEqual([0, 4, 0]);
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [0, 4, 0],
- 1.0
- )).toEqual([1, 4, 0]);
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [6, 4, 0],
- 0.0
- )).toEqual([5, 4, 0]);
-
- expect(mut_gene_source(
- n_input, n_internal, n_output,
- [6, 4, 0],
- 1.0
- )).toEqual([6, 4, 0]);
-});
-
-
-test('mutate gene sink', () => {
- const n_input = 3;
- const n_internal = 4;
- const n_output = 5;
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [0, 7, 0],
- 0.0
- )).toEqual([0, 7, 0]);
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [0, 7, 0],
- 1.0
- )).toEqual([0, 8, 0]);
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [6, 11, 0],
- 0.0
- )).toEqual([6, 10, 0]);
-
- expect(mut_gene_sink(
- n_input, n_internal, n_output,
- [6, 11, 0],
- 1.0
- )).toEqual([6, 11, 0]);
-});
-
-
-test('mutate gene weight', () => {
- const weight_max = 4.0;
-
- expect(mut_gene_weight(
- weight_max, [0, 0, 1], 0.0
- )).toEqual([0, 0, (2 - 4)/3]);
-
- expect(mut_gene_weight(
- weight_max, [0, 0, -4], 1.0
- )).toEqual([0, 0, (-8 + 4)/3]);
-
- expect(mut_gene_weight(
- weight_max, [0, 0, 3], 0.5
- )).toEqual([0, 0, (6+0)/3]);
-});
-
-
-test('expand genome', () => {
- const n_input = 1;
- const n_internal = 3;
- const n_output = 1;
-
- const genome = {
- n_input, n_internal, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 0],
- [2, 3, 0],
- [3, 4, 0],
- ],
- };
-
- expect(mut_genome_expand(genome, 0.0)).toEqual({
- n_input, n_internal: n_internal+1, n_output,
- genes: [
- [0, 2, 0],
- [2, 3, 0],
- [3, 4, 0],
- [4, 5, 0],
- ],
- });
-
- expect(mut_genome_expand(genome, 0.5)).toEqual({
- n_input, n_internal: n_internal+1, n_output,
- genes: [
- [0, 1, 0],
- [1, 3, 0],
- [3, 4, 0],
- [4, 5, 0],
- ],
- });
-
- expect(mut_genome_expand(genome, 0.99)).toEqual({
- n_input, n_internal: n_internal+1, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 0],
- [2, 4, 0],
- [4, 5, 0],
- ],
- });
-});
-
-
-test('contract genome', () => {
- const n_input = 1;
- const n_internal = 3;
- const n_output = 1;
-
- const genome = {
- n_input, n_internal, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 1],
- [2, 3, 2],
- [3, 4, 3],
- ],
- };
-
- expect(mut_genome_contract(genome, 0.0)).toEqual({
- n_input, n_internal: n_internal-1, n_output,
- genes: [
- [0, 1, 0],
- [0, 1, 1],
- [1, 2, 2],
- [2, 3, 3],
- ],
- });
-
- expect(mut_genome_contract(genome, 0.5)).toEqual({
- n_input, n_internal: n_internal-1, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 1],
- [1, 2, 2],
- [2, 3, 3],
- ],
- });
-
- expect(mut_genome_contract(genome, 0.99)).toEqual({
- n_input, n_internal: n_internal-1, n_output,
- genes: [
- [0, 1, 0],
- [1, 2, 1],
- [2, 3, 2],
- [2, 3, 3],
- ],
- });
-});
-
-
-
-test('insert new genes', () => {
- const n_input = 1;
- const n_internal = 2;
- const n_output = 1;
- const weight_max = 4;
-
- expect(mut_genome_insert({
- n_input, n_internal, n_output,
- genes: []
- }, weight_max, 0, 0.5, 0)).toEqual({
- n_input, n_internal, n_output,
- genes: [[0, 2, -4]]
- });
-
- expect(mut_genome_insert({
- n_input, n_internal, n_output,
- genes: [[0, 2, -4]]
- }, weight_max, 0.99, 0, 1)).toEqual({
- n_input, n_internal, n_output,
- genes: [[0, 2, -4], [2, 1, 4]]
- });
-});
-
-
-test('remove genes', () => {
- const n_input = 0;
- const n_output = 0;
- const n_internal = 3;
- const genome = {
- n_input, n_internal, n_output,
- genes: [[0, 1, 0], [1, 2, 0]],
- };
-
- expect(mut_genome_delete(genome, 0.0)).toEqual({
- n_input, n_internal, n_output,
- genes: [[1, 2, 0]],
- });
-
- expect(mut_genome_delete(genome, 0.99)).toEqual({
- n_input, n_internal, n_output,
- genes: [[0, 1, 0]],
- });
-});
diff --git a/src/genome/trial.js b/src/genome/trial.js
deleted file mode 100644
index 2ce23bf..0000000
--- a/src/genome/trial.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {
- get_size,
- mut_genome_insert, mutate_genome,
-} from './genome.js';
-
-const recurse = (f, x0, n) => {
- if (n == 0) {
- return x0;
- } else {
- return f(recurse(f, x0, n-1));
- }
-};
-
-
-const n_input = 5;
-const n_output = 5;
-
-
-const [_1, _2, _3, genome] = recurse(
- s => mut_genome_insert(
- s, 4,
- Math.random(), Math.random(), Math.random()
- ),
- [n_input, 10, n_output, []],
- 20);
-
-
-const n_internal = get_size(n_input, n_output, genome) - n_input - n_output;
-console.log([n_input, n_internal, n_output, genome]);
-
-const mutation = recurse(
- s => mutate_genome(s, 4),
- [n_input, n_internal, n_output, genome],
- 40
-);
-
-console.log(mutation);
diff --git a/src/index.html b/src/index.html
deleted file mode 100644
index 6b7ebf7..0000000
--- a/src/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!doctype html>
-<html>
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>nerine</title>
- <style>
- </style>
- <script type="module" src="ui/index.js"></script>
- </head>
- <body>
- <canvas id="canvas" width="95vw" height="95vw">
- </body>
-</html>
diff --git a/src/mind/README.md b/src/mind/README.md
deleted file mode 100644
index 1ece125..0000000
--- a/src/mind/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-mind
-====
-
-This module is used to create arbitrary stateful neural networks.
-
-The only export is the following function:
-
-```
-network(input_count, internal_count, output_count, weight_max = 4 : number)
-```
-
-This function returns an object that represents a neural network with `input_count` input neurons,
-`internal_count` internal (and stateful) neurons, and `output_count` output neurons. `max_weight` determines
-the maximum absolute value allowed for connection weights.
-
-A network object has two methods:
-
-```
-connect(source, sink, weight : number)
-```
-
-This method returns a new network object that is an exact copy of the original, except with a new
-connection between the neuron indexed by `source` and `sink`, with weight `weight`. Neuron indices
-are zero-indexed, and span all neurons, first the inputs, then the internal neurons, and finally the outputs.
-An error will be thrown if `source` is in the range of output neurons or if `sink` is in the range of input
-neurons.
-
-
-```
-compute(inputs, state : array[number])
-```
-
-This method returns a tuple `[output, newState]`, where `output` is an array of `output_count` values
-corresponding to the output neuron's computed values, and `newState` is the new state of the internal neurons.
-
-`input` must be an array of numbers with length equal to `input_count`, and `state` must be an array of numbers
-with length equal to `internal_count` or an error will be raised.
diff --git a/src/mind/topology.js b/src/mind/topology.js
deleted file mode 100644
index 946dd86..0000000
--- a/src/mind/topology.js
+++ /dev/null
@@ -1,183 +0,0 @@
-'use strict';
-
-import { create } from '../util.js';
-
-
-const DEFAULT_WEIGHT_MAX = 4;
-
-
-// prototype for network objects
-const network_proto = {
- connect: function(source, sink, weight) {
- return network_connect(this, source, sink, weight);
- },
- compute: function(inputs, state) {
- return network_compute(this, inputs, state);
- },
-};
-
-
-// create a new network
-export function network(input_count, internal_count, output_count, weight_max = 4) {
- const count = input_count + internal_count + output_count;
- const n = create({
- input_count,
- output_count,
- adjacency: new Array(count).fill([]),
- weight: [],
- }, network_proto);
- return n;
-}
-
-
-// check index is an input
-function is_input(n, index) {
- return index < n.input_count;
-}
-// check if index is an output
-function is_output(n, index) {
- return index >= (n.adjacency.length - n.output_count);
-}
-// check if index is a hidden neuron
-function is_hidden(n, index) {
- return (!is_input(n, index)) && (!is_output(n, index));
-}
-
-
-// returns a new network with an edge between the given nodes
-// with the given 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 (${source} -> ${sink})`);
- }
- if (is_output(n, source)) {
- // outputs cannot be sources
- throw new Error(`attempt to use output as source (${source} -> ${sink})`);
- }
-
- return create({
- ...n,
- adjacency: n.adjacency.map((row, i) => {
- if (i === source && i === sink) {
- // self-loop
- return [...row, 2];
- } else if (i === source) {
- return [...row, 1];
- } else if (i === sink) {
- return [...row, -1];
- } else {
- return [...row, 0];
- }
- }),
- weight: [...n.weight, weight],
- }, network_proto);
-}
-
-
-// gets the indices of the edges incident on the given adjacency list
-function incident_edges(n, adj) {
- const incident = adj
- .map((edge, index) => (edge < 0) || (edge === 2) ? index : null)
- .filter(index => index !== null);
-
- return incident;
-}
-
-
-// get the indices of the ends of an edge
-// in the case of self-loops, both values are the same
-function edge_ends(n, edge) {
- const ends = n.adjacency
- .map((adj, index) => adj[edge] !== 0 ? index : null)
- .filter(index => index != null);
-
- ends.sort((a, b) => n.adjacency[a][edge] < n.adjacency[b][edge] ? -1 : 1);
-
- if (ends.length === 1) {
- return { source: ends[0], sink: ends[0] };
- } else if (ends.length === 2) {
- return { source: ends[1], sink: ends[0] };
- } else {
- throw new Error("something bad happened with the ends");
- }
-}
-
-
-// recursively get the value of a node from the input nodes,
-// optionally caching the computed values
-function get_value(n, index, input, prev, cache) {
- // check if value is cached
- if (cache !== undefined && cache[index]) {
- return cache[index];
- }
- // check if value is input
- if (is_input(n, index)) {
- return input[index];
- }
-
- const adj = n.adjacency[index]; // get adjacency list
- const incident = incident_edges(n, adj); // get incident edges
- const weight = incident.map(x => n.weight[x]); // edge weights
- const sources = incident // get ancestor nodes
- .map(x => edge_ends(n, x).source);
-
- // get the value of each ancestor
- const values = sources
- .map(x => x === index // if the ancestor is this node
- ? prev[x - n.input_count] // then the value is the previous value
- : get_value(n, x, input, prev, cache)); // else recurse
-
- const sum = values // compute the weighted sum of the values
- .reduce((acc, x, i) => acc + (weight[i] * x), 0);
-
- if (sum !== sum) { // NaN test
- console.log(n);
- console.log(sources);
- console.log(input);
- throw new Error(`failed to get output for index ${index}`);
- }
-
- // compute result
- const value = Math.tanh(sum);
-
- // !!! impure caching !!!
- // cache result
- if (cache !== undefined) {
- cache[index] = value;
- }
-
- return value;
-}
-
-
-// compute a network's output and new hidden state
-// given the input and previous hidden state
-export function network_compute(n, input, state) {
- // validate input
- if (input.length !== n.input_count) {
- throw new Error("incorrect number of input elements");
- }
- // validate state
- const hidden_count = n.adjacency.length - n.input_count - n.output_count;
- if (state.length !== hidden_count) {
- throw new Error("incorrect number of state elements");
- }
-
- // !!! impure caching !!!
- const value_cache = {};
-
- const result = Object.freeze(n.adjacency
- .map((x, i) => is_output(n, i) ? i : null) // output index or null
- .filter(i => i !== null) // remove nulls
- .map(x => get_value(n, x, input, state, value_cache)) // map to computed value
- );
-
- const newstate = Object.freeze(n.adjacency
- .map((x, i) => is_hidden(n, i) ? i : null) // hidden index or null
- .filter(i => i !== null) // remove nulls
- .map(x => get_value(n, x, input, state, value_cache)) // map to computed value (using cache)
- );
-
- return Object.freeze([result, newstate]);
-}
diff --git a/src/mind/topology.test.js b/src/mind/topology.test.js
deleted file mode 100644
index 52c196f..0000000
--- a/src/mind/topology.test.js
+++ /dev/null
@@ -1,235 +0,0 @@
-'use strict';
-
-import { network } from './topology';
-
-
-test('basic network functionality', () => {
- const n = network(0, 5, 0);
- expect(n).toEqual({
- input_count: 0,
- output_count: 0,
- adjacency: [ [], [], [], [], [] ],
- weight: [],
- });
-
- expect(() => n.adjacency = []).toThrow();
- expect(() => n.weight = []).toThrow();
-
- const nn = n.connect(0, 1, -2);
- expect(nn).toEqual({
- input_count: 0,
- output_count: 0,
- adjacency: [
- [ 1 ],
- [ -1 ],
- [ 0 ],
- [ 0 ],
- [ 0 ]
- ],
- weight: [ -2 ],
- });
-
- expect(() => nn.adjacency = []).toThrow();
- expect(() => nn.weight = []).toThrow();
-
- const nnn = nn.connect(2, 4, 3);
- expect(nnn).toEqual({
- input_count: 0,
- output_count: 0,
- adjacency: [
- [ 1, 0 ],
- [ -1, 0 ],
- [ 0, 1 ],
- [ 0, 0 ],
- [ 0, -1 ]
- ],
- weight: [ -2, 3 ],
- });
-
- expect(() => nnn.adjacency = []).toThrow();
- expect(() => nnn.weight = []).toThrow();
-});
-
-
-test(
-'networks are restricted from sinking to inputs or sourcing from outputs',
-() => {
- const n = network(2, 2, 2);
-
- expect(n.connect(1,2,0)).toEqual({
- input_count: 2,
- output_count: 2,
- adjacency: [
- [ 0 ],
- [ 1 ],
- [ -1 ],
- [ 0 ],
- [ 0 ],
- [ 0 ],
- ],
- weight: [ 0 ],
- });
- expect(() => n.connect(2, 1, 0)).toThrow();
-
- expect(n.connect(3, 4, 2)).toEqual({
- input_count: 2,
- output_count: 2,
- adjacency: [
- [ 0 ],
- [ 0 ],
- [ 0 ],
- [ 1 ],
- [ -1 ],
- [ 0 ],
- ],
- weight: [ 2 ],
- });
- expect(() => n.connect(4, 3, 2)).toThrow();
-});
-
-
-test('self-connections work correctly', () => {
- const n = network(0, 1, 0).connect(0, 0, 2.0);
- expect(n).toEqual({
- input_count: 0,
- output_count: 0,
- adjacency: [
- [ 2 ],
- ],
- weight: [ 2 ],
- });
-});
-
-
-test('network computations', () => {
- const n = network(1, 0, 1).connect(0, 1, 2.0);
- const input = [ -0.5 ];
- const state = [];
- const result = n.compute(input, state);
- expect(result).toEqual([
- [ Math.tanh(-0.5 * 2.0) ],
- [],
- ]);
-
- expect(input).toEqual([ -0.5 ]);
- expect(state).toEqual([]);
-
- expect(() => result[0] = 'hi').toThrow();
- expect(() => result[0].push('hi')).toThrow();
- expect(() => result[1] = 'hi').toThrow();
- expect(() => result[1].push('hi')).toThrow();
-});
-
-
-test('multiple input network', () => {
- const n = network(4, 0, 1)
- .connect(0, 4, -1.0)
- .connect(1, 4, -2.0)
- .connect(2, 4, 1.0)
- .connect(3, 4, 2.0)
-
- expect(n.compute([1, 2, 3, 5], [])).toEqual([
- [ Math.tanh(
- (-1.0 * 1) +
- (-2.0 * 2) +
- (1.0 * 3) +
- (2.0 * 5))],
- [],
- ]);
-});
-
-
-test('multiple outputs', () => {
- const n = network(4, 0, 2)
- .connect(0, 4, -1)
- .connect(1, 4, 1)
- .connect(2, 5, -1)
- .connect(3, 5, 1);
-
- expect(n.compute([1,2,3,5], [])).toEqual([
- [ Math.tanh(2-1), Math.tanh(5-3) ],
- [],
- ]);
-});
-
-
-test('hidden neurons', () => {
- const n = network(4, 2, 1)
- .connect(0, 4, -1)
- .connect(1, 4, 1)
- .connect(2, 5, -1)
- .connect(3, 5, 1)
- .connect(4, 6, -1)
- .connect(5, 6, 1);
-
- expect(n.compute([1,2,3,5], [ 0, 0 ])).toEqual([
- [ Math.tanh( Math.tanh(5-3) - Math.tanh(2-1) ) ],
- [ Math.tanh(2-1), Math.tanh(5-3) ],
- ]);
-});
-
-
-test('arbitrary hidden neurons', () => {
- const n = network(1, 2, 1)
- .connect(0, 1, 1)
- .connect(1, 2, -1)
- .connect(2, 3, 2)
-
- const [output, state] = n.compute([1], [0, 0]);
-
- expect(output).toEqual([
- Math.tanh(
- 2*Math.tanh(
- -1*Math.tanh(1)
- )
- )
- ]);
-
- expect(state).toEqual([
- Math.tanh(1),
- Math.tanh( -Math.tanh(1) ),
- ]);
-});
-
-
-test('memory', () => {
- const n = network(0, 1, 1).connect(0, 0, -0.5).connect(0, 1, 2);
-
- expect(n.compute([], [1])).toEqual([
- [ Math.tanh( 2 * Math.tanh( -0.5 * 1 ) ) ],
- [ Math.tanh( -0.5 * 1) ],
- ]);
-});
-
-
-test('memory and input', () => {
- const n = network(1, 1, 1)
- .connect(0, 1, 1)
- .connect(1, 1, 1)
- .connect(1, 2, 1);
-
- expect(n.compute([2], [-1])).toEqual([
- [ Math.tanh( Math.tanh( 2-1 ) ) ],
- [ Math.tanh( 2-1 ) ],
- ]);
-});
-
-
-test('input and state must be the correct size', () => {
- const n = network(2, 1, 1)
- .connect(0, 2, 1)
- .connect(1, 2, 1)
- .connect(2, 3, 1);
-
- // wrong input size
- expect(() => n.compute([], [4])).toThrow();
- expect(() => n.compute([1], [4])).toThrow();
- expect(() => n.compute([1, 1, 1], [4])).toThrow();
-
- // wrong state size
- expect(() => n.compute([1, 1], [])).toThrow();
- expect(() => n.compute([1, 1], [4, 4])).toThrow();
-
- // prove correct sizes work
- n.compute([1, 1], [4]);
-});
diff --git a/src/simulation/actions.js b/src/simulation/actions.js
deleted file mode 100644
index ad08572..0000000
--- a/src/simulation/actions.js
+++ /dev/null
@@ -1,260 +0,0 @@
-'use strict';
-
-const threshold = 0.5;
-
-
-const move_forward = {
- size: 1,
- propose: (world, agent, head) => {
- if (agent.flags.frozen === true) { return []; }
- if (head[0] > threshold) {
- console.log('move forward');
- console.log(agent.id, agent.x, agent.y);
- const dx = { n: 0, e: 1, s: 0, w: -1 }[agent.flags.orientation];
- const dy = { n: -1, e: 0, s: 1, w: 0 }[agent.flags.orientation];
- return [{
- agent_changes: [{
- agent_id: agent.id,
- x: agent.x + dx,
- y: agent.y + dy,
- }],
- }];
- } else {
- return [];
- }
- },
-};
-
-
-const move_backward = {
- size: 1,
- propose: (world, agent, head) => {
- if (agent.flags.frozen === true) { return []; }
- if (head[0] > threshold) {
- console.log('move backward');
- const dx = { n: 0, e: 1, s: 0, w: -1 }[agent.flags.orientation];
- const dy = { n: -1, e: 0, s: 1, w: 0 }[agent.flags.orientation];
- return [{
- agent_changes: [{
- agent_id: agent.id,
- x: agent.x - dx,
- y: agent.y - dy,
- }],
- }];
- } else {
- return [];
- }
- },
-};
-
-
-const turn_left = {
- size: 1,
- propose: (world, agent, head) => {
- if (agent.flags.frozen === true) { return []; }
- if (head[0] > threshold) {
- console.log('turn left');
- const orientation = { n: 'w', e: 'n', s: 'e', w: 's' }[agent.flags.orientation];
- return [{
- agent_changes: [{
- agent_id: agent.id,
- flags: { orientation },
- }],
- }];
- } else {
- return [];
- }
- },
-};
-
-
-const turn_right = {
- size: 1,
- propose: (world, agent, head) => {
- if (agent.flags.frozen === true) { return []; }
- if (head[0] > threshold) {
- console.log('turn right');
- const orientation = { n: 'e', e: 's', s: 'w', w: 'n' }[agent.flags.orientation];
- return [{
- agent_changes: [{
- agent_id: agent.id,
- flags: { orientation },
- }],
- }];
- } else {
- return [];
- }
- },
-};
-
-
-const place = {
- size: 1,
- propose: (world, agent, head) => {
- if (head[0] < threshold) { return []; }
- else {
- const dx = { n: 0, e: 1, s: 0, w: -1 }[agent.flags.orientation];
- const dy = { n: -1, e: 0, s: 1, w: 0 }[agent.flags.orientation];
- return [
- { lattice_changes: [{
- x: agent.x + dx, y: agent.y + dy,
- from: 'empty', to: 'mutable',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] },
- }]}
- ];
- }
- },
-};
-
-
-const trigger = {
- size: 1,
- propose: (world, agent, head) => {
- if (head[0] < threshold) { return []; }
- else {
- const dx = { n: 0, e: 1, s: 0, w: -1 }[agent.flags.orientation];
- const dy = { n: -1, e: 0, s: 1, w: 0 }[agent.flags.orientation];
- return [
- { lattice_changes: [{
- x: agent.x + dx, y: agent.y + dy,
- from: 'mutable', to: 'active',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] },
- }]}
- ];
- }
- },
-};
-
-
-const pretend_frozen = {
- size: 1,
- propose: (world, agent, head) => {
- if (head[0] < threshold) { return [{
- agent_changes: [{
- agent_id: agent.id,
- flags: { pretend_frozen: false },
- }],
- }]; }
- else {
- return [{
- agent_changes: [{
- agent_id: agent.id,
- flags: { pretend_frozen: true },
- }]
- }];
- }
- },
-};
-
-
-const unfreeze = {
- size: 1,
- propose: (world, agent, head) => {
- if (head[0] < threshold) { return []; }
- else {
- const dx = { n: 0, e: 1, s: 0, w: -1 }[agent.flags.orientation];
- const dy = { n: -1, e: 0, s: 1, w: 0 }[agent.flags.orientation];
- const target = world.agents.filter(
- a => a.x === agent.x+dx && a.y === agent.y+dy
- )[0];
- if (target === undefined) { return []; }
- return [
- { agent_changes: [{
- agent_id: target.id,
- flags: { frozen: false, emit: [0, 1, 0, 0, 0, 0, 0, 0] },
- }]}
- ];
- }
- },
-};
-
-
-const take_flag = {
- size: 1,
- propose: (world, agent, head) => {
- if (head[0] < threshold) { return []; }
- else {
- const dx = { n: 0, e: 1, s: 0, w: -1 }[agent.flags.orientation];
- const dy = { n: -1, e: 0, s: 1, w: 0 }[agent.flags.orientation];
- const target = world.agents.filter(
- a => a.x === agent.x+dx && a.y === agent.y+dy
- )[0];
- if (target === undefined || !target.flags.flag) {
- return [
- {
- lattice_changes: [{
- x: agent.x + dx, y: agent.y + dy,
- from: 'flag', to: 'empty',
- flags: { emit: [ 0, 0, 1, 0, 0, 0, 0, 0 ] },
- }],
- agent_changes: [{
- agent_id: agent.id,
- flags: { flag: true },
- }],
- }
- ];
- } else {
- return [{
- agent_changes: [
- {
- agent_id: target.id,
- flags: { flag: false },
- },
- {
- agent_id: agent.id,
- flags: { flag: true },
- },
- ],
- }];
- }
- }
- },
-};
-
-
-const drop_flag = {
- size: 1,
- propose: (world, agent, head) => {
- if (head[0] < threshold) { return []; }
- else if (!agent.flags.flag) { return []; }
- else {
- const dx = { n: 0, e: 1, s: 0, w: -1 }[agent.flags.orientation];
- const dy = { n: -1, e: 0, s: 1, w: 0 }[agent.flags.orientation];
- return [
- {
- lattice_changes: [{
- x: agent.x + dx, y: agent.y + dy,
- from: 'empty', to: 'flag',
- flags: { emit: [ 0, 0, -1, 0, 0, 0, 0, 0 ] },
- }],
- agent_changes: [{
- agent_id: agent.id,
- flags: { flag: false },
- }],
- }
- ];
- }
- },
-};
-
-
-const speak = {
- size: 8,
- propose: (world, agent, head) => {
- return [
- {
- agent_changes: [{
- agent_id: agent.id,
- flags: { emit: head },
- }],
- },
- ];
- },
-};
-
-
-export const actions = [
- move_forward, /*move_backward, */turn_left, turn_right,
- place, trigger, pretend_frozen, unfreeze, take_flag, drop_flag,
- speak,
-];
diff --git a/src/simulation/actions.test.js b/src/simulation/actions.test.js
deleted file mode 100644
index e8406bc..0000000
--- a/src/simulation/actions.test.js
+++ /dev/null
@@ -1,294 +0,0 @@
-'use strict';
-
-import { actions } from './actions.js';
-
-
-const [
- move_forward, move_backward,
- turn_left, turn_right,
- place, trigger,
- pretend_frozen, unfreeze,
- take_flag,
- ...rest
-] = actions;
-
-
-test("move forward", () => {
- const n = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const s = { id: 0, x: 0, y: 0, flags: { orientation: 's' } };
- const e = { id: 0, x: 0, y: 0, flags: { orientation: 'e' } };
- const w = { id: 0, x: 0, y: 0, flags: { orientation: 'w' } };
-
- expect(move_forward.propose(null, n, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: 0, y: -1 }] },
- ]);
- expect(move_forward.propose(null, s, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: 0, y: 1 }] },
- ]);
- expect(move_forward.propose(null, e, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: 1, y: 0 }] },
- ]);
- expect(move_forward.propose(null, w, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: -1, y: 0 }] },
- ]);
-
- expect(move_forward.propose(null, n, [0])).toEqual([]);
- expect(move_forward.propose(null, s, [-1])).toEqual([]);
- expect(move_forward.propose(null, e, [0])).toEqual([]);
- expect(move_forward.propose(null, w, [-1])).toEqual([]);
-});
-
-
-test("move backward", () => {
- const n = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const s = { id: 0, x: 0, y: 0, flags: { orientation: 's' } };
- const e = { id: 0, x: 0, y: 0, flags: { orientation: 'e' } };
- const w = { id: 0, x: 0, y: 0, flags: { orientation: 'w' } };
-
- expect(move_backward.propose(null, n, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: 0, y: 1 }] },
- ]);
- expect(move_backward.propose(null, s, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: 0, y: -1 }] },
- ]);
- expect(move_backward.propose(null, e, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: -1, y: 0 }] },
- ]);
- expect(move_backward.propose(null, w, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, x: 1, y: 0 }] },
- ]);
-
- expect(move_backward.propose(null, n, [0])).toEqual([]);
- expect(move_backward.propose(null, s, [-1])).toEqual([]);
- expect(move_backward.propose(null, e, [0])).toEqual([]);
- expect(move_backward.propose(null, w, [-1])).toEqual([]);
-});
-
-
-test("turn_left", () => {
- const n = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const s = { id: 0, x: 0, y: 0, flags: { orientation: 's' } };
- const e = { id: 0, x: 0, y: 0, flags: { orientation: 'e' } };
- const w = { id: 0, x: 0, y: 0, flags: { orientation: 'w' } };
-
- expect(turn_left.propose(null, n, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 'w' } }] },
- ]);
- expect(turn_left.propose(null, s, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 'e' } }] },
- ]);
- expect(turn_left.propose(null, e, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 'n' } }] },
- ]);
- expect(turn_left.propose(null, w, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 's' } }] },
- ]);
-
- expect(turn_left.propose(null, n, [0])).toEqual([]);
- expect(turn_left.propose(null, s, [-1])).toEqual([]);
- expect(turn_left.propose(null, e, [0])).toEqual([]);
- expect(turn_left.propose(null, w, [-1])).toEqual([]);
-});
-
-
-test("turn_right", () => {
- const n = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const s = { id: 0, x: 0, y: 0, flags: { orientation: 's' } };
- const e = { id: 0, x: 0, y: 0, flags: { orientation: 'e' } };
- const w = { id: 0, x: 0, y: 0, flags: { orientation: 'w' } };
-
- expect(turn_right.propose(null, n, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 'e' } }] },
- ]);
- expect(turn_right.propose(null, s, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 'w' } }] },
- ]);
- expect(turn_right.propose(null, e, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 's' } }] },
- ]);
- expect(turn_right.propose(null, w, [1])).toEqual([
- { agent_changes: [{ agent_id: 0, flags: { orientation: 'n' } }] },
- ]);
-
- expect(turn_right.propose(null, n, [0])).toEqual([]);
- expect(turn_right.propose(null, s, [-1])).toEqual([]);
- expect(turn_right.propose(null, e, [0])).toEqual([]);
- expect(turn_right.propose(null, w, [-1])).toEqual([]);
-});
-
-
-test("place", () => {
- const n = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const s = { id: 0, x: 0, y: 0, flags: { orientation: 's' } };
- const e = { id: 0, x: 0, y: 0, flags: { orientation: 'e' } };
- const w = { id: 0, x: 0, y: 0, flags: { orientation: 'w' } };
-
- expect(place.propose(null, n, [1])).toEqual([
- { lattice_changes: [
- {
- x: 0, y: -1, from: 'empty', to: 'mutable',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] }
- }
- ]}
- ]);
- expect(place.propose(null, s, [1])).toEqual([
- { lattice_changes: [
- {
- x: 0, y: 1, from: 'empty', to: 'mutable',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] }
- }
- ]}
- ]);
- expect(place.propose(null, e, [1])).toEqual([
- { lattice_changes: [
- {
- x: 1, y: 0, from: 'empty', to: 'mutable',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] }
- }
- ]}
- ]);
- expect(place.propose(null, w, [1])).toEqual([
- { lattice_changes: [
- {
- x: -1, y: 0, from: 'empty', to: 'mutable',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] }
- }
- ]}
- ]);
-
- expect(place.propose(null, n, [0])).toEqual([]);
- expect(place.propose(null, s, [-1])).toEqual([]);
- expect(place.propose(null, e, [0])).toEqual([]);
- expect(place.propose(null, w, [-1])).toEqual([]);
-});
-
-
-test("trigger", () => {
- const n = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const s = { id: 0, x: 0, y: 0, flags: { orientation: 's' } };
- const e = { id: 0, x: 0, y: 0, flags: { orientation: 'e' } };
- const w = { id: 0, x: 0, y: 0, flags: { orientation: 'w' } };
-
- expect(trigger.propose(null, n, [1])).toEqual([
- { lattice_changes: [{
- x: 0, y: -1, from: 'mutable', to: 'active',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] },
- }]},
- ]);
- expect(trigger.propose(null, s, [1])).toEqual([
- { lattice_changes: [{
- x: 0, y: 1, from: 'mutable', to: 'active',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] },
- }]},
- ]);
- expect(trigger.propose(null, e, [1])).toEqual([
- { lattice_changes: [{
- x: 1, y: 0, from: 'mutable', to: 'active',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] },
- }]},
- ]);
- expect(trigger.propose(null, w, [1])).toEqual([
- { lattice_changes: [{
- x: -1, y: 0, from: 'mutable', to: 'active',
- flags: { emit: [1, 0, 0, 0, 0, 0, 0, 0] },
- }]},
- ]);
-
- expect(trigger.propose(null, n, [0])).toEqual([]);
- expect(trigger.propose(null, s, [-1])).toEqual([]);
- expect(trigger.propose(null, e, [0])).toEqual([]);
- expect(trigger.propose(null, w, [-1])).toEqual([]);
- });
-
-
-test("pretend frozen", () => {
- const agent = { id: 2, x: 0, y: 0, flags: { orientation: 'n' } };
-
- expect(pretend_frozen.propose(null, agent, [1])).toEqual([{
- agent_changes: [{
- agent_id: 2,
- flags: { pretend_frozen: true },
- }]
- }]);
-
- expect(pretend_frozen.propose(null, agent, [0])).toEqual([{
- agent_changes: [{
- agent_id: 2,
- flags: { pretend_frozen: false },
- }]
- }]);
-});
-
-
-test("unfreeze", () => {
- const agent1 = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const agent2 = { id: 1, x: 0, y: -1 };
- const agent3 = { id: 10, x: -1, y: 0 };
- const agents = [ agent1, agent2, agent3 ];
-
- expect(unfreeze.propose({agents}, agent1, [1])).toEqual([{
- agent_changes: [{
- agent_id: 1,
- flags: { frozen: false, emit: [ 0, 1, 0, 0, 0, 0, 0, 0 ] },
- }]
- }]);
-
- agent1.flags.orientation = 'w';
-
- expect(unfreeze.propose({agents}, agent1, [1])).toEqual([{
- agent_changes: [{
- agent_id: 10,
- flags: { frozen: false, emit: [ 0, 1, 0, 0, 0, 0, 0, 0 ] },
- }]
- }]);
-
- agent1.flags.orientation = 's';
- expect(unfreeze.propose({agents}, agent1, [1])).toEqual([]);
-
- expect(unfreeze.propose({agents}, agent1, [0])).toEqual([]);
-});
-
-
-test("take flag", () => {
- const agent = { id: 0, x: 0, y: 0, flags: { orientation: 'n' } };
- const other1 = { id: 1, x: 0, y: -1, flags: { flag: true } };
- const other2 = { id: 2, x: 0, y: 1, flags: { flag: false } };
-
- const world = { agents: [ agent, other1, other2 ] };
-
- expect(take_flag.propose(world, agent, [1])).toEqual([
- { agent_changes: [
- { agent_id: 1, flags: { flag: false } },
- { agent_id: 0, flags: { flag: true } },
- ]},
- ]);
-
- agent.flags.orientation = 's';
- expect(take_flag.propose(world, agent, [1])).toEqual([
- {
- lattice_changes: [
- {
- x: 0, y: 1, from: 'flag', to: 'empty',
- flags: { emit: [ 0, 0, 1, 0, 0, 0, 0, 0 ] },
- },
- ],
- agent_changes: [
- { agent_id: 0, flags: { flag: true } },
- ]
- },
- ]);
-
- expect(take_flag.propose(world, agent, [0])).toEqual([]);
-});
-
-
-test("frozen agents cannot move", () => {
- const agent = { id: 0, x: 0, y: 0, flags: { orientation: 'n', frozen: true } };
-
- const world = { agents: [agent] };
-
- expect(move_forward.propose(world, agent, [1])).toEqual([]);
- expect(move_backward.propose(world, agent, [1])).toEqual([]);
- expect(turn_left.propose(world, agent, [1])).toEqual([]);
- expect(turn_right.propose(world, agent, [1])).toEqual([]);
-});
diff --git a/src/simulation/game.js b/src/simulation/game.js
deleted file mode 100644
index 978289d..0000000
--- a/src/simulation/game.js
+++ /dev/null
@@ -1,255 +0,0 @@
-'use strict';
-
-
-import { random_choice, apply, shuffle } from '../util.js';
-import { mut_genome_insert, parse_genome } from '../genome/genome.js';
-import { world_update } from '../world/world.js';
-import { senses } from './senses.js';
-import { actions } from './actions.js';
-import { lattice_rules } from './lattice_rules.js';
-import { validity } from './validity.js';
-import { postprocess } from './postprocess.js';
-
-
-function is_wall(size, x, y) {
- return (
- x === 0 || x === size-1 ||
- y === 0 || y === size-1
- );
-}
-function is_corner(size, x, y) {
- const subsize = Math.floor(size/3);
- return (
- (x < subsize || x >= 2*subsize) &&
- (y < subsize || y >= 2*subsize)
- );
-}
-export function get_team(size, x, y) {
- const subsize = Math.floor(size/3);
- if (y < subsize) {
- return 0;
- } else if (x >= 2*subsize) {
- return 1;
- } else if (y >= 2*subsize) {
- return 2;
- } else if (x < subsize) {
- return 3;
- } else {
- return undefined;
- }
-}
-
-export function setup_board(size) {
- const lattice = [...Array(size)]
- .map(() => [...Array(size)])
- .map((row, y) => row.map((_, x) => {
- if (is_wall(size, x, y)) {
- return { type: 'immutable', flags: {} };
- } else if (is_corner(size, x, y)) {
- return { type: 'immutable', flags: {} };
- } else {
- const team = get_team(size, x, y);
- if (Math.random() > 0.95 && get_team(size, x, y) === undefined) {
- return { type: 'flag', flags: { team } };
- } else {
- return { type: 'empty', flags: { team } };
- }
- }
- }));
- return lattice;
-}
-
-
-export function create_world(size, teams) {
- const lattice = setup_board(size);
-
- const agents = teams.reduce(
- (agents, team, team_num) => {
- const team_cells = lattice.map((row, y) => row.map((cell, x) => [x, y, cell])).flat()
- // only check cells with the right team
- .filter(([x, y, cell]) => cell.type === 'empty' && cell.flags.team === team_num)
- return agents.concat(team.reduce(
- (acc, agent) => {
- const available_cells = team_cells.filter(([x, y, cell]) => acc.reduce(
- (occupied, a) => occupied && ((a.x !== x) || (a.y !== y)),
- true
- ))
- const [x, y, ..._] = random_choice(available_cells);
- const orientation = random_choice([ 'n', 'e', 's', 'w' ]);
- return [...acc, {...agent, x, y, flags: { ...agent.flags, team: team_num, orientation } }];
- },
- []
- ));
- },
- []
- ).flat();
-
- return { lattice, lattice_rules, agents, actions, senses, validity };
-};
-
-
-// team structure:
-// {
-// agents
-// genome
-// score
-// games
-// }
-
-const N_INPUT = senses.reduce((acc, sense) => acc + sense.size, 0);
-const N_OUTPUT = actions.reduce((acc, action) => acc + action.size, 0);
-const MAX_MUTATIONS = 15;
-
-
-let agent_id = 0;
-
-export function create_agent(genome, n_internal) {
- return {
- id: agent_id++, // !!!! side effect !!!!
- net: parse_genome(genome),
- state: [...Array(n_internal)].map(_ => (2*Math.random()) - 1),
- }
-}
-
-
-
-export function create_team(size, genome_size, n_internal) {
- const genome = apply(
- s => mut_genome_insert(s, 4),
- genome_size,
- {n_input: N_INPUT, n_internal, n_output: N_OUTPUT, genes: []},
- );
- console.log(N_INPUT, N_OUTPUT, genome);
-
- const agents = [...Array(size)].map(_ => create_agent(genome, n_internal));
- return { agents, genome, score: 0, games: 0 };
-}
-
-
-export function child_team(team, keep=Math.floor(team.size/2)) {
- const n_internal = get_size(team.genome) - N_INPUT - N_OUTPUT;
- const genome = apply(
- s => mutate_genome(s, 4),
- Math.floor(MAX_MUTATIONS * Math.random()),
- [N_INPUT, n_internal, N_OUTPUT, team.genome]
- );
-
- const old_agents = [...Array(team.agents.length - keep)].reduce(
- (acc, _) => {
- const idx = Math.floor(Math.random() * acc.length);
- acc.splice(idx, 1);
- return acc;
- },
- );
- const new_agents = [...Array(team.agents.length - keep)].map(_ => create_agent(genome, n_internal));
- const agents = [old_agents, new_agents].flat();
-
- return { agents, genome, score: 0, games: 0 };
-}
-
-
-// game structure:
-// {
-// world: world
-// team_indices: number[]
-// time: number
-// }
-
-
-export function create_game(size, teams, team_indices) {
- const world = create_world(size, team_indices.map(i => teams[i].agents));
- return { world, team_indices, time: 0 };
-}
-
-
-export function step_game(game) {
- return {
- ...game,
- world: world_update(game.world, postprocess),
- time: game.time + 1,
- };
-}
-
-
-function score(lattice, team_num) {
- const size = lattice.length;
-
- // count number of flags in the team's area
- return lattice
- .map((row, y) => row.map((cell, x) => [x, y, cell]))
- .flat()
- .filter(([x, y, cell]) => get_team(size, x, y) === team_num && cell.type === 'flag')
- .length;
-}
-
-export function finish_game(teams, game) {
- const scores = [0, 1, 2, 3].map(t => score(game.world.lattice, t));
- return game.team_indices.reduce(
- (acc, idx, i) => {
- const team = teams[idx];
- acc.splice(idx, 1, {...team, score: team.score + scores[i], games: team.games+1});
- return acc;
- },
- teams,
- );
-}
-
-
-
-// epoch structure
-// {
-// game
-// time
-// teams
-// }
-
-function random_indices(teams) {
- return [...Array(teams.length - 4)].reduce(
- (acc) => {
- const idx = Math.floor(Math.random() * acc.length);
- acc.splice(idx, 1);
- return acc;
- },
- [...teams.keys()]
- );
-}
-
-let epoch_num = 0;
-export function create_epoch(size, teams) {
- return {
- game: create_game(size, teams, random_indices(teams)),
- time: 0,
- epoch: epoch_num++, // !!!! side effects !!!!
- size,
- teams,
- }
-}
-
-
-const GAME_STEPS = 10000
-const EPOCH_STEPS = 200
-
-export function update_epoch(epoch) {
- if (epoch.game.time < GAME_STEPS) {
- return { ...epoch, game: step_game(epoch.game) };
- } else if (epoch.time < EPOCH_STEPS) {
- return {
- ...epoch,
- teams: finish_game(epoch.teams, epoch.game),
- game: create_game(size, epoch.teams, random_indices(epoch.teams)),
- time: epoch.time+1
- };
- } else {
- // epoch complete!!
- const source_teams = epoch.teams
- .map(team => {
- const normalized_score = team.score/team.games;
- const count = Math.ceil(16*normalized_score);
- return [...Array(count > 0 ? count : 1)].map(x => team)
- })
- .flat();
- const new_teams = [...Array(epoch.teams.length)]
- .reduce((acc) => child_team(random_choice(source_teams)), []);
- return create_epoch(epoch.size, new_teams);
- }
-}
diff --git a/src/simulation/game.test.js b/src/simulation/game.test.js
deleted file mode 100644
index 219dce6..0000000
--- a/src/simulation/game.test.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict';
-
-import { apply } from '../util.js';
-import { setup_board, create_world } from './game.js';
-
-
-test("set up boards correctly", () => {
- const _ = { type: 'empty', flags: {} };
- const a = { type: 'empty', flags: { team: 0 } };
- const b = { type: 'empty', flags: { team: 1 } };
- const c = { type: 'empty', flags: { team: 2 } };
- const d = { type: 'empty', flags: { team: 3 } };
- const W = { type: 'immutable', flags: {} };
-
- expect(setup_board(6)).toEqual([
- [ W, W, W, W, W, W, ],
- [ W, W, a, a, W, W, ],
- [ W, d, _, _, b, W, ],
- [ W, d, _, _, b, W, ],
- [ W, W, c, c, W, W, ],
- [ W, W, W, W, W, W, ],
- ]);
- expect(setup_board(9)).toEqual([
- [ W, W, W, W, W, W, W, W, W, ],
- [ W, W, W, a, a, a, W, W, W, ],
- [ W, W, W, a, a, a, W, W, W, ],
- [ W, d, d, _, _, _, b, b, W, ],
- [ W, d, d, _, _, _, b, b, W, ],
- [ W, d, d, _, _, _, b, b, W, ],
- [ W, W, W, c, c, c, W, W, W, ],
- [ W, W, W, c, c, c, W, W, W, ],
- [ W, W, W, W, W, W, W, W, W, ],
- ]);
-});
-
-
-test("creating a world works correctly", () => {
- const id = 0;
- const agent = (id) => ({ id, net: `${id}`, state: `s${id}` });
- const team1 = [agent(0), agent(1)];
- const team2 = [agent(2), agent(3)];
- const team3 = [agent(4), agent(5)];
- const team4 = [agent(6), agent(7)];
-
- const world = create_world(6, [team1, team2, team3, team4]);
- const agent_cell = (agent) => {
- const { x, y } = agent;
- return world.lattice[y][x];
- };
-
- expect(agent_cell(world.agents[0])).toEqual({ type: 'empty', flags: { team: 0 } });
- expect(agent_cell(world.agents[1])).toEqual({ type: 'empty', flags: { team: 0 } });
- expect(agent_cell(world.agents[2])).toEqual({ type: 'empty', flags: { team: 1 } });
- expect(agent_cell(world.agents[3])).toEqual({ type: 'empty', flags: { team: 1 } });
- expect(agent_cell(world.agents[4])).toEqual({ type: 'empty', flags: { team: 2 } });
- expect(agent_cell(world.agents[5])).toEqual({ type: 'empty', flags: { team: 2 } });
- expect(agent_cell(world.agents[6])).toEqual({ type: 'empty', flags: { team: 3 } });
- expect(agent_cell(world.agents[7])).toEqual({ type: 'empty', flags: { team: 3 } });
-});
diff --git a/src/simulation/lattice_rules.js b/src/simulation/lattice_rules.js
deleted file mode 100644
index cc2fc5b..0000000
--- a/src/simulation/lattice_rules.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import { pairs } from '../util.js';
-import { get_team } from './game.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',
- flags: { emit: [0, 0, 0, 0, 0, 0, 0, 1] },
- }]};
- } else {
- return { world_updates: [{
- x, y, flags: { team: get_team(lattice.length, x, y) },
- }]};
- }
- },
-
- 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;
- const die = { world_updates: [{
- x, y, from: 'active', to: 'empty',
- flags: { emit: [0, 0, 0, 0, 0, 0, 0, -1] },
- }]};
- if (num_active_neighbors < 2) {
- return die;
- } else if (num_active_neighbors > 3) {
- return die;
- }
- },
-
- mutable: (lattice, x, y) => {
- const num_active_neighbors = neighbors(lattice, x, y)
- .map(([x, y, cell]) => cell.type)
- .filter(type => type === 'active')
- .length;
- if (num_active_neighbors > 0) {
- // become living cell
- return { world_updates: [{
- x, y, from: 'empty', to: 'active',
- flags: { emit: [0, 0, 0, 0, 0, 0, 0, 1] },
- }]};
- }
- },
- immutable: () => {},
- flag: () => {},
-
-};
diff --git a/src/simulation/lattice_rules.test.js b/src/simulation/lattice_rules.test.js
deleted file mode 100644
index c285711..0000000
--- a/src/simulation/lattice_rules.test.js
+++ /dev/null
@@ -1,147 +0,0 @@
-import { world_update } from '../world/world.js';
-import { lattice_rules } from './lattice_rules.js';
-
-
-function apply(f, n, x0) {
- if (n == 0) {
- return x0;
- } else {
- return f(apply(f, n-1, x0));
- }
-}
-
-
-// remove cell team information
-function clean_team(lattice) {
- return lattice.map(row => row.map(cell => ({...cell, flags: {...cell.flags, team: undefined } })));
-}
-
-
-test("blinker", () => {
- const L = { type: 'active', flags: { team: undefined, } };
- const _ = { type: 'empty', flags: { team: undefined, } };
- const l = { type: 'active', flags: { team: undefined, emit: [0, 0, 0, 0, 0, 0, 0, 1] } };
- const d = { type: 'empty', flags: { team: undefined, emit: [0, 0, 0, 0, 0, 0, 0, -1] } };
- const lattice = [
- [ _, _, _, _, _ ],
- [ _, _, _, _, _ ],
- [ _, L, L, L, _ ],
- [ _, _, _, _, _ ],
- [ _, _, _, _, _ ],
- ];
-
- const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
- expect(clean_team(world_update(world).lattice)).toEqual([
- [ _, _, _, _, _ ],
- [ _, _, l, _, _ ],
- [ _, d, L, d, _ ],
- [ _, _, l, _, _ ],
- [ _, _, _, _, _ ],
- ]);
- expect(clean_team(world_update(world_update(world)).lattice)).toEqual([
- [ _, _, _, _, _ ],
- [ _, _, d, _, _ ],
- [ _, l, L, l, _ ],
- [ _, _, d, _, _ ],
- [ _, _, _, _, _ ],
- ]);
-});
-
-
-test("glider", () => {
- const L = { type: 'active', flags: { team: undefined, } };
- const _ = { type: 'empty', flags: { team: undefined, } };
- const l = { type: 'active', flags: { team: undefined, emit: [0, 0, 0, 0, 0, 0, 0, 1] } };
- const d = { type: 'empty', flags: { team: undefined, emit: [0, 0, 0, 0, 0, 0, 0, -1] } };
- const lattice = [
- [ _, _, _, _, _, _ ],
- [ _, _, _, L, _, _ ],
- [ _, L, _, L, _, _ ],
- [ _, _, L, L, _, _ ],
- [ _, _, _, _, _, _ ],
- [ _, _, _, _, _, _ ],
- ];
-
- const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
- //expect(clean_team(world_update(world).lattice)).toEqual([
- expect(clean_team(apply(world_update, 1, world).lattice)).toEqual([
- [ _, _, _, _, _, _ ],
- [ _, _, l, d, _, _ ],
- [ _, d, _, L, l, _ ],
- [ _, _, L, L, _, _ ],
- [ _, _, _, _, _, _ ],
- [ _, _, _, _, _, _ ],
- ]);
- expect(clean_team(apply(world_update, 2, world).lattice)).toEqual([
- [ _, _, _, _, _, _ ],
- [ _, _, d, l, _, _ ],
- [ _, _, _, d, L, _ ],
- [ _, _, L, L, l, _ ],
- [ _, _, _, _, _, _ ],
- [ _, _, _, _, _, _ ],
- ]);
- expect(clean_team(apply(world_update, 3, world).lattice)).toEqual([
- [ _, _, _, _, _, _ ],
- [ _, _, _, d, _, _ ],
- [ _, _, l, _, L, _ ],
- [ _, _, d, L, L, _ ],
- [ _, _, _, l, _, _ ],
- [ _, _, _, _, _, _ ],
- ]);
- expect(clean_team(apply(world_update, 4, world).lattice)).toEqual([
- [ _, _, _, _, _, _ ],
- [ _, _, _, _, _, _ ],
- [ _, _, d, _, L, _ ],
- [ _, _, l, d, L, _ ],
- [ _, _, _, L, l, _ ],
- [ _, _, _, _, _, _ ],
- ]);
-});
-
-
-test("beehive", () => {
- const L = { type: 'active', flags: { team: undefined, } };
- const _ = { type: 'empty', flags: { team: undefined, } };
- const lattice = [
- [ _, _, _, _, _, _ ],
- [ _, _, L, L, _, _ ],
- [ _, L, _, _, L, _ ],
- [ _, _, L, L, _, _ ],
- [ _, _, _, _, _, _ ],
- ];
-
- const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
- //expect(clean_team(world_update(world).lattice)).toEqual([
- expect(clean_team(apply(world_update, 1, world).lattice)).toEqual(lattice);
-});
-
-
-test("mutables are activated by neighboring actives", () => {
- const L = { type: 'active', flags: { team: undefined, } };
- const _ = { type: 'empty', flags: { team: undefined, } };
- const l = { type: 'active', flags: { team: undefined, emit: [0, 0, 0, 0, 0, 0, 0, 1] } };
- const d = { type: 'empty', flags: { team: undefined, emit: [0, 0, 0, 0, 0, 0, 0, -1] } };
- const M = { type: 'mutable', flags: { team: undefined, } };
-
- const lattice = [
- [ _, _, _, _, ],
- [ _, L, M, _, ],
- [ _, M, M, _, ],
- [ _, _, _, _, ],
- ];
-
- const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
-
- expect(clean_team(apply(world_update, 1, world).lattice)).toEqual([
- [ _, _, _, _, ],
- [ _, L, l, _, ],
- [ _, l, l, _, ],
- [ _, _, _, _, ],
- ]);
- expect(clean_team(apply(world_update, 2, world).lattice)).toEqual([
- [ _, _, _, _, ],
- [ _, L, L, _, ],
- [ _, L, L, _, ],
- [ _, _, _, _, ],
- ]);
-});
diff --git a/src/simulation/postprocess.js b/src/simulation/postprocess.js
deleted file mode 100644
index d50839e..0000000
--- a/src/simulation/postprocess.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-export const postprocess = [
- (world) => ({
- ...world,
- agents: world.agents.map(a => {
- const {x, y} = a;
- if (
- world.lattice[y][x].type === 'mutable' ||
- world.lattice[y][x].type === 'active'
- ) {
- return { ...a, flags: {...a.flags, frozen: true } };
- } else {
- return a;
- }
- }),
- }),
-];
diff --git a/src/simulation/postprocess.test.js b/src/simulation/postprocess.test.js
deleted file mode 100644
index dc77c6a..0000000
--- a/src/simulation/postprocess.test.js
+++ /dev/null
@@ -1,37 +0,0 @@
-'use strict';
-
-import { world_update } from '../world/world.js';
-import { postprocess } from './postprocess.js';
-
-test("agents freeze when finishing on a mutable or active", () => {
- const agent = {
- id: 1,
- net: { compute: () => [[1], null] },
- state: null,
- x: 0, y: 0,
- flags: {},
- };
-
- const lattice = [[{ type: 'empty', flags: {} }]];
-
- const world = {
- lattice,
- lattice_rules: { empty: ()=>{}, active: ()=>{}, mutable: ()=>{} },
- agents: [agent],
- senses: [],
- actions: [],
- validity: [],
- };
-
- expect(world_update(world, postprocess).agents[0]).toEqual(agent);
- world.lattice[0][0].type = 'mutable';
- expect(world_update(world, postprocess).agents[0]).toEqual({
- ...agent,
- flags: { frozen: true },
- });
- world.lattice[0][0].type = 'active';
- expect(world_update(world, postprocess).agents[0]).toEqual({
- ...agent,
- flags: { frozen: true },
- });
-});
diff --git a/src/simulation/senses.js b/src/simulation/senses.js
deleted file mode 100644
index 970f86b..0000000
--- a/src/simulation/senses.js
+++ /dev/null
@@ -1,182 +0,0 @@
-'use strict';
-
-
-const frozen = {
- size: 1,
- read: (world, agent) => {
- if (agent.flags.frozen === true) {
- return [ 1 ];
- } else {
- return [ 0 ];
- }
- },
-};
-
-
-// add two arrays together element-wise with a scaling factor
-function array_scalesum(a, s, b) {
- return a.map((x, i) => x + (s*b[i]));
-}
-// determine the square of the distance between two cells
-function lattice_dist2(x0, y0, x1, y1) {
- if (x0 === x1 && y0 === y1) { return 1; } // not proper distance but avoids divide-by-zero errors c:
- return ((x0-x1)**2) + ((y0-y1)**2);
-}
-const hear = {
- size: 8,
- read: (world, agent) => {
- const {x, y} = agent;
- const lattice_sounds = world.lattice
- .map((row, cy) => row.map((cell, cx) => [ 1/lattice_dist2(x, y, cx, cy), cell ]))
- .flat()
- .filter(([scale, cell]) => cell.flags.emit !== undefined)
- .reduce(
- (acc, [scale, cell]) => array_scalesum(acc, scale, cell.flags.emit),
- [0, 0, 0, 0, 0, 0, 0, 0]
- );
- const agent_sounds = world.agents
- .filter(a => a.flags.emit !== undefined)
- .reduce(
- (acc, a) => array_scalesum(acc, 1/lattice_dist2(x, y, a.x, a.y), a.flags.emit),
- [0, 0, 0, 0, 0, 0, 0, 0]
- );
-
- return array_scalesum(lattice_sounds, 1, agent_sounds).map(ch => Math.tanh(ch));
- },
-};
-
-
-const [VIS_WIDTH, VIS_HEIGHT] = [31, 31];
-const [VIS_HWIDTH, VIS_HHEIGHT] = [Math.floor(VIS_WIDTH/2), Math.floor(VIS_HEIGHT/2)];
-function identity_mod(n, p) {
- const mod = (n % (2*p)) - p + 1;
- return mod/p;
-}
-
-function world_pos_to_vision_pos(world, agent, x, y) {
- const dx = x - agent.x;
- const dy = y - agent.y;
- const orientation = agent.flags.orientation || 'n';
- switch (orientation) {
- case 'n':
- return [VIS_HWIDTH+dx, VIS_HEIGHT+dy-1];
- case 's':
- return [VIS_HWIDTH-dx, VIS_HEIGHT-dy-1];
- case 'e':
- return [VIS_HWIDTH+dy, VIS_HEIGHT-dx-1];
- case 'w':
- return [VIS_HWIDTH-dy, VIS_HEIGHT+dx-1];
- }
-}
-
-function vision_pos_to_world_pos(world, agent, x, y) {
- const dx = x-VIS_HWIDTH;
- const dy = y-VIS_HEIGHT+1;
- const orientation = agent.flags.orientation || 'n';
- switch (orientation) {
- case 'n':
- return [agent.x + dx, agent.y + dy];
- case 's':
- return [agent.x - dx, agent.y - dy];
- case 'e':
- return [agent.x - dy, agent.y - dx];
- case 'w':
- return [agent.x + dy, agent.y + dx];
- }
-}
-
-function world_pos_to_vision_idx(world, agent, x, y) {
- const [vx, vy] = world_pos_to_vision_pos(world, agent, x, y);
- return (VIS_WIDTH * vy) + vx;
-}
-function vision_idx_to_world_pos(world, agent, idx) {
- const vx = idx % VIS_WIDTH;
- const vy = Math.floor(idx / VIS_WIDTH);
- const result = vision_pos_to_world_pos(world, agent, vx, vy);
- return result;
-}
-
-function see_cell(world, x, y) {
- const team = 0;
- const orientation = 0;
- if (!world.lattice[y] || !world.lattice[y][x]) {
- // beyond the map edge
- return [ 0, 0, 0 ];
- }
- const type = {
- active: -0.8,
- mutable: -0.4,
- empty: 0.0,
- immutable: 0.4,
- flag: 0.8,
- }[world.lattice[y][x].type];
- return [team, orientation, type];
-}
-
-
-function relative_orientation(viewer, agent) {
- switch(viewer.flags.orientation) {
- case 'n': return { n: -0.8, e: -0.4, s: 0.4, w: 0.8 }[agent.flags.orientation];
- case 'e': return { e: -0.8, s: -0.4, w: 0.4, n: 0.8 }[agent.flags.orientation];
- case 's': return { s: -0.8, w: -0.4, n: 0.4, e: 0.8 }[agent.flags.orientation];
- case 'w': return { w: -0.8, n: -0.4, e: 0.4, s: 0.8 }[agent.flags.orientation];
- }
-}
-
-
-function see_agent(viewer, agent) {
- const team = {
- 0: -0.8,
- 1: -0.4,
- 2: 0.4,
- 3: 0.8,
- }[agent.flags.team] + (identity_mod(agent.id, 11)/16);
- const orientation = relative_orientation(viewer, agent) + (identity_mod(agent.id, 37)/16);
- const frozen = agent.flags.frozen || agent.flags.pretend_frozen;
- const type = (() => {
- if (frozen && agent.flags.flag) {
- return -0.4;
- } else if (frozen) {
- return -0.8;
- } else if (agent.flags.flag) {
- return 0.8;
- } else {
- return 0.0;
- }
- })() + (identity_mod(agent.id, 499)/16);
-
- return [team, orientation, type];
-}
-
-
-const see = {
- size: 3*VIS_WIDTH * VIS_HEIGHT,
- read: (world, agent) => {
- const indices = [...Array(VIS_WIDTH*VIS_HEIGHT).keys()]
- const vision = indices
- .map(idx => {
- const [x, y] = vision_idx_to_world_pos(world, agent, idx);
- return see_cell(world, x, y);
- });
- const result = world.agents.reduce(
- (acc, a) => {
- const idx = world_pos_to_vision_idx(world, agent, a.x, a.y);
-
- if (idx < 0 || idx >= VIS_WIDTH*VIS_HEIGHT) {
- return acc;
- } else {
- acc.splice(idx, 1, see_agent(agent, a));
- return acc;
- }
- },
- vision
- );
- return result.flat();
- },
-};
-
-
-
-export const senses = [
- frozen, hear, see, { size: 1, read: () => [1] },
-];
diff --git a/src/simulation/senses.test.js b/src/simulation/senses.test.js
deleted file mode 100644
index d3941c7..0000000
--- a/src/simulation/senses.test.js
+++ /dev/null
@@ -1,499 +0,0 @@
-'use strict';
-
-import { senses } from './senses.js';
-
-const [ frozen, hear, see, ...rest ] = senses;
-
-
-test("frozen sense", () => {
- const agent = {
- id: 0, x: 0, y: 0,
- flags: { frozen: true, },
- };
-
- expect(frozen.read(null, agent)).toEqual([1]);
- agent.flags.frozen = false;
- expect(frozen.read(null, agent)).toEqual([0]);
-});
-
-
-// --===== hearing =====--
-
-test("hear nothing", () => {
- const agent = { id: 4, x: 1, y: 1, flags: {} };
- const o = { type: 'empty', flags: {} };
- const world = {
- lattice: [
- [ o, o, o ],
- [ o, o, o ],
- [ o, o, o ],
- ],
- agents: [agent],
- };
-
- expect(hear.read(world, agent)).toEqual([
- Math.tanh(0), Math.tanh(0), Math.tanh(0), Math.tanh(0),
- Math.tanh(0), Math.tanh(0), Math.tanh(0), Math.tanh(0),
- ]);
-});
-
-
-test("hear self", () => {
- const agent = { id: 4, x: 1, y: 1, flags: { emit: [1, 0, 0.5, 0, 0, 0, 0, 1] } };
- const o = { type: 'empty', flags: {} };
- const world = {
- lattice: [
- [ o, o, o ],
- [ o, o, o ],
- [ o, o, o ],
- ],
- agents: [agent],
- };
-
- expect(hear.read(world, agent)).toEqual([
- Math.tanh(1), Math.tanh(0), Math.tanh(0.5), Math.tanh(0),
- Math.tanh(0), Math.tanh(0), Math.tanh(0), Math.tanh(1),
- ]);
-});
-
-
-test("hear cells", () => {
- const agent = { id: 4, x: 2, y: 2, flags: {} };
- const o = { type: 'empty', flags: {} };
- const s = { type: 'empty', flags: { emit: [1, 0.5, 0.25, 0.125, 0, 0, 0, 0] } };
- const world = {
- lattice: [
- [ o, o, s, o, o ],
- [ o, o, o, o, o ],
- [ o, s, o, o, o ],
- [ o, o, o, o, o ],
- [ o, o, o, o, o ],
- ],
- agents: [agent],
- };
-
- expect(hear.read(world, agent)).toEqual([
- Math.tanh(1.25), Math.tanh(0.625), Math.tanh(0.3125), Math.tanh(0.15625),
- Math.tanh(0), Math.tanh(0), Math.tanh(0), Math.tanh(0),
- ]);
-});
-
-
-test("hear cells & agents", () => {
- const agent = { id: 4, x: 2, y: 2, flags: {} };
- const agent2 = { id: 0, x: 2, y: 4, flags: { emit: [0, 0, 0, 0, 1, 1, 1, 1] } };
- const o = { type: 'empty', flags: {} };
- const s = { type: 'empty', flags: { emit: [1, 0.5, 0.25, 0.125, 0, 0, 0, 0] } };
- const world = {
- lattice: [
- [ o, o, s, o, o ],
- [ o, o, o, o, o ],
- [ o, s, o, o, o ],
- [ o, o, o, o, o ],
- [ o, o, o, o, o ],
- ],
- agents: [agent, agent2],
- };
-
- expect(hear.read(world, agent)).toEqual([
- Math.tanh(1.25), Math.tanh(0.625), Math.tanh(0.3125), Math.tanh(0.15625),
- Math.tanh(0.25), Math.tanh(0.25), Math.tanh(0.25), Math.tanh(0.25),
- ]);
-});
-
-
-// --===== vision =====--
-
-
-test("see agents", () => {
- const o = { type: 'empty', flags: {} };
- const lattice = [
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- [ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o ],
- ];
- const agentA = { id: 0, x: 15, y: 30, flags: { team: 0, orientation: 'n' } };
- const agentB = { id: 1, x: 15, y: 0, flags: { team: 1, orientation: 's' } };
- const agentC = { id: 2, x: 0, y: 15, flags: { team: 2, orientation: 'e', flag: true } };
- const agentD = { id: 3, x: 30, y: 15, flags: { team: 3, orientation: 'w', frozen: true } };
- const agentE = { id: 4, x: 30, y: 30, flags: { team: 0, orientation: 's', flag: true, frozen: true } };
- const world = {
- lattice,
- agents: [agentA, agentB, agentC, agentD, agentE],
- };
-
- const an = [
- -0.8 + 0.0625*(-10/11), // team + identity
- -0.8 + 0.0625*(-36/37), // orientation + identity
- 0.0 + 0.0625*(-498/499), // class + identity
- ];
- const as = [
- -0.8 + 0.0625*(-10/11), // team + identity
- 0.4 + 0.0625*(-36/37), // orientation + identity
- 0.0 + 0.0625*(-498/499), // class + identity
- ];
- const ae = [
- -0.8 + 0.0625*(-10/11), // team + identity
- -0.4 + 0.0625*(-36/37), // orientation + identity
- 0.0 + 0.0625*(-498/499), // class + identity
- ];
- const aw = [
- -0.8 + 0.0625*(-10/11), // team + identity
- 0.8 + 0.0625*(-36/37), // orientation + identity
- 0.0 + 0.0625*(-498/499), // class + identity
- ];
-
- const bn = [
- -0.4 + 0.0625*(-9/11), // team + identity
- -0.8 + 0.0625*(-35/37), // orientation + identity
- 0.0 + 0.0625*(-497/499), // class + identity
- ];
- const bs = [
- -0.4 + 0.0625*(-9/11), // team + identity
- 0.4 + 0.0625*(-35/37), // orientation + identity
- 0.0 + 0.0625*(-497/499), // class + identity
- ];
- const be = [
- -0.4 + 0.0625*(-9/11), // team + identity
- -0.4 + 0.0625*(-35/37), // orientation + identity
- 0.0 + 0.0625*(-497/499), // class + identity
- ];
- const bw = [
- -0.4 + 0.0625*(-9/11), // team + identity
- 0.8 + 0.0625*(-35/37), // orientation + identity
- 0.0 + 0.0625*(-497/499), // class + identity
- ];
-
- const cn = [
- 0.4 + 0.0625*(-8/11), // team + identity
- -0.8 + 0.0625*(-34/37), // orientation + identity
- 0.8 + 0.0625*(-496/499), // class + identity
- ];
- const cs = [
- 0.4 + 0.0625*(-8/11), // team + identity
- 0.4 + 0.0625*(-34/37), // orientation + identity
- 0.8 + 0.0625*(-496/499), // class + identity
- ];
- const ce = [
- 0.4 + 0.0625*(-8/11), // team + identity
- -0.4 + 0.0625*(-34/37), // orientation + identity
- 0.8 + 0.0625*(-496/499), // class + identity
- ];
- const cw = [
- 0.4 + 0.0625*(-8/11), // team + identity
- 0.8 + 0.0625*(-34/37), // orientation + identity
- 0.8 + 0.0625*(-496/499), // class + identity
- ];
-
- const dn = [
- 0.8 + 0.0625*(-7/11), // team + identity
- -0.8 + 0.0625*(-33/37), // orientation + identity
- -0.8 + 0.0625*(-495/499), // class + identity
- ];
- const ds = [
- 0.8 + 0.0625*(-7/11), // team + identity
- 0.4 + 0.0625*(-33/37), // orientation + identity
- -0.8 + 0.0625*(-495/499), // class + identity
- ];
- const de = [
- 0.8 + 0.0625*(-7/11), // team + identity
- -0.4 + 0.0625*(-33/37), // orientation + identity
- -0.8 + 0.0625*(-495/499), // class + identity
- ];
- const dw = [
- 0.8 + 0.0625*(-7/11), // team + identity
- 0.8 + 0.0625*(-33/37), // orientation + identity
- -0.8 + 0.0625*(-495/499), // class + identity
- ];
-
- const en = [
- -0.8 + 0.0625*(-6/11), // team + identity
- -0.8 + 0.0625*(-32/37), // orientation + identity
- -0.4 + 0.0625*(-494/499), // class + identity
- ];
- const es = [
- -0.8 + 0.0625*(-6/11), // team + identity
- 0.4 + 0.0625*(-32/37), // orientation + identity
- -0.4 + 0.0625*(-494/499), // class + identity
- ];
- const ee = [
- -0.8 + 0.0625*(-6/11), // team + identity
- -0.4 + 0.0625*(-32/37), // orientation + identity
- -0.4 + 0.0625*(-494/499), // class + identity
- ];
- const ew = [
- -0.8 + 0.0625*(-6/11), // team + identity
- 0.8 + 0.0625*(-32/37), // orientation + identity
- -0.4 + 0.0625*(-494/499), // class + identity
- ];
-
- const _ = [
- 0.0, 0.0, 0.0,
- ];
-
-
- expect(see.read(world, agentA)).toEqual([
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, bs, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ce, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, dw,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, an, _, _, _, _, _, _, _, _, _, _, _, _, _, _, es,
- ]);
- expect(see.read(world, agentB)).toEqual([
- en, _, _, _, _, _, _, _, _, _, _, _, _, _, _, as, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- de, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, cw,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, bn, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ]);
- expect(see.read(world, agentC)).toEqual([
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, ds, _, _, _, _, _, _, _, _, _, _, _, _, _, _, ee,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- be, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, aw,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, cn, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ]);
- expect(see.read(world, agentD)).toEqual([
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, cs, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ae, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, bw,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ew, _, _, _, _, _, _, _, _, _, _, _, _, _, _, dn, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ]);
-});
-
-
-test("seeing 'nothing' looks like teamless empty squares", () => {
- const lattice = [[]];
- const agent = { id: 0, x: 0, y: 0, flags: { team: 0, orientation: 'n' } };
- const world = {
- lattice, agents: [agent],
- };
-
- const _ = [
- 0.0, 0.0, 0.0,
- ];
- const an = [
- -0.8 + 0.0625*(-10/11), // team + identity
- -0.8 + 0.0625*(-36/37), // orientation + identity
- 0.0 + 0.0625*(-498/499), // class + identity
- ];
-
- expect(see.read(world, agent)).toEqual([
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, an, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ]);
-});
-
-
-test("agents pretending to be frozen appear frozen", () => {
- const lattice = [[]];
- const agent = { id: 0, x: 0, y: 0, flags: { team: 0, orientation: 'n', pretend_frozen: true } };
- const world = {
- lattice, agents: [agent],
- };
-
- const _ = [
- 0.0, 0.0, 0.0,
- ];
- const an = [
- -0.8 + 0.0625*(-10/11), // team + identity
- -0.8 + 0.0625*(-36/37), // orientation + identity
- -0.8 + 0.0625*(-498/499), // class + identity
- ];
-
- expect(see.read(world, agent)).toEqual([
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, an, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
- ]);
-});
diff --git a/src/simulation/trial.js b/src/simulation/trial.js
deleted file mode 100644
index b4462c5..0000000
--- a/src/simulation/trial.js
+++ /dev/null
@@ -1,12 +0,0 @@
-'use strict';
-
-import { create_team, create_epoch, update_epoch } from './game.js';
-
-const start_teams = [...Array(50)].map(x => create_team(32, 5, 5));
-
-let epoch = create_epoch(start_teams);
-
-while (epoch.epoch < 1) {
- console.log(epoch.epoch, epoch.time, epoch.game.time);
- epoch = update_epoch(epoch);
-}
diff --git a/src/simulation/validity.js b/src/simulation/validity.js
deleted file mode 100644
index de4acd1..0000000
--- a/src/simulation/validity.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export const validity = [
- // prevent agents from moving onto immutables
- (world, proposal) => (proposal.agent_changes || []).reduce(
- (acc, change) => {
- const {x, y} = change;
- if (x !== undefined && y !== undefined && world.lattice[y][x].type === 'immutable') {
- return false;
- } else {
- return acc;
- }
- },
- true,
- ),
-];
diff --git a/src/simulation/validity.test.js b/src/simulation/validity.test.js
deleted file mode 100644
index ba9e684..0000000
--- a/src/simulation/validity.test.js
+++ /dev/null
@@ -1,43 +0,0 @@
-'use strict';
-
-import { world_update } from '../world/world.js';
-import { validity } from './validity.js';
-
-test("agents are not allowed to move into immutables", () => {
- const actions = [{
- size: 1, propose: (world, agent, head) => {
- return [{
- agent_changes: [{
- agent_id: agent.id,
- x: agent.x + 1, y: agent.y,
- }],
- }];
- },
- }];
-
- const agent = {
- id: 1,
- net: { compute: () => [[1], null] },
- state: null,
- x: 0, y: 0,
- flags: {},
- };
-
- const lattice = [[{ type: 'empty', flags: {} }, { type: 'immutable', flags: {} }]];
-
- const world = {
- lattice,
- lattice_rules: { empty: ()=>{}, immutable: ()=>{} },
- agents: [agent],
- senses: [],
- actions,
- validity,
- };
-
- expect(world_update(world).agents[0]).toEqual(agent);
- world.validity = [];
- expect(world_update(world).agents[0]).toEqual({
- ...agent,
- x: 1, y: 0,
- });
-});
diff --git a/src/ui/canvas.js b/src/ui/canvas.js
deleted file mode 100644
index b04b966..0000000
--- a/src/ui/canvas.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-export function draw(canvas, size, fn) {
- const ctx = canvas.getContext("2d");
- const scale = canvas.clientWidth/size;
- ctx.save();
- ctx.scale(scale, scale);
- ctx.clearRect(0, 0, size, size);
- fn(ctx);
- ctx.restore();
-}
diff --git a/src/ui/index.js b/src/ui/index.js
deleted file mode 100644
index 01068cb..0000000
--- a/src/ui/index.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import { draw } from './canvas.js';
-import { create_team, create_epoch, update_epoch } from '../simulation/game.js';
-
-
-console.log("generating agents...");
-const start_teams = [...Array(4)].map(x => create_team(2, 400, 50));
-console.log("creating epoch...");
-let epoch = create_epoch(60, start_teams);
-console.log("ready!");
-
-
-function draw_cell(ctx, x, y, cell) {
- ctx.fillStyle = (() => {
- switch (cell.type) {
- case 'empty':
- return '#ffffff';
- case 'immutable':
- return '#0000ff';
- case 'mutable':
- return '#555555';
- case 'active':
- return '#000000';
- case 'flag':
- return '#ffff00';
- default:
- return '#00ff00';
- }
- })();
- ctx.fillRect(x, y, 1, 1);
-}
-
-function draw_agent(ctx, agent) {
- ctx.beginPath();
- ctx.fillStyle = '#ff0000';
- const { x, y } = agent;
- ctx.arc(x+.5, y+.5, .5, 0, 2*Math.PI);
- ctx.fill();
-}
-
-
-function render(canvas) {
- draw(canvas, epoch.size, (ctx) => {
- for (let y=0; y<epoch.size; y++) {
- for (let x=0; x<epoch.size; x++) {
- draw_cell(ctx, x, y, epoch.game.world.lattice[y][x]);
- }
- }
- epoch.game.world.agents.forEach(a => draw_agent(ctx, a));
- });
-}
-
-
-function update(canvas) {
- console.log('update');
- epoch = update_epoch(epoch);
- render(canvas);
- setTimeout(() => update(canvas), 1);
-}
-
-
-function main() {
- const canvas = document.getElementById('canvas');
- window.onresize = () => {
- const size = 0.95*window.innerWidth
- canvas.width = size;
- canvas.height = size;
- render(canvas);
- }
- window.onresize();
- console.log("c:");
- update(canvas);
-}
-
-
-window.onload = main;
diff --git a/src/util.js b/src/util.js
deleted file mode 100644
index f83039f..0000000
--- a/src/util.js
+++ /dev/null
@@ -1,62 +0,0 @@
-'use strict';
-
-
-export function create(obj, proto=Object.prototype) {
- const props = Object.keys(obj)
- .map((key) => [ key, { value: obj[key], enumerable: true } ])
- .reduce((acc, [ key, value ]) => ({ ...acc, [key]: value }), {});
-
- return Object.create(proto, props);
-};
-
-
-export function random_choice(collection, r=Math.random()) {
- const idx = Math.floor(collection.length * r);
- return collection[idx];
-}
-
-
-export function pairs(arr1, arr2) {
- return arr1
- .map((x, i) => arr2.map(y => [x, y]))
- .flat();
-}
-
-
-export function deepEqual(a, b, debug=false) {
- if (typeof(a) === 'object') {
- if (typeof(b) === 'object') {
- // do deep equality
- return [...new Set(Object.keys(a).concat(Object.keys(b)))].reduce(
- (acc, key) => {
- return acc && deepEqual(a[key], b[key]);
- },
- true
- );
- } else {
- // one object, one non-object
- return false;
- }
- } else {
- return a === b;
- }
-}
-
-
-export function apply(f, n, x0) {
- if (n == 0) {
- return x0;
- } else {
- return f(apply(f, n-1, x0));
- }
-}
-
-
-export function shuffle(arr) {
- const shuffled = [...arr];
- for (let i=arr.length-1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i+1));
- [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
- }
- return shuffled;
-}
diff --git a/src/world/README.md b/src/world/README.md
deleted file mode 100644
index a88eadc..0000000
--- a/src/world/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# world
-
-The world is composed of a cell lattice. Each cell can contain a specific type of cell, and these cells
-evolve according to fixed, regular rules.
-
-Agents exist on top of the cell lattice. They occupy specific cell locations, but those locations are
-otherwise considered empty. Agents exist independent of the cells and evolve independent of them, though of
-course they are intertwined.
-
-Time step process:
-
- * Agents update internal state and propose moves.
- * World updates
- * Agent moves are resolved in the new world, and agent status is updated.
- * World state is updated to include agents.
-
-Cell types:
-*(all cells, except for Empties and Flags, count as GoL living)*
-
- * Empty (territory-colored, GoL dead)
- * Immutable
- * Mutable
- * Active (GoL living)
- * Flag
-
-Agent aspects: (64)
- * team (4)
- * orientation (4)
- * mobile/frozen (2)
- * has flag (2)
- * identity (3-channel)
-
-Agents have *internal state* corresponding to the state of their internal neurons (and roughly corresponding to their memories and experiences) and *status*, which refers to things like their current orientation, position, and mobility.
-
-
-Agent senses:
- * Hearing (inverse square, infinite range)
- * Sight (2D top-down, see further ahead than behind or to the sides, 7-channel)
- * Match timer
- * Epoch timer
-
-Agent actions:
- * Move forward/backward
- * Turn left/right
- * Place mutable
- * Trigger mutable -> active (5x5 square ahead of agent)
- * Play frozen
- * Unfreeze
- * Take flag
- * Drop flag (immediately ahead of agent)
-
-
- When carrying a flag, an agent counts as a flag tile (and other agents can steal the flag from them!). Otherwise, an agent counts as an empty tile.
diff --git a/src/world/agent.js b/src/world/agent.js
deleted file mode 100644
index 2be7420..0000000
--- a/src/world/agent.js
+++ /dev/null
@@ -1,56 +0,0 @@
-'use strict';
-
-import { sense_read } from './sense.js';
-import { proposal_merge } from './proposal.js';
-
-
-export function agent_decide(world, agent, senses, actions) {
- const inputs = senses.map(s => sense_read(world, agent, s)).flat();
- const [result, state] = agent.net.compute(inputs, agent.state);
- console.log(result, 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(world, 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
deleted file mode 100644
index d10a43a..0000000
--- a/src/world/agent.test.js
+++ /dev/null
@@ -1,49 +0,0 @@
-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: (world, agent, head) => [{ agent_changes: [{ agent_id: 3, flags: { act1: head[0] } }] }] },
- { size: 1, propose: (world, 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, 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/lattice.js b/src/world/lattice.js
deleted file mode 100644
index 066a5ca..0000000
--- a/src/world/lattice.js
+++ /dev/null
@@ -1,54 +0,0 @@
-'use strict';
-
-// get the proposals for cell updates
-export function lattice_update(lattice, update_rules) {
- return lattice
- .map((row, y) => row.map((cell, x) => [x, y, cell.type]))
- .flat()
- .reduce((acc, [x, y, type]) => [...acc, update_rules[type](lattice, x, y)], [])
- .filter(x => x !== undefined)
-}
-
-
-// check if, given the current lattice configuration, a proposal is valid
-export function lattice_valid(lattice, proposal) {
- if (!proposal.world_updates) { return true; }
- return proposal.world_updates.reduce(
- (acc, update) => {
- const valid =
- (update.x >= 0 && update.x < lattice[0].length) &&
- (update.y >= 0 && update.y < lattice.length) &&
- (lattice[update.y][update.x].type == update.from)
- return valid && acc;
- },
- true
- );
-}
-
-
-// apply a set of proposals, returning the new lattice
-export function lattice_apply(lattice, proposals) {
- const result = proposals.reduce(
- (acc, prop) => {
- const change = (prop.world_updates || []).reduce(
- (acc_, update) => {
- const cell = acc_[update.y][update.x];
- if (update.to) { cell.type = update.to; }
- if (update.flags) {
- cell.flags = { ...(cell.flags || {}), ...update.flags };
- //cell.flags = cell.flags || {}
- //// this is very side-effect-y but i couldn't think of a nicer compatible way of doing it 😔
- //for (let k of Object.keys(update.flags)) {
- // cell.flags[k] = update.flags[k];
- //}
- }
- return acc_
- },
- [...acc]
- );
- return change;
- },
- [...lattice]
- );
- return result;
-}
diff --git a/src/world/lattice.test.js b/src/world/lattice.test.js
deleted file mode 100644
index d1bdd13..0000000
--- a/src/world/lattice.test.js
+++ /dev/null
@@ -1,111 +0,0 @@
-'use strict';
-
-import { lattice_update, lattice_valid, lattice_apply } from './lattice.js';
-
-
-test("growth update rule", () => {
- const lattice = [[
- { type: 'empty', flags: {} },
- { type: 'empty', flags: {} },
- { type: 'plant', flags: {} },
- ]];
- const update_rules = {
- plant: () => {},
- empty: (lattice, x, y) => {
- if (lattice[y][x+1].type === 'plant') {
- return { world_updates: [{ x, y, from: 'empty', to: 'plant' }] };
- }
- },
- };
-
- expect(lattice_update(lattice, update_rules)).toEqual([
- { world_updates: [{ x: 1, y: 0, from: 'empty', to: 'plant' }] },
- ]);
-
- lattice[0][1] = { type: 'plant' };
-
- expect(lattice_update(lattice, update_rules)).toEqual([
- { world_updates: [{ x: 0, y: 0, from: 'empty', to: 'plant' }] },
- ]);
-
- lattice[0][0] = { type: 'plant' };
-
- expect(lattice_update(lattice, update_rules)).toEqual([]);
-});
-
-
-//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: {} },
- { type: 'empty', flags: {} },
- { type: 'plant', flags: {} },
- ]];
- expect(lattice_apply(lattice, [{ world_updates:[{ x: 1, y: 0, from: 'empty', to: 'plant' }]}])).toEqual([[
- { type: 'empty', flags: {} },
- { type: 'plant', flags: {} },
- { type: 'plant', flags: {} },
- ]]);
-
- expect(lattice_apply(lattice, [
- { world_updates: [{ x: 2, y: 0, from: 'plant', to: 'empty' } ]},
- { world_updates: [{ x: 1, y: 0, from: 'empty', to: 'plant' } ]},
- { world_updates: [{ x: 0, y: 0, from: 'empty', to: 'plant' } ]},
- ])).toEqual([[
- { type: 'plant', flags: {} },
- { type: 'plant', flags: {} },
- { type: 'empty', flags: {} },
- ]]);
-});
-
-
-test("check proposals agains lattice for validity", () => {
- const lattice = [[ { type: 'empty' }, { type: 'empty' }, { type: 'plant' } ]];
- expect(lattice_valid(lattice, { world_updates: [{ x: -1, y: 0, from: 'blah', to: 'blah' }] })).toBe(false);
- expect(lattice_valid(lattice, { world_updates: [{ x: 0, y: 0, from: 'blah', to: 'blah' }] })).toBe(false);
- expect(lattice_valid(lattice, { world_updates: [{ x: 0, y: 0, from: 'empty', to: 'blah' }] })).toBe(true);
- expect(lattice_valid(lattice, { world_updates: [{ x: 2, y: 0, from: 'empty', to: 'blah' }] })).toBe(false);
- expect(lattice_valid(lattice, { world_updates: [{ x: 2, y: 1, from: 'empty', to: 'blah' }] })).toBe(false);
-});
-
-
-// this test is no longer relevant because resetting the cell flags is taken care of by world_update,
-// not lattice_apply
-//
-//test("proposals update cell flags appropriately", () => {
-// const lattice = [
-// [
-// { type: 'empty', flags: { step: 1} },
-// { type: 'empty', flags: {} },
-// { type: 'plant', flags: { foo: 'bar' } },
-// ]
-// ];
-//
-// // flags are reset each time step
-// expect(lattice_apply(lattice, [{ world_updates:[{ x: 1, y: 0, from: 'empty', to: 'plant' }]}])).toEqual([[
-// { type: 'empty', flags: {} },
-// { type: 'plant', flags: {} },
-// { type: 'plant', flags: {} },
-// ]]);
-//
-// // flags are combined when updating
-// expect(lattice_apply(lattice, [
-// { world_updates: [{ x: 1, y: 0, flags: { foo: 'bar' } } ]},
-// { world_updates: [{ x: 1, y: 0, from: 'empty', to: 'plant', flags: { baz: 'baz' } } ]},
-// { world_updates: [{ x: 0, y: 0, from: 'empty', to: 'plant', flags: { foo: 'foo' } } ]},
-// { world_updates: [{ x: 0, y: 0, flags: { beep: 'boop' } } ]},
-// ])).toEqual([[
-// { type: 'plant', flags: { foo: 'foo', beep: 'boop' } },
-// { type: 'plant', flags: { foo: 'bar', baz: 'baz' } },
-// { type: 'plant', flags: {} },
-// ]]);
-//
-//});
diff --git a/src/world/proposal.js b/src/world/proposal.js
deleted file mode 100644
index 8baca06..0000000
--- a/src/world/proposal.js
+++ /dev/null
@@ -1,183 +0,0 @@
-import { pairs, deepEqual } from '../util.js';
-
-/* agent structure:
- * {
- * id: string
- * net: network
- * state: network_state
- * x, y: number
- * flags: object
- * }
- */
-
-
-/* cell structure:
- * {
- * type: string
- * flags: object
- * }
- */
-
-/* action structure:
- * {
- * name: string,
- * propose: (agent, output) => proposal[]
- * }
- */
-
-
-/* proposal structure
- * all proposed lattice and agent changes must be included for the proposed action to be valid
- * similarly, if any lattice or agent change creates merge conflicts, the proposal cannot be merged
- * and must be removed
- * {
- * lattice_changes: proposal_lattice_change[]?
- * agent_changes: proposal_agent_change[]?
- * }
- */
-
-/* proposal_lattice_change
- * {
- * x, y: number
- * from: string
- * to: string
- * flags: object?
- * }
- */
-
-/* proposal_agent_change
- * {
- * agent_id: string
- * x, y: number?
- * flags: object?
- * }
- */
-
-
-// check that two flags objects are compatible
-// flags are considered compatible if they do not have any common keys with different values
-function flags_compatible(a, b) {
- if (a === undefined || b === undefined) { return true; }
- const keys = [...new Set(Object.keys(a).concat(Object.keys(b)))];
- return keys.reduce(
- (acc, key) => {
- const eq = (a[key] === undefined || b[key] === undefined) ? true : deepEqual(a[key], b[key]);
- return acc && eq;
- },
- true
- );
-}
-
-
-// return a tuple [conflict, merge]
-// conflict is true if the two lattice_changes are incompatible
-// merge is true if the two lattice changes are identical
-// otherwise they are false
-function lattice_change_conflict(a, b) {
- if (deepEqual(a, b)) {
- // merge
- return [false, true];
- }
- if (
- a.x === b.x &&
- a.y === b.y &&
- (a.to != b.to || !flags_compatible((a.flags || {}), (b.flags || {})))
- ) {
- // conflict!
- return [true, false];
- } else {
- // no conflict c:
- return [false, false];
- }
-}
-
-
-// returns true as long as x & y are both defined
-function pos_exists(a) {
- if (a.x !== undefined && a.y !== undefined) {
- return true;
- } else {
- return false;
- }
-}
-
-
-// check the equality of two objects with (x,y) keys
-function pos_equal(a, b) {
- if (a.x !== b.x) { return false; }
- if (a.y !== b.y) { return false; }
- return true;
-}
-
-
-// agent changes merge if they are identical
-// they conflict if two agents are trying to move to the same tile, or
-// if the same agent is trying to move to two different tiles, or if an agent
-// is being updated to incompatible flags
-//
-//
-function agent_change_conflict(a, b) {
- if (deepEqual(a, b)) {
- // identical: merge
- return [false, true];
- } else if (a.agent_id === b.agent_id) {
- if (
- pos_exists(a) && pos_exists(b) && !pos_equal(a, b)
- ) {
- // same agent, different positions: conflict
- return [true, false];
- } else if (!flags_compatible(a.flags, b.flags)) {
- // same agent, incompatible flags: conflict
- return [true, false];
- } else {
- // no conflict c:
- return [false, false];
- }
- } else {
- // different agents
- if (pos_exists(a) && pos_exists(b) && pos_equal(a, b)) {
- // different agents, same position: conflict
- return [true, false];
- } else {
- // no conflict c:
- return [false, false];
- }
- }
-}
-
-
-// combine lattice_change and agent_change conflict/merge tuples for a pair of proposals
-function proposal_conflict_merge(a, b) {
- const [lattice_conflict, lattice_merge] = pairs(a.lattice_changes || [], b.lattice_changes || []).reduce(
- (acc, [a, b]) => {
- const [conflict, merge] = lattice_change_conflict(a, b);
- return [acc[0] || conflict, acc[1] || merge];
- },
- [false, false]
- );
- const [agent_conflict, agent_merge] = pairs(a.agent_changes || [], b.agent_changes || []).reduce(
- (acc, [a, b]) => {
- const [conflict, merge] = agent_change_conflict(a, b);
- return [acc[0] || conflict, acc[1] || merge];
- },
- [false, false]
- );
- return [lattice_conflict || agent_conflict, lattice_merge || agent_merge];
-}
-
-
-// merge proposals
-// if two sub-updates conflict, they are both omitted from the final merged proposal
-export function proposal_merge(arr, proposal) {
- const conflict_merge = arr.map(x => proposal_conflict_merge(x, proposal));
-
- // if any conflicts are detected then don't merge
- if (conflict_merge.reduce((acc, [c, m]) => acc || c, false)) {
- const conflict_free = arr.filter((x, i) => !conflict_merge[i][0]);
- return conflict_free;
- } else {
- // no conflicts, but need to merge identical actions
- const no_merge = arr.filter((x, i) => !conflict_merge[i][1]);
- return [...no_merge, proposal];
- }
-}
diff --git a/src/world/proposal.test.js b/src/world/proposal.test.js
deleted file mode 100644
index 2f870e6..0000000
--- a/src/world/proposal.test.js
+++ /dev/null
@@ -1,197 +0,0 @@
-import {
- proposal_merge,
-} from './proposal.js';
-
-
-// tile updates
-
-test("proposals changing different tiles don't conflict", () => {
- const a = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable' }],
- };
- const b = {
- lattice_changes: [{ x: 4, y: 4, from: 'empty', to: 'flag' }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a, b]);
-});
-
-
-test("proposals changing the same tile to different states conflict", () => {
- const a = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable' }],
- };
- const b = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'flag' }],
- };
-
- expect(proposal_merge([a], b)).toEqual([]);
-});
-
-
-test("proposals changing the same tile to the same state merge to a single proposal", () => {
- const a = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable' }],
- };
- const b = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable' }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a]);
-});
-
-
-test("proposals with identical tile updates but incompatible tile flags conflict", () => {
- const a = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'a' } }],
- };
- const b = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'b' } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([]);
-});
-
-
-test("proposals with identical tile updates but compatible tile flags do not conflict", () => {
- const a = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'a', r: 'd' } }],
- };
- const b = {
- lattice_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'a', u: 'b' } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a, b]);
-});
-
-
-
-
-test("proposals moving two agents to the same tile conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', x: 4, y: 3 }],
- };
- const b = {
- agent_changes: [{ agent_id: 'bbb', x: 4, y: 3 }],
- };
-
- expect(proposal_merge([a], b)).toEqual([]);
-});
-
-
-// agent updates
-test("proposals moving two agents to different tiles do not conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', x: 4, y: 3 }],
- };
- const b = {
- agent_changes: [{ agent_id: 'bbb', x: 3, y: 3 }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a, b]);
-});
-
-
-test("proposals moving the same agent to different tiles conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', x: 4, y: 3 }],
- };
- const b = {
- agent_changes: [{ agent_id: 'aaa', x: 3, y: 3 }],
- };
-
- expect(proposal_merge([a], b)).toEqual([]);
-});
-
-
-test("proposals moving the same agent to the same tile merge", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', x: 4, y: 3 }],
- };
- const b = {
- agent_changes: [{ agent_id: 'aaa', x: 4, y: 3 }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a]);
-});
-
-
-test("proposals setting flags on different agents do not conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', flags: { frozen: false } }],
- };
-
- const b = {
- agent_changes: [{ agent_id: 'bbb', flags: { frozen: false } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a, b]);
-});
-
-
-test("setting the same agent to compatible flags does not conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', flags: { frozen: false } }],
- };
-
- const b = {
- agent_changes: [{ agent_id: 'aaa', flags: { crumpet: 'hi' } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a, b]);
-});
-
-
-test("setting the same agent to compatible object flags does not conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', flags: { emit: [0, 1, 1, 0] } }],
- };
-
- const b = {
- agent_changes: [{ agent_id: 'aaa', flags: { emit: [0, 1, 1, 0], hi: 4 } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a, b]);
-});
-
-
-test("setting the same agent to incompatible flags does conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', flags: { frozen: false } }],
- };
-
- const b = {
- agent_changes: [{ agent_id: 'aaa', flags: { frozen: true, crumpet: 'hi' } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([]);
-});
-
-
-test("setting the same agent to incompatible object flags does conflict", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', flags: { emit: [0, 1, 1, 0] } }],
- };
-
- const b = {
- agent_changes: [{ agent_id: 'aaa', flags: { emit: [0, 1, 1, 1], hi: 4 } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([]);
-});
-
-
-test("setting the same agent to identical flags merges", () => {
- const a = {
- agent_changes: [{ agent_id: 'aaa', flags: { emit: [0, 1, 1, 0] } }],
- };
-
- const b = {
- agent_changes: [{ agent_id: 'aaa', flags: { emit: [0, 1, 1, 0] } }],
- };
-
- expect(proposal_merge([a], b)).toEqual([a]);
-});
-
-
-
diff --git a/src/world/sense.js b/src/world/sense.js
deleted file mode 100644
index 9b5c7d4..0000000
--- a/src/world/sense.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict';
-
-/* sense structure:
- * {
- * size: number
- * read: function(lattice, agent) -> number[size]
- * }
- */
-
-
-export function sense_read(world, agent, sense) {
- const result = sense.read(world, agent);
- if (result.length !== sense.size) {
- throw new Error(`Expected result of size ${sense.size}, but got ${result.length} instead.`);
- }
- return result;
-}
diff --git a/src/world/sense.test.js b/src/world/sense.test.js
deleted file mode 100644
index 27ee2b5..0000000
--- a/src/world/sense.test.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { sense_read } from './sense.js';
-
-
-test("basic sense works", () => {
- const flag_sense = {
- size: 1,
- read: (world, agent) => {
- const {x, y} = agent;
- return [ world.lattice[y-1][x].type === 'flag' ? 1.0 : 0.0 ]
- },
- };
-
- const lattice = [[ { type: 'flag' } ]];
- const agent = { x: 0, y: 1 };
-
- expect(sense_read({lattice}, agent, flag_sense)).toEqual([1.0]);
-});
-
-
-test("senses throw if the size is incorrect", () => {
- const flag_sense = {
- size: 2,
- read: (world, agent) => {
- const {x, y} = agent;
- return [ world.lattice[y-1][x].type === 'flag' ? 1.0 : 0.0 ]
- },
- }
-
- const lattice = [[ { type: 'flag' } ]];
- const agent = { x: 0, y: 1 };
-
- expect(() => sense_read({lattice}, agent, flag_sense)).toThrow();
-});
diff --git a/src/world/world.js b/src/world/world.js
deleted file mode 100644
index 63d974a..0000000
--- a/src/world/world.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import { lattice_update, lattice_valid, lattice_apply } from './lattice.js';
-import { agent_decide, agent_apply } from './agent.js';
-import { proposal_merge } from './proposal.js';
-
-
-// world structure:
-// {
-// lattice
-// lattice_rules: object
-// agents: agent[]
-// senses: sense[]
-// actions: action[]
-// validity: (function(proposal) => bool)[]
-// }
-
-
-export function world_update(world, postprocess=[]) {
- const lattice_props = lattice_update(world.lattice, world.lattice_rules);
- const intermediate_lattice = lattice_apply(
- world.lattice.map(row => row.map(cell => ({ ...cell, flags: {} }))),
- lattice_props
- );
-
- const decisions = world.agents
- .map(a => agent_decide(world, a, world.senses, world.actions))
- .reduce(
- ([agents, props], [agent, prop]) => [[...agents, agent], [...props, prop]],
- [[], []]
- );
- const intermediate_agents = decisions[0];
- const agent_props = world.validity.reduce(
- (acc, rule) => acc.filter(prop => rule({...world, lattice: intermediate_lattice}, prop)),
- 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 = intermediate_agents.map(a => agent_apply(a, agent_props));
-
- const new_world = {...world, lattice, agents};
- return postprocess.reduce(
- (acc, f) => f(acc),
- new_world
- );
-}