From d1bc29694219a3d3d34e6dcc696ce249c7540fcd Mon Sep 17 00:00:00 2001 From: brian cully Date: Tue, 23 Dec 2025 19:52:31 -0500 Subject: js: begin migration to multi-bot life --- site/main.mjs | 112 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) (limited to 'site') diff --git a/site/main.mjs b/site/main.mjs index 5a4d94a..fd9abb6 100644 --- a/site/main.mjs +++ b/site/main.mjs @@ -11,6 +11,8 @@ const TICK_RATE_SELECTOR = '#tick-rate-select'; const RUN_BUTTON_SELECTOR = '#run'; const FPS_SELECTOR = '#fps'; +const ROBO_RADIUS = 25; + function clamp(radius, x, y, width, height) { const xx = Math.min(Math.max(radius, x), width-radius); const yy = Math.min(Math.max(radius, y), height-radius); @@ -21,83 +23,90 @@ function renderRobo(ctx, x, y) { ctx.fillStyle = 'rgb(200 0 0)'; ctx.beginPath(); //ctx.arc(Math.floor(x), Math.floor(y), 25, 0, 2 * Math.PI); - ctx.arc(x, y, 25, 0, 2 * Math.PI); + ctx.arc(x, y, ROBO_RADIUS, 0, 2 * Math.PI); ctx.fill(); } -function renderArena(robos, delta=0) { +function renderArena(robos, now=0) { // interpolation factor for smoother movement independent of tick // rate, but never tween more than 1 tick. - const timeScale = Math.min(delta / MS_PER_TICK, 1.0); const canvas = document.querySelector(CANVAS_SELECTOR); const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); robos.forEach(robo => { + const delta = robo.lastTick ? now - robo.lastTick : 0; + const timeScale = Math.min(delta / MS_PER_TICK, 1.0); let [x, y] = [robo.x, robo.y]; if (delta > 0) { const [velx, vely] = [ robo.speedx, robo.speedy ].map(x => timeScale * x); - [x, y] = clamp(25, robo.x + velx, robo.y + vely, canvas.width, canvas.height); + [x, y] = clamp(ROBO_RADIUS, robo.x + velx, robo.y + vely, canvas.width, canvas.height); } renderRobo(ctx, x, y); }); } async function loaded() { + Inspector.register(); + const canvas = document.querySelector(CANVAS_SELECTOR); - const roboWorker = new Worker('robo.mjs', { type: 'module' }); - const robo = { - worker: roboWorker, + const robos = [{ + worker: new Worker('robo.mjs', { type: 'module' }), lastTick: undefined, - x: 132, - y: 25, + x: ROBO_RADIUS + Math.floor(Math.random() * (canvas.width - ROBO_RADIUS)), + y: ROBO_RADIUS + Math.floor(Math.random() * (canvas.height - ROBO_RADIUS)), heading: 0, speed: 0, speedx: 0, speedy: 0, - }; - roboWorker.onmessage = e => { - const { kind, res, trans } = e.data; - switch (kind) { - case 'compile': - if (res) { - renderArena([robo]); - document.querySelector('x-inspector').render(trans, true); + }]; + robos.forEach((robo, i) => { + robo.worker.onmessage = e => { + const { kind, res, trans } = e.data; + switch (kind) { + case 'compile': + if (res) { + renderArena(robos); + document.querySelectorAll(Inspector.name)[i].render(trans, true); + } + break; + case 'tick': + robo.lastTick = document.timeline.currentTime; + + [robo.x, robo.y] = clamp(ROBO_RADIUS, robo.x + robo.speedx, robo.y + robo.speedy, canvas.width, canvas.height); + renderArena(robos); + + robo.heading = trans.vars.heading; + robo.speed = trans.vars.speed; + [robo.speedx, robo.speedy] = [ + Math.cos(2 * Math.PI * robo.heading / 360), + Math.sin(2 * Math.PI * robo.heading / 360) + ].map(x => robo.speed * x); + + document.querySelectorAll(Inspector.name)[i].render(trans); + break; + default: + console.error('invalid message from robo worker', e.data); } - break; - case 'tick': - robo.lastTick = document.timeline.currentTime; - - const [x, y] = clamp(25, robo.x + robo.speedx, robo.y + robo.speedy, canvas.width, canvas.height); - robo.x = x; - robo.y = y; - renderArena([robo]); - - robo.heading = trans.vars.heading; - robo.speed = trans.vars.speed; - const [speedx, speedy] = [ - Math.cos(2 * Math.PI * robo.heading / 360), - Math.sin(2 * Math.PI * robo.heading / 360) - ].map(x => robo.speed * x); - robo.speedx = speedx; - robo.speedy = speedy; - - document.querySelector('x-inspector').render(trans); - break; - default: - console.error('invalid message from robo worker', e.data); - } - }; - roboWorker.onerror = e => { - console.error('error in roboWorker', e); - }; + }; + robo.worker.onerror = e => { + console.error('error in roboWorker', e); + }; + }); + + document.querySelectorAll(Inspector.name).forEach((elt, i) => { + elt.addEventListener(Inspector.compileRequest, e => { + console.debug('compiling', e.detail.text); + robos[i].worker.postMessage({ kind: 'compile', text: e.detail.text }); + }); + }); document.querySelector(TICK_BUTTON_SELECTOR).onclick = e => { console.debug('tick clicked', e); - roboWorker.postMessage({ kind: 'tick' }); + robos.forEach(robo => robo.worker.postMessage({ kind: 'tick' })); }; document.querySelector(TICK_RATE_SELECTOR).onchange = e => { @@ -120,8 +129,7 @@ async function loaded() { const ms = t - lastTime; if (ms > 0) { lastTime = t; - const delta = robo.lastTick ? t - robo.lastTick : 0; - renderArena([robo], delta); + renderArena(robos, t); const fps = document.querySelector(FPS_SELECTOR); fps.textContent = ms > 0 ? Math.floor(1_000 / ms) : '0'; } @@ -133,7 +141,7 @@ async function loaded() { } self.setTimeout(timeout, MS_PER_TICK); - robo.worker.postMessage({ kind: 'tick' }); + robos.forEach(robo => robo.worker.postMessage({ kind: 'tick' })); } document.querySelector(RUN_BUTTON_SELECTOR).onclick = e => { @@ -153,14 +161,6 @@ async function loaded() { document.querySelector(FPS_SELECTOR).textContent = 'n/a'; } } - - Inspector.register(); - document.querySelectorAll(Inspector.name).forEach(elt => { - elt.addEventListener(Inspector.compileRequest, e => { - console.debug('compiling', e.detail.text); - roboWorker.postMessage({ kind: 'compile', text: e.detail.text }); - }); - }); } document.addEventListener('DOMContentLoaded', loaded); -- cgit v1.3