Test runner.
parent
ddc8b023d4
commit
0fb8f8220f
|
@ -33,7 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
<object id="robotbodySVG" type="image/svg+xml" data="robotbody.svg" width=0 ></object>
|
<object id="robotbodySVG" type="image/svg+xml" data="robotbody.svg" width=0 ></object>
|
||||||
<canvas id="arenaLemming" width="450" height="450" style="cursor: initial;"></canvas>
|
<canvas id="arenaLemming" width="450" height="450" style="cursor: initial;"></canvas>
|
||||||
<br>
|
<br>
|
||||||
<table border="0">
|
<table border="0" style="float: left; ">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><th colspan="2" align="center">Simulation properties</th><td></td></tr>
|
<tr><th colspan="2" align="center">Simulation properties</th><td></td></tr>
|
||||||
<tr><td>Simulation steps: <span id="SimStepLabel"></span></td></tr>
|
<tr><td>Simulation steps: <span id="SimStepLabel"></span></td></tr>
|
||||||
|
@ -46,8 +46,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
</td>
|
</td>
|
||||||
<td>Sensor values<span id="SensorLabel"></span></td>
|
<td>Sensor values<span id="SensorLabel"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr><th colspan="2" align="center">Display</th></tr>
|
<tr><th colspan="2" align="center">Robot Display</th></tr>
|
||||||
<tr><td colspan="2" id="bayDisplay"></td></tr>
|
<tr><td colspan="2" id="bayDisplay"></td></tr>
|
||||||
|
<tr><th colspan="2">Experiments</th></tr>
|
||||||
|
<tr><td><label for="robotAmnt">Robot Count</label></td><td><input id="robotAmnt" type="number" value="1" min="0" max="3" /></td></tr>
|
||||||
|
<tr><td><label for="ffwdtimes">nr of experiments</label></td><td><input id="ffwdtimes" type="number" value="10" min="1" max="20" /></td></tr>
|
||||||
|
<tr><td><label for="wanderRate">robot wander rate</label></td><td><input id="wanderRate" type="number" value="0.0002" step="0.0001"></td></tr>
|
||||||
|
<tr><td><label for="boxFric">box friction</label></td><td><input id="boxFric" type="number" value="0.001" step="0.0005" min=0></td></tr>
|
||||||
|
<tr><td><input type="button" value="Restart Slow" onclick="initSimulation(); sim.start()" /><input type="button" value="toggle pause" onclick="sim.toggle()"></td><td><input type="button" value="run experiments" onclick="runExperiments()" /><input type="button" value="clear" onclick="resultsTable.clear()" /></td></tr>
|
||||||
|
<tr><td></td></tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
|
<table id="results"></table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -198,11 +198,20 @@ class Simulation {
|
||||||
|
|
||||||
this.bay = new Bay()
|
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)
|
// advance simulation by one step (except MatterJS engine's physics)
|
||||||
if (this.curSteps < simInfo.maxSteps) {
|
if (this.curSteps < simInfo.maxSteps) {
|
||||||
this.bay.repaint();
|
if (draw) {
|
||||||
this.draw();
|
this.bay.repaint();
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
this.robots.forEach(robot => {
|
this.robots.forEach(robot => {
|
||||||
robot.updateSensors();
|
robot.updateSensors();
|
||||||
robot.move();
|
robot.move();
|
||||||
|
@ -214,7 +223,7 @@ class Simulation {
|
||||||
})
|
})
|
||||||
// count and display number of steps
|
// count and display number of steps
|
||||||
this.curSteps += 1;
|
this.curSteps += 1;
|
||||||
document.getElementById("SimStepLabel").innerHTML =
|
if (draw) document.getElementById("SimStepLabel").innerHTML =
|
||||||
padnumber(this.curSteps, 5) +
|
padnumber(this.curSteps, 5) +
|
||||||
' of ' +
|
' of ' +
|
||||||
padnumber(simInfo.maxSteps, 5);
|
padnumber(simInfo.maxSteps, 5);
|
||||||
|
@ -262,6 +271,19 @@ class Simulation {
|
||||||
if (this.doContinue) this.stop()
|
if (this.doContinue) this.stop()
|
||||||
else this.start()
|
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
|
function init() { // called once when loading HTML file
|
||||||
// the pathseg polyfill will run in the html document
|
// 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
|
// regular function and inject it into the svg using eval
|
||||||
const svg = document.getElementById('robotbodySVG')
|
const svg = document.getElementById('robotbodySVG')
|
||||||
svg.contentWindow.eval('(' + window.pathseg.toString() + ')()')
|
svg.contentWindow.eval('(' + window.pathseg.toString() + ')()')
|
||||||
sim = new Simulation()
|
resultsTable = new ResultsTable(document.getElementById("results"))
|
||||||
sim.init()
|
resultsTable.clear()
|
||||||
sim.addRobot(new Lemming({
|
initSimulation()
|
||||||
color: [255, 255, 255], // color of the robot shape
|
|
||||||
init: {x: 50, y: 50, angle: 0}, // initial position and orientation
|
|
||||||
}))
|
|
||||||
sim.start()
|
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 = "<tr><th colspan=4>Experiment Results</th></tr>"
|
||||||
|
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 {
|
class Bay {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -288,6 +402,9 @@ class Bay {
|
||||||
this.elem.style.backgroundColor = 'silver'
|
this.elem.style.backgroundColor = 'silver'
|
||||||
addMouseTracker(this.elem);
|
addMouseTracker(this.elem);
|
||||||
}
|
}
|
||||||
|
destroy() {
|
||||||
|
this.elem.removeMouseTracker()
|
||||||
|
}
|
||||||
load(robot) {
|
load(robot) {
|
||||||
this.robot = robot
|
this.robot = robot
|
||||||
robot.sensors.forEach(sensor => {
|
robot.sensors.forEach(sensor => {
|
||||||
|
|
15
mouse.js
15
mouse.js
|
@ -20,7 +20,7 @@ function addMouseTracker(elem) {
|
||||||
elem.mouse.interactiveElements = [];
|
elem.mouse.interactiveElements = [];
|
||||||
elem.getInteractiveElement = findElement;
|
elem.getInteractiveElement = findElement;
|
||||||
elem.addEventListener("mousedown",
|
elem.addEventListener("mousedown",
|
||||||
function(event) {
|
elem.mouse.downListener = function(event) {
|
||||||
this.mouse.buttons = event.buttons;
|
this.mouse.buttons = event.buttons;
|
||||||
this.mouse.x = event.offsetX;
|
this.mouse.x = event.offsetX;
|
||||||
this.mouse.y = event.offsetY;
|
this.mouse.y = event.offsetY;
|
||||||
|
@ -30,7 +30,7 @@ function addMouseTracker(elem) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
elem.addEventListener("mouseup",
|
elem.addEventListener("mouseup",
|
||||||
function(event) {
|
elem.mouse.upListener = function(event) {
|
||||||
this.mouse.buttons = event.buttons;
|
this.mouse.buttons = event.buttons;
|
||||||
if (this.mouse.targetElement != null) {
|
if (this.mouse.targetElement != null) {
|
||||||
this.mouse.targetElement.endAction(event);
|
this.mouse.targetElement.endAction(event);
|
||||||
|
@ -38,7 +38,7 @@ function addMouseTracker(elem) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
elem.addEventListener("mousemove",
|
elem.addEventListener("mousemove",
|
||||||
function(event) {
|
elem.mouse.moveListener = function(event) {
|
||||||
this.mouse.hasFocus = true;
|
this.mouse.hasFocus = true;
|
||||||
this.mouse.x = event.offsetX;
|
this.mouse.x = event.offsetX;
|
||||||
this.mouse.y = event.offsetY;
|
this.mouse.y = event.offsetY;
|
||||||
|
@ -54,12 +54,19 @@ function addMouseTracker(elem) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
elem.addEventListener("mouseout",
|
elem.addEventListener("mouseout",
|
||||||
function(event) {
|
elem.mouse.outListener = function(event) {
|
||||||
this.mouse.hasFocus = false;
|
this.mouse.hasFocus = false;
|
||||||
});
|
});
|
||||||
elem.addInteractiveElement = function(obj) {
|
elem.addInteractiveElement = function(obj) {
|
||||||
if (!obj.mouseHit) obj.mouseHit = mouseHit
|
if (!obj.mouseHit) obj.mouseHit = mouseHit
|
||||||
this.mouse.interactiveElements.push(obj);
|
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) {
|
function mouseHit(x, y) {
|
||||||
|
|
Loading…
Reference in New Issue