summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/simulation/game.js45
-rw-r--r--src/simulation/lattice_rules.js5
-rw-r--r--src/simulation/lattice_rules.test.js58
-rw-r--r--src/util.js10
4 files changed, 87 insertions, 31 deletions
diff --git a/src/simulation/game.js b/src/simulation/game.js
index ac6b2fd..7a3119d 100644
--- a/src/simulation/game.js
+++ b/src/simulation/game.js
@@ -1,7 +1,7 @@
'use strict';
-import { random_choice, apply } from '../util.js';
+import { random_choice, apply, shuffle } from '../util.js';
import { senses } from './senses.js';
import { actions } from './actions.js';
import { lattice_rules } from './lattice_rules.js';
@@ -21,7 +21,7 @@ function is_corner(size, x, y) {
(y < subsize || y >= 2*subsize)
);
}
-function get_team(size, x, y) {
+export function get_team(size, x, y) {
const subsize = Math.floor(size/3);
if (y < subsize) {
return 0;
@@ -95,7 +95,7 @@ export function create_agent(genome, n_internal) {
return {
id: agent_id++, // !!!! side effect !!!!
net: parse_genome,
- state: [...Array(n_internal)].map(_ => (2*Math.random()) - 1),
+ state: [...Array(n_internal)].map(_ => (2*Math.random()) - 1),
}
}
@@ -106,11 +106,11 @@ const MAX_MUTATIONS = 15;
export function create_team(size, genome_size, n_internal) {
- const [..._, genome] = apply(
+ const genome = apply(
s => mut_genome_insert(s, 4, Math.random(), Math.random(), Math.random()),
genome_size,
[N_INPUT, n_internal, N_OUTPUT, []],
- );
+ ).slice(-1)[0];
const agents = [...Array(size)].map(_ => create_agent(genome, n_internal));
return { agents, genome, score: 0 };
@@ -148,4 +148,39 @@ export function child_team(team, keep=Math.floor(team.size/2)) {
export function create_game(teams, team_indices) {
+ const world = create_world(999, team_indices.map(i => teams[i].agents));
+ return { world, team_indices, time: 0 };
+}
+
+
+export function step_game(game) {
+ return {
+ ...game,
+ world: world_update(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]});
+ return acc;
+ },
+ teams,
+ );
}
diff --git a/src/simulation/lattice_rules.js b/src/simulation/lattice_rules.js
index 80f3221..cc2fc5b 100644
--- a/src/simulation/lattice_rules.js
+++ b/src/simulation/lattice_rules.js
@@ -1,4 +1,5 @@
import { pairs } from '../util.js';
+import { get_team } from './game.js';
function mod(k, n) {
return ((k % n) + n) % n;
@@ -34,6 +35,10 @@ export const lattice_rules = {
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) },
+ }]};
}
},
diff --git a/src/simulation/lattice_rules.test.js b/src/simulation/lattice_rules.test.js
index 7648a68..c285711 100644
--- a/src/simulation/lattice_rules.test.js
+++ b/src/simulation/lattice_rules.test.js
@@ -11,11 +11,17 @@ function apply(f, n, 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: {} };
- const _ = { type: 'empty', flags: {} };
- const l = { type: 'active', flags: { emit: [0, 0, 0, 0, 0, 0, 0, 1] } };
- const d = { type: 'empty', flags: { emit: [0, 0, 0, 0, 0, 0, 0, -1] } };
+ 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 = [
[ _, _, _, _, _ ],
[ _, _, _, _, _ ],
@@ -25,14 +31,14 @@ test("blinker", () => {
];
const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
- expect(world_update(world).lattice).toEqual([
+ expect(clean_team(world_update(world).lattice)).toEqual([
[ _, _, _, _, _ ],
[ _, _, l, _, _ ],
[ _, d, L, d, _ ],
[ _, _, l, _, _ ],
[ _, _, _, _, _ ],
]);
- expect(world_update(world_update(world)).lattice).toEqual([
+ expect(clean_team(world_update(world_update(world)).lattice)).toEqual([
[ _, _, _, _, _ ],
[ _, _, d, _, _ ],
[ _, l, L, l, _ ],
@@ -43,10 +49,10 @@ test("blinker", () => {
test("glider", () => {
- const L = { type: 'active', flags: {} };
- const _ = { type: 'empty', flags: {} };
- const l = { type: 'active', flags: { emit: [0, 0, 0, 0, 0, 0, 0, 1] } };
- const d = { type: 'empty', flags: { emit: [0, 0, 0, 0, 0, 0, 0, -1] } };
+ 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, _, _ ],
@@ -57,8 +63,8 @@ test("glider", () => {
];
const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
- //expect(world_update(world).lattice).toEqual([
- expect(apply(world_update, 1, world).lattice).toEqual([
+ //expect(clean_team(world_update(world).lattice)).toEqual([
+ expect(clean_team(apply(world_update, 1, world).lattice)).toEqual([
[ _, _, _, _, _, _ ],
[ _, _, l, d, _, _ ],
[ _, d, _, L, l, _ ],
@@ -66,7 +72,7 @@ test("glider", () => {
[ _, _, _, _, _, _ ],
[ _, _, _, _, _, _ ],
]);
- expect(apply(world_update, 2, world).lattice).toEqual([
+ expect(clean_team(apply(world_update, 2, world).lattice)).toEqual([
[ _, _, _, _, _, _ ],
[ _, _, d, l, _, _ ],
[ _, _, _, d, L, _ ],
@@ -74,7 +80,7 @@ test("glider", () => {
[ _, _, _, _, _, _ ],
[ _, _, _, _, _, _ ],
]);
- expect(apply(world_update, 3, world).lattice).toEqual([
+ expect(clean_team(apply(world_update, 3, world).lattice)).toEqual([
[ _, _, _, _, _, _ ],
[ _, _, _, d, _, _ ],
[ _, _, l, _, L, _ ],
@@ -82,7 +88,7 @@ test("glider", () => {
[ _, _, _, l, _, _ ],
[ _, _, _, _, _, _ ],
]);
- expect(apply(world_update, 4, world).lattice).toEqual([
+ expect(clean_team(apply(world_update, 4, world).lattice)).toEqual([
[ _, _, _, _, _, _ ],
[ _, _, _, _, _, _ ],
[ _, _, d, _, L, _ ],
@@ -94,8 +100,8 @@ test("glider", () => {
test("beehive", () => {
- const L = { type: 'active', flags: {} };
- const _ = { type: 'empty', flags: {} };
+ const L = { type: 'active', flags: { team: undefined, } };
+ const _ = { type: 'empty', flags: { team: undefined, } };
const lattice = [
[ _, _, _, _, _, _ ],
[ _, _, L, L, _, _ ],
@@ -105,17 +111,17 @@ test("beehive", () => {
];
const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
- //expect(world_update(world).lattice).toEqual([
- expect(apply(world_update, 1, world).lattice).toEqual(lattice);
+ //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: {} };
- const _ = { type: 'empty', flags: {} };
- const l = { type: 'active', flags: { emit: [0, 0, 0, 0, 0, 0, 0, 1] } };
- const d = { type: 'empty', flags: { emit: [0, 0, 0, 0, 0, 0, 0, -1] } };
- const M = { type: 'mutable', flags: {} };
+ 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 = [
[ _, _, _, _, ],
@@ -126,13 +132,13 @@ test("mutables are activated by neighboring actives", () => {
const world = { lattice, lattice_rules, agents: [], senses: [], actions: [], validity: [] };
- expect(apply(world_update, 1, world).lattice).toEqual([
+ expect(clean_team(apply(world_update, 1, world).lattice)).toEqual([
[ _, _, _, _, ],
[ _, L, l, _, ],
[ _, l, l, _, ],
[ _, _, _, _, ],
]);
- expect(apply(world_update, 2, world).lattice).toEqual([
+ expect(clean_team(apply(world_update, 2, world).lattice)).toEqual([
[ _, _, _, _, ],
[ _, L, L, _, ],
[ _, L, L, _, ],
diff --git a/src/util.js b/src/util.js
index ecae45e..f83039f 100644
--- a/src/util.js
+++ b/src/util.js
@@ -50,3 +50,13 @@ export function apply(f, n, x0) {
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;
+}