summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-11-12 13:59:50 -0600
committersanine <sanine.not@pm.me>2023-11-12 13:59:50 -0600
commit95fd952b6d4c496efc0e6236ba2acfcbbeee8ed9 (patch)
tree2fd28076f9fe119481dcb439865b4076d00fc692
parent22fe18beac4459427c2b40499afafc7c5c868691 (diff)
add visualization
-rw-r--r--src/index.html14
-rw-r--r--src/simulation/actions.js1
-rw-r--r--src/simulation/game.js21
-rw-r--r--src/ui/canvas.js11
-rw-r--r--src/ui/index.js72
-rw-r--r--src/world/agent.js1
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(