From 661847f269030c0cbaa681e2d4697873f891cdec Mon Sep 17 00:00:00 2001 From: sanine Date: Sat, 11 Nov 2023 23:05:12 -0600 Subject: fix team information for empty cells --- src/simulation/game.js | 45 ++++++++++++++++++++++++---- src/simulation/lattice_rules.js | 5 ++++ src/simulation/lattice_rules.test.js | 58 ++++++++++++++++++++---------------- src/util.js | 10 +++++++ 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; +} -- cgit v1.2.1