From 0fb8f8220fe26cb72dade1bac774d7d134030486 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Sat, 2 Dec 2017 17:02:28 +0100 Subject: [PATCH] Test runner. --- LemmingSim.html | 12 +++- lemming_sim_student.js | 137 ++++++++++++++++++++++++++++++++++++++--- mouse.js | 15 +++-- 3 files changed, 148 insertions(+), 16 deletions(-) diff --git a/LemmingSim.html b/LemmingSim.html index f9aaa4d..a297aed 100644 --- a/LemmingSim.html +++ b/LemmingSim.html @@ -33,7 +33,7 @@ along with this program. If not, see .
- +
@@ -46,8 +46,16 @@ along with this program. If not, see . - + + + + + + + +
Simulation properties
Simulation steps:
Sensor values
Display
Robot Display
Experiments
+
diff --git a/lemming_sim_student.js b/lemming_sim_student.js index 2a5d542..fe2d0ab 100644 --- a/lemming_sim_student.js +++ b/lemming_sim_student.js @@ -198,11 +198,20 @@ class Simulation { this.bay = new Bay() } - step() { + destroy() { + this.stop() + Matter.Engine.clear(this.engine) + this.elem.removeMouseTracker() + this.bay.destroy() + } + step(draw = true) { + // console.log("stepping", this.id) // advance simulation by one step (except MatterJS engine's physics) if (this.curSteps < simInfo.maxSteps) { - this.bay.repaint(); - this.draw(); + if (draw) { + this.bay.repaint(); + this.draw(); + } this.robots.forEach(robot => { robot.updateSensors(); robot.move(); @@ -214,7 +223,7 @@ class Simulation { }) // count and display number of steps this.curSteps += 1; - document.getElementById("SimStepLabel").innerHTML = + if (draw) document.getElementById("SimStepLabel").innerHTML = padnumber(this.curSteps, 5) + ' of ' + padnumber(simInfo.maxSteps, 5); @@ -262,6 +271,19 @@ class Simulation { if (this.doContinue) this.stop() else this.start() } + runSteps(steps) { + this.stop() + const sensorDebug_ = simInfo.debugSensors + simInfo.debugSensors = false + for(let i = 0; i < steps-1; i++) { + Matter.Engine.update(this.engine, 1000/60); + this.step(false) + } + simInfo.debugSensors = sensorDebug_ + Matter.Engine.update(this.engine, 1000/60); + this.step(true) + + } } function init() { // called once when loading HTML file // the pathseg polyfill will run in the html document @@ -269,14 +291,106 @@ function init() { // called once when loading HTML file // regular function and inject it into the svg using eval const svg = document.getElementById('robotbodySVG') svg.contentWindow.eval('(' + window.pathseg.toString() + ')()') - sim = new Simulation() - sim.init() - sim.addRobot(new Lemming({ - color: [255, 255, 255], // color of the robot shape - init: {x: 50, y: 50, angle: 0}, // initial position and orientation - })) + resultsTable = new ResultsTable(document.getElementById("results")) + resultsTable.clear() + initSimulation() sim.start() }; +function initSimulation() { + if (sim) { + sim.stop() + sim.destroy() + } + simInfo.boxFric = +document.getElementById("boxFric").value + simInfo.wanderRate = +document.getElementById("wanderRate").value + sim = new Simulation() + sim.init() + let noRobots = +document.getElementById('robotAmnt').value + while(noRobots--) { + sim.addRobot(new Lemming({ + // random color + color: [Math.random()*255, Math.random()*255, Math.random()*255], // color of the robot shape + init: {x: 50*noRobots + 50, y: 50, angle: 0}, // initial position and orientation + })) + } +} +function promiseTimeout(time) { + return new Promise((yes, no) => setTimeout(yes, time)) +} +function blocksSorted(sim) { + const {width, height} = sim + return sim.world.composites[0].bodies.every(({position: {x, y}, color: [r, g, b]}) => { + const dist = Math.min(x - 10, width - 10 - x, y - 10, height - 10 - y) + // if red block: ignore + if (r > g + b) return true + // blue block + if (b > r + g) { + const dist = Math.min(x - 10, width - 10 - x, y - 10, height - 10 - y) + // distance from wall + return dist < 70 + } else { + console.warn("unknown block type") + } + }) +} +function redblocksatwall(sim) { + const {width, height} = sim + let result = 0 + sim.world.composites[0].bodies.forEach(({position: {x, y}, color: [r, g, b]}) => { + const dist = Math.min(x - 10, width - 10 - x, y - 10, height - 10 - y) + // if red block: count + if (r > g + b) { + if (dist < 70) result++ + } + }) + return result +} +async function experimentRunner(stopCondition=blocksSorted) { + initSimulation() + while(!stopCondition(sim) && sim.curSteps < simInfo.maxSteps) { + sim.runSteps(500) + await promiseTimeout(50) + } + + // save data + const rv = { + steps: sim.curSteps, + done: stopCondition(sim), + redblocks: redblocksatwall(sim), + robots: sim.robots.length + } + sim.destroy() + sim = null + return rv +} +async function runExperiments() { + let count = +document.getElementById('ffwdtimes').value + while(count--) { + await (experimentRunner().then(x => resultsTable.add(x))) + } +} + +class ResultsTable { + constructor(elem) { + this.elem = elem + } + clear() { + this.elem.innerHTML = "Experiment Results" + const tr = document.createElement('tr') + const headers = ["robots", "steps", "sorted", "red blocks@wall"] + headers.forEach(txt => { + tr.appendChild(document.createElement('th')).appendChild(document.createTextNode(txt)) + }) + this.elem.appendChild(tr) + } + add({steps, done, redblocks, robots}) { + const tr = document.createElement('tr') + ;[robots, steps, done, redblocks].forEach(txt => { + tr.appendChild(document.createElement('td')).appendChild(document.createTextNode(txt)) + }) + this.elem.appendChild(tr) + } +} class Bay { constructor() { @@ -288,6 +402,9 @@ class Bay { this.elem.style.backgroundColor = 'silver' addMouseTracker(this.elem); } + destroy() { + this.elem.removeMouseTracker() + } load(robot) { this.robot = robot robot.sensors.forEach(sensor => { diff --git a/mouse.js b/mouse.js index f49af96..4440288 100644 --- a/mouse.js +++ b/mouse.js @@ -20,7 +20,7 @@ function addMouseTracker(elem) { elem.mouse.interactiveElements = []; elem.getInteractiveElement = findElement; elem.addEventListener("mousedown", - function(event) { + elem.mouse.downListener = function(event) { this.mouse.buttons = event.buttons; this.mouse.x = event.offsetX; this.mouse.y = event.offsetY; @@ -30,7 +30,7 @@ function addMouseTracker(elem) { } }); elem.addEventListener("mouseup", - function(event) { + elem.mouse.upListener = function(event) { this.mouse.buttons = event.buttons; if (this.mouse.targetElement != null) { this.mouse.targetElement.endAction(event); @@ -38,7 +38,7 @@ function addMouseTracker(elem) { } }); elem.addEventListener("mousemove", - function(event) { + elem.mouse.moveListener = function(event) { this.mouse.hasFocus = true; this.mouse.x = event.offsetX; this.mouse.y = event.offsetY; @@ -54,12 +54,19 @@ function addMouseTracker(elem) { } }); elem.addEventListener("mouseout", - function(event) { + elem.mouse.outListener = function(event) { this.mouse.hasFocus = false; }); elem.addInteractiveElement = function(obj) { if (!obj.mouseHit) obj.mouseHit = mouseHit this.mouse.interactiveElements.push(obj); + elem.removeMouseTracker = function() { + elem.removeEventListener("mousedown", this.mouse.downListener) + elem.removeEventListener("mouseup", this.mouse.upListener) + elem.removeEventListener("mousemove", this.mouse.moveListener) + elem.removeEventListener("mouseout", this.mouse.outListener) + delete elem.mouse + } }; } function mouseHit(x, y) {