diff options
author | sanine <sanine.not@pm.me> | 2023-11-12 13:59:50 -0600 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-11-12 13:59:50 -0600 |
commit | 95fd952b6d4c496efc0e6236ba2acfcbbeee8ed9 (patch) | |
tree | 2fd28076f9fe119481dcb439865b4076d00fc692 /src | |
parent | 22fe18beac4459427c2b40499afafc7c5c868691 (diff) |
add visualization
Diffstat (limited to 'src')
-rw-r--r-- | src/index.html | 14 | ||||
-rw-r--r-- | src/simulation/actions.js | 1 | ||||
-rw-r--r-- | src/simulation/game.js | 21 | ||||
-rw-r--r-- | src/ui/canvas.js | 11 | ||||
-rw-r--r-- | src/ui/index.js | 72 | ||||
-rw-r--r-- | src/world/agent.js | 1 |
6 files changed, 112 insertions, 8 deletions
diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..6b7ebf7 --- /dev/null +++ b/src/index.html @@ -0,0 +1,14 @@ +<!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/simulation/actions.js b/src/simulation/actions.js index 4ee17d8..3b3e942 100644 --- a/src/simulation/actions.js +++ b/src/simulation/actions.js @@ -8,6 +8,7 @@ const move_forward = { propose: (world, agent, head) => { if (agent.flags.frozen === true) { return []; } if (head[0] > threshold) { + console.log('move forward'); 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 [{ diff --git a/src/simulation/game.js b/src/simulation/game.js index 05ac4a1..978289d 100644 --- a/src/simulation/game.js +++ b/src/simulation/game.js @@ -49,7 +49,11 @@ export function setup_board(size) { return { type: 'immutable', flags: {} }; } else { const team = get_team(size, x, y); - return { type: 'empty', flags: { team } }; + if (Math.random() > 0.95 && get_team(size, x, y) === undefined) { + return { type: 'flag', flags: { team } }; + } else { + return { type: 'empty', flags: { team } }; + } } })); return lattice; @@ -152,8 +156,8 @@ export function child_team(team, keep=Math.floor(team.size/2)) { // } -export function create_game(teams, team_indices) { - const world = create_world(100, team_indices.map(i => teams[i].agents)); +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 }; } @@ -211,17 +215,18 @@ function random_indices(teams) { } let epoch_num = 0; -export function create_epoch(teams) { +export function create_epoch(size, teams) { return { - game: create_game(teams, random_indices(teams)), + game: create_game(size, teams, random_indices(teams)), time: 0, epoch: epoch_num++, // !!!! side effects !!!! + size, teams, } } -const GAME_STEPS = 5000 +const GAME_STEPS = 10000 const EPOCH_STEPS = 200 export function update_epoch(epoch) { @@ -231,7 +236,7 @@ export function update_epoch(epoch) { return { ...epoch, teams: finish_game(epoch.teams, epoch.game), - game: create_game(epoch.teams, random_indices(epoch.teams)), + game: create_game(size, epoch.teams, random_indices(epoch.teams)), time: epoch.time+1 }; } else { @@ -245,6 +250,6 @@ export function update_epoch(epoch) { .flat(); const new_teams = [...Array(epoch.teams.length)] .reduce((acc) => child_team(random_choice(source_teams)), []); - return create_epoch(new_teams); + return create_epoch(epoch.size, new_teams); } } diff --git a/src/ui/canvas.js b/src/ui/canvas.js new file mode 100644 index 0000000..b04b966 --- /dev/null +++ b/src/ui/canvas.js @@ -0,0 +1,11 @@ +'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 new file mode 100644 index 0000000..b710b48 --- /dev/null +++ b/src/ui/index.js @@ -0,0 +1,72 @@ +import { draw } from './canvas.js'; +import { create_team, create_epoch, update_epoch } from '../simulation/game.js'; + + +const start_teams = [...Array(5)].map(x => create_team(8, 50, 50)); +let epoch = create_epoch(60, start_teams); + + +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/world/agent.js b/src/world/agent.js index 3faa3c8..dbfdf5d 100644 --- a/src/world/agent.js +++ b/src/world/agent.js @@ -7,6 +7,7 @@ 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(agent, result); const new_agent = { ...agent, state }; const [proposals, _] = actions.reduce( |