summaryrefslogtreecommitdiff
path: root/src/simulation/game.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/simulation/game.js')
-rw-r--r--src/simulation/game.js255
1 files changed, 0 insertions, 255 deletions
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);
- }
-}