Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show user times #82

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
20 changes: 17 additions & 3 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ var createParamsUrl = function(current, overrides) {

$(function() {
var tsKey = "elevatorTimeScale";
var tmKey = "showTimer";
var editor = createEditor();

var params = {};
Expand All @@ -141,12 +142,24 @@ $(function() {
var elevatorTempl = document.getElementById("elevator-template").innerHTML.trim();
var elevatorButtonTempl = document.getElementById("elevatorbutton-template").innerHTML.trim();
var userTempl = document.getElementById("user-template").innerHTML.trim();
var statsTempl = document.getElementById("stats-template").innerHTML.trim();
var challengeTempl = document.getElementById("challenge-template").innerHTML.trim();
var feedbackTempl = document.getElementById("feedback-template").innerHTML.trim();
var codeStatusTempl = document.getElementById("codestatus-template").innerHTML.trim();

var app = riot.observable({});
var showTimer = (function() {
var __showTimer = localStorage.getItem(tmKey).toString() || "Commute";
var accessor = function() {
if (arguments.length > 0) {
__showTimer = arguments[0].toString();
localStorage.setItem(tmKey, __showTimer);
} else {
return __showTimer;
}
};
return accessor;
})();

app.worldController = createWorldController(1.0 / 60.0);
app.worldController.on("code_error", function(e) {
console.log("World raised code error", e);
Expand Down Expand Up @@ -175,9 +188,10 @@ $(function() {
app.currentChallengeIndex = challengeIndex;
app.world = app.worldCreator.createWorld(challenges[challengeIndex].options);
window.world = app.world;
world.showTimer = showTimer;

clearAll([$world, $stats, $feedback]);
presentStats($stats, app.world, statsTempl);
clearAll([$world, $feedback]);
presentStats($stats, app.world);
presentChallenge($challenge, challenges[challengeIndex], app, app.world, app.worldController, challengeIndex + 1, challengeTempl);
presentWorld($world, app.world, floorTempl, elevatorTempl, elevatorButtonTempl, userTempl);

Expand Down
32 changes: 16 additions & 16 deletions challenges.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,25 @@ var requireUserCountWithinTime = function(userCount, timeLimit) {
};
};

var requireUserCountWithMaxWaitTime = function(userCount, maxWaitTime) {
var requireUserCountWithMaxCommuteTime = function(userCount, maxCommuteTime) {
return {
description: "Transport <span class='emphasis-color'>" + userCount + "</span> people and let no one wait more than <span class='emphasis-color'>" + maxWaitTime.toFixed(1) + "</span> seconds",
description: "Transport <span class='emphasis-color'>" + userCount + "</span> people and let no one take more than <span class='emphasis-color'>" + maxCommuteTime.toFixed(1) + "</span> seconds",
evaluate: function(world) {
if(world.maxWaitTime >= maxWaitTime || world.transportedCounter >= userCount) {
return world.maxWaitTime <= maxWaitTime && world.transportedCounter >= userCount;
if(world.maxCommuteTime >= maxCommuteTime || world.transportedCounter >= userCount) {
return world.maxCommuteTime <= maxCommuteTime && world.transportedCounter >= userCount;
} else {
return null;
}
}
};
};

var requireUserCountWithinTimeWithMaxWaitTime = function(userCount, timeLimit, maxWaitTime) {
var requireUserCountWithinTimeWithMaxCommuteTime = function(userCount, timeLimit, maxCommuteTime) {
return {
description: "Transport <span class='emphasis-color'>" + userCount + "</span> people in <span class='emphasis-color'>" + timeLimit.toFixed(0) + "</span> seconds or less and let no one wait more than <span class='emphasis-color'>" + maxWaitTime.toFixed(1) + "</span> seconds",
description: "Transport <span class='emphasis-color'>" + userCount + "</span> people in <span class='emphasis-color'>" + timeLimit.toFixed(0) + "</span> seconds or less and let no one wait more than <span class='emphasis-color'>" + maxCommuteTime.toFixed(1) + "</span> seconds",
evaluate: function(world) {
if(world.elapsedTime >= timeLimit || world.maxWaitTime >= maxWaitTime || world.transportedCounter >= userCount) {
return world.elapsedTime <= timeLimit && world.maxWaitTime <= maxWaitTime && world.transportedCounter >= userCount;
if(world.elapsedTime >= timeLimit || world.maxCommuteTime >= maxCommuteTime || world.transportedCounter >= userCount) {
return world.elapsedTime <= timeLimit && world.maxCommuteTime <= maxCommuteTime && world.transportedCounter >= userCount;
} else {
return null;
}
Expand Down Expand Up @@ -67,21 +67,21 @@ var challenges = [
,{options: {floorCount: 6, elevatorCount: 4, spawnRate: 1.7}, condition: requireUserCountWithinTime(100, 68)}
,{options: {floorCount: 4, elevatorCount: 2, spawnRate: 0.8}, condition: requireUserCountWithinMoves(40, 60)}
,{options: {floorCount: 3, elevatorCount: 3, spawnRate: 3.0}, condition: requireUserCountWithinMoves(100, 63)}
,{options: {floorCount: 6, elevatorCount: 2, spawnRate: 0.4, elevatorCapacities: [5]}, condition: requireUserCountWithMaxWaitTime(50, 21)}
,{options: {floorCount: 7, elevatorCount: 3, spawnRate: 0.6}, condition: requireUserCountWithMaxWaitTime(50, 20)}
,{options: {floorCount: 6, elevatorCount: 2, spawnRate: 0.4, elevatorCapacities: [5]}, condition: requireUserCountWithMaxCommuteTime(50, 21)}
,{options: {floorCount: 7, elevatorCount: 3, spawnRate: 0.6}, condition: requireUserCountWithMaxCommuteTime(50, 20)}

,{options: {floorCount: 13, elevatorCount: 2, spawnRate: 1.1, elevatorCapacities: [4,10]}, condition: requireUserCountWithinTime(50, 70)}

,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.1}, condition: requireUserCountWithMaxWaitTime(60, 19)}
,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.1}, condition: requireUserCountWithMaxWaitTime(80, 17)}
,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.1, elevatorCapacities: [5]}, condition: requireUserCountWithMaxWaitTime(100, 15)}
,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.0, elevatorCapacities: [6]}, condition: requireUserCountWithMaxWaitTime(110, 15)}
,{options: {floorCount: 8, elevatorCount: 6, spawnRate: 0.9}, condition: requireUserCountWithMaxWaitTime(120, 14)}
,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.1}, condition: requireUserCountWithMaxCommuteTime(60, 19)}
,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.1}, condition: requireUserCountWithMaxCommuteTime(80, 17)}
,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.1, elevatorCapacities: [5]}, condition: requireUserCountWithMaxCommuteTime(100, 15)}
,{options: {floorCount: 9, elevatorCount: 5, spawnRate: 1.0, elevatorCapacities: [6]}, condition: requireUserCountWithMaxCommuteTime(110, 15)}
,{options: {floorCount: 8, elevatorCount: 6, spawnRate: 0.9}, condition: requireUserCountWithMaxCommuteTime(120, 14)}

,{options: {floorCount: 12, elevatorCount: 4, spawnRate: 1.4, elevatorCapacities: [5,10]}, condition: requireUserCountWithinTime(70, 80)}
,{options: {floorCount: 21, elevatorCount: 5, spawnRate: 1.9, elevatorCapacities: [10]}, condition: requireUserCountWithinTime(110, 80)}

,{options: {floorCount: 21, elevatorCount: 8, spawnRate: 1.5, elevatorCapacities: [6,8]}, condition: requireUserCountWithinTimeWithMaxWaitTime(2675, 1800, 45)}
,{options: {floorCount: 21, elevatorCount: 8, spawnRate: 1.5, elevatorCapacities: [6,8]}, condition: requireUserCountWithinTimeWithMaxCommuteTime(2675, 1800, 45)}

,{options: {floorCount: 21, elevatorCount: 8, spawnRate: 1.5, elevatorCapacities: [6,8]}, condition: requireDemo()}
];
Expand Down
40 changes: 29 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@


<script type="text/template" id="user-template">
<i class="movable fa user fa-{u.displayType} {state}" style="left: {u.worldX}px; top: {u.worldY}px;"></i>
<i class="movable fa user fa-{u.displayType} {state}" style="left: {u.worldX}px; top: {u.worldY}px;">
<span class="stackable timer">{timer}</span>
</i>
</script>

<script type="text/template" id="floor-template">
Expand Down Expand Up @@ -66,15 +68,6 @@ <h2 class="emphasis-color">{title}</h2>
</div>
</script>

<script type="text/template" id="stats-template">
<div><span class="key">Transported</span><span class="value">{transportedCounter}</span></div>
<div><span class="key">Elapsed time</span><span class="value">{elapsedTime}s</span></div>
<div><span class="key">Transported/s</span><span class="value">{transportedPerSec}</span></div>
<div><span class="key">Avg waiting time</span><span class="value">{avgWaitTime}s</span></div>
<div><span class="key">Max waiting time</span><span class="value">{maxWaitTime}s</span></div>
<div><span class="key" title="Number of floors that have been travelled by elevators">Moves</span><span class="value">{moveCount}</span></div>
</script>

<script type="text/template" id="challenge-template">
<div class="left">
<h3>Challenge #{num}: {challenge.condition.description}</h3>
Expand Down Expand Up @@ -130,7 +123,32 @@ <h2>Your browser does not appear to support JavaScript. This page contains a bro
<div class="feedbackcontainer"></div>
<div class="innerworld">
</div>
<div class="statscontainer"></div>
<div class="statscontainer">
<div><span class="key">People</span><span class="value" id="spawnedCounter"></span></div>
<div><span class="key">Transported</span><span class="value" id="transportedCounter"></span></div>
<div><span class="key">Elapsed time</span><span class="value" id="elapsedTime"></span></div>
<div><span class="key">Transported/s</span><span class="value" id="transportedPerSec"></span></div>
<div><span class="key" title="Number of floors that have been travelled by elevators">Moves</span><span class="value" id="moveCount"></span></div>
<table style="width:100%">
<thead>
<th class="invisible">Time</th><th scope="col">Avg</th><th scope="col">Max</th>
</thead>
<tbody>
<tr class="set-timer-shown" id="Commute">
<td><span class="key"><i class="fa fa-clock-o"/></i>Commute time</span></td>
<td><span class="value" id="avgCommuteTime"></span></td>
<td><span class="value" id="maxCommuteTime"></span></td></tr>
<tr class="set-timer-shown" id="Wait">
<td><span class="key"><i class="fa fa-clock-o"></i>Wait time</span></td>
<td><span class="value" id="avgWaitTime"></span></td>
<td><span class="value" id="maxWaitTime"></span></td></tr>
<tr class="set-timer-shown" id="Travel">
<td><span class="key"><i class="fa fa-clock-o"></i>Travel time</span></td>
<td><span class="value" id="avgTravelTime"></span></td>
<td><span class="value" id="maxTravelTime"></span></td></tr>
</tbody>
</table>
</div>
</div>

<div class="codestatus"></div>
Expand Down
76 changes: 65 additions & 11 deletions presenters.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,43 @@ var clearAll = function($elems) {
};


var presentStats = function($parent, world, statsTempl) {
world.on("stats_display_changed", function() {
$parent.html(riot.render(statsTempl, {
transportedCounter: world.transportedCounter,
elapsedTime: (world.elapsedTime).toFixed(0),
transportedPerSec: world.transportedPerSec.toPrecision(3),
avgWaitTime: (world.avgWaitTime).toFixed(1),
maxWaitTime: (world.maxWaitTime).toFixed(1),
moveCount: (world.moveCount)
}));
var presentStats = function($parent, world) {
var updateShownTimer = function() {
var showTimer = world.showTimer();
$parent.find(".fa").addClass("invisible");
switch (showTimer) {
// Don't search for strange/empty IDs
case "Commute": case "Wait": case "Travel":
$parent.find("#" + world.showTimer()).find("i").removeClass("invisible"); break;
default: break;
}
_.each(world.users, function(u) { u.trigger("new_state"); });
};
var updateStats = function() {
$parent.find("#spawnedCounter").text(world.spawnedCounter.toFixed());
$parent.find("#transportedCounter").text(world.transportedCounter.toFixed());
$parent.find("#elapsedTime").text(world.elapsedTime.toFixed(0) + "s");
$parent.find("#transportedPerSec").text(world.transportedPerSec.toPrecision(3));
$parent.find("#moveCount").text(world.moveCount.toFixed());
$parent.find("#avgCommuteTime").text(world.avgCommuteTime.toFixed(1) + "s");
$parent.find("#maxCommuteTime").text(world.maxCommuteTime.toFixed(1) + "s");
$parent.find("#avgWaitTime").text(world.avgWaitTime.toFixed(1) + "s");
$parent.find("#maxWaitTime").text(world.maxWaitTime.toFixed(1) + "s");
$parent.find("#avgTravelTime").text(world.avgTravelTime.toFixed(1) + "s");
$parent.find("#maxTravelTime").text(world.maxTravelTime.toFixed(1) + "s");
};

$parent.find(".set-timer-shown").on("click", function () {
var newTimer = $(this).attr("id").toString();
if (world.showTimer() == newTimer) {
newTimer = ""; // Disables displaying user timers
}
world.showTimer(newTimer);
updateShownTimer();
});
world.on("stats_display_changed", updateStats);
world.trigger("stats_display_changed");
updateShownTimer();
};

var presentChallenge = function($parent, challenge, app, world, worldController, challengeNum, challengeTempl) {
Expand Down Expand Up @@ -103,11 +128,40 @@ var presentWorld = function($world, world, floorTempl, elevatorTempl, elevatorBu
}));

world.on("new_user", function(user) {
var $user = $(riot.render(userTempl, {u: user, state: user.done ? "leaving" : ""}));
var $user = $(riot.render(userTempl, {u: user, state: user.done ? "leaving" : "", timer: "0"}));

user.on("new_state", function() {
var userTime;
var $elem = $user.find(".timer");
var oldTime = $elem.text();
$user.css({left: user.worldX, top: user.worldY});
if(user.done) { $user.addClass("leaving"); }
switch (world.showTimer()) {
case "Wait":
userTime = (user.enterTimestamp||world.elapsedTime) - user.spawnTimestamp;
break;
case "Travel":
if (user.enterTimestamp !== undefined) {
userTime = (user.exitTimestamp||world.elapsedTime) - user.enterTimestamp;
}
break;
case "Commute":
userTime = (user.exitTimestamp||world.elapsedTime) - user.spawnTimestamp;
break;
default:
userTime = undefined;
break;
}
if (userTime !== undefined) {
userTime = Math.round(userTime).toFixed();
if (oldTime !== userTime) {
$elem.text(userTime);
}
} else {
if (oldTime !== "") {
$elem.text("");
}
}
});
user.on("removed", function() {
$user.remove();
Expand Down
32 changes: 26 additions & 6 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -175,28 +175,30 @@ button.right {
border-right: 1px solid black;
border-left: 1px solid black;
font-family: Arial, Helvetica, sans-serif;
float: left;
}

.statscontainer {
font: 12px Consolas, Monaco, monospace;
line-height: 10px;
color: #999;
position: absolute;
top: 0;
right: 0px;
width: 240px;
height: 200px;
padding: 20px;
z-index: 1;
float: right;
}

.statscontainer div {
width: 240px;
.statscontainer div, tr, th, td {
height: 10px;
margin-bottom: 8px;
border-bottom: 1px solid #444;
}

.statscontainer th {
text-align: right;
}

.statscontainer .key {
float: left;
}
Expand All @@ -211,7 +213,7 @@ button.right {

.feedback {
position: absolute;
width: 1280px;
width: 1220px;
height: 2000px;
padding-top: 20px;
text-align: center;
Expand All @@ -227,6 +229,24 @@ button.right {
display: inline-block;
}

.stackable {
/* Must be inside a moveable class */
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}

.timer {
top: 10%;
text-align: center;
vertical-align: middle;
font: 9px Consolas, Monaco, monospace;
color: black;
text-shadow: 1px 1px 2px white;
}

.user {
color: white;
text-shadow: 0 1px 3px black;
Expand Down
16 changes: 8 additions & 8 deletions test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ describe("World controller", function() {
describe("Challenge requirements", function() {
var fakeWorld = null;
beforeEach(function() {
fakeWorld = { elapsedTime: 0.0, transportedCounter: 0, maxWaitTime: 0.0, moveCount: 0 };
fakeWorld = { elapsedTime: 0.0, spawnedCounter: 0, transportedCounter: 0, maxCommuteTime: 0.0, moveCount: 0 };
});

describe("requireUserCountWithinTime", function (){
Expand All @@ -141,15 +141,15 @@ describe("Challenge requirements", function() {
expect(challengeReq.evaluate(fakeWorld)).toBe(true);
});
});
describe("requireUserCountWithMaxWaitTime", function (){
describe("requireUserCountWithMaxCommuteTime", function (){
it("evaluates correctly", function() {
var challengeReq = requireUserCountWithMaxWaitTime(10, 4.0);
var challengeReq = requireUserCountWithMaxCommuteTime(10, 4.0);
expect(challengeReq.evaluate(fakeWorld)).toBe(null);
fakeWorld.maxWaitTime = 4.5;
fakeWorld.maxCommuteTime = 4.5;
expect(challengeReq.evaluate(fakeWorld)).toBe(false);
fakeWorld.transportedCounter = 11;
expect(challengeReq.evaluate(fakeWorld)).toBe(false);
fakeWorld.maxWaitTime = 3.9;
fakeWorld.maxCommuteTime = 3.9;
expect(challengeReq.evaluate(fakeWorld)).toBe(true);
});
});
Expand All @@ -165,17 +165,17 @@ describe("Challenge requirements", function() {
expect(challengeReq.evaluate(fakeWorld)).toBe(true);
});
});
describe("requireUserCountWithinTimeWithMaxWaitTime", function(){
describe("requireUserCountWithinTimeWithMaxCommuteTime", function(){
it("evaluates correctly", function() {
var challengeReq = requireUserCountWithinTimeWithMaxWaitTime(10, 5.0, 4.0);
var challengeReq = requireUserCountWithinTimeWithMaxCommuteTime(10, 5.0, 4.0);
expect(challengeReq.evaluate(fakeWorld)).toBe(null);
fakeWorld.elapsedTime = 5.1;
expect(challengeReq.evaluate(fakeWorld)).toBe(false);
fakeWorld.transportedCounter = 11;
expect(challengeReq.evaluate(fakeWorld)).toBe(false);
fakeWorld.elapsedTime = 4.9;
expect(challengeReq.evaluate(fakeWorld)).toBe(true);
fakeWorld.maxWaitTime = 4.1;
fakeWorld.maxCommuteTime = 4.1;
expect(challengeReq.evaluate(fakeWorld)).toBe(false);
});
});
Expand Down
Loading