|
|
|
@ -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 = "<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 { |
|
|
|
|
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 => { |
|
|
|
|