Created
January 11, 2012 21:08
-
-
Save mischa/1596773 to your computer and use it in GitHub Desktop.
KA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/javascript/exercises/time_differences_1.js b/javascript/exercises/time_differences_1.js | |
new file mode 100644 | |
--- /dev/null | |
+++ b/javascript/exercises/time_differences_1.js | |
@@ -0,0 +1,272 @@ | |
+/* | |
+* Time Differences 1 | |
+* Author: Mischa Fierer | |
+* Date: 2011-05-14 | |
+* | |
+* Problem spec: http://vimeo.com/23539196 | |
+* How much time passes between X and Y | |
+* Every now and then, give a time that is the next day | |
+* | |
+* TODO: for fun change to how much time spent watching a movie, sleeping, riding bike, etc. | |
+* | |
+*/ | |
+ | |
+function TimeDifferencesExercise() { | |
+ var start = null, | |
+ end = null, | |
+ totalMinutes = null; | |
+ | |
+ this.init = function() { | |
+ generateProblem(); | |
+ showProblem(); | |
+ generateHints(); | |
+ } | |
+ | |
+ function generateProblem() { | |
+ var times = [randomTime(), randomTime()].sort(function(a, b) { return a.date > b.date}) | |
+ | |
+ if(get_random() % 4 == 0) { // Span multiple days | |
+ times.reverse()[1].date.setDate(2); | |
+ } | |
+ | |
+ start = times[0]; | |
+ end = times[1]; | |
+ setCorrectAnswer(start.timeTo(end)); | |
+ } | |
+ | |
+ function randomTime() { | |
+ return new TimelineTime(getRandomIntRange(0, 60), getRandomIntRange(0, 24)); | |
+ } | |
+ | |
+ function showProblem() { | |
+ var next = ''; | |
+ if(start.date.getDay() < end.date.getDay()){ | |
+ next = ' the next day'; | |
+ } | |
+ | |
+ write_text('How much time passes between <strong>' + start.pretty() + '</strong> and <strong>' + end.pretty() + next + '</strong>?'); | |
+ } | |
+ | |
+ function generateHints() { | |
+ var timeline = new Timeline(start, end, print), | |
+ firstHour = new TimelineTime(0, start.date.getHours() + 1), | |
+ lastHour = new TimelineTime(0, end.date.getHours()); | |
+ | |
+ lastHour.date.setDate(end.date.getDate()); | |
+ | |
+ Exercise.hints = []; | |
+ Exercise.timeline = timeline; | |
+ Exercise.hints.push(timeline.drawStartAndEnd); | |
+ Exercise.hints.push(timeline.addHour.partial(firstHour, 'first')); | |
+ Exercise.hints.push(timeline.addHour.partial(lastHour, 'last')); | |
+ | |
+ Exercise.hints.push(timeline.addThresholds) | |
+ Exercise.hints.push(timeline.close) | |
+ Exercise.hints.push(showSum) | |
+ } | |
+ | |
+ function print(hours, minutes, color, firstTime, secondTime) { | |
+ var time = firstTime.timeTo(secondTime, Timeline.timeInWords), | |
+ from = ' from ' + firstTime.pretty() + ' to ' + secondTime.pretty(); | |
+ $(function(){ | |
+ $('ul#hints').append('<li style="color:' + color + '">' + time + from + '</li>'); | |
+ }); | |
+ } | |
+ | |
+ function showSum() { | |
+ var answer = start.timeTo(end) + " total time passes."; | |
+ $(function(){ | |
+ $('ul#hints').append("<li class='answer'> " + answer + "</li>"); | |
+ }); | |
+ } | |
+} | |
+ | |
+function Timeline(start, end, printCallback) { | |
+ start.timeline = this; | |
+ end.timeline = this; | |
+ | |
+ this.printCallback = printCallback; | |
+ this.WIDTH = 680; | |
+ this.PADDING = 80; | |
+ this.MARGIN = 40; | |
+ | |
+ this.paper = Raphael(20, 350, this.WIDTH, 200); | |
+ this.start = start; | |
+ this.end = end; | |
+ this.times = []; | |
+ this.hourWidth = (this.WIDTH - this.PADDING*2 - this.MARGIN*2) / ((end.date - start.date) / 1000 / 60 / 60); | |
+ this.colors = ['salmon', 'purple', 'red', 'green', 'orange', 'blue']; | |
+} | |
+ | |
+Timeline.prototype.drawTime = function(time, left, above) { | |
+ this.paper.path("M" + left + " 25L" + left + " " + 80); | |
+ if(above !== 'above') { | |
+ this.paper.text(left, 100, time.pretty()).attr('font-size', 20); | |
+ } else { | |
+ this.paper.text(left, 10, time.pretty()).attr('font-size', 20); | |
+ } | |
+ | |
+ time.left = left; | |
+ this.times.push(time) | |
+ this.times = this.times.sort(function(a, b) { return a.date > b.date}); | |
+} | |
+ | |
+Timeline.prototype.drawStartAndEnd = function () { | |
+ this.paper.path("M0 50L" + this.WIDTH + " 50"); | |
+ this.drawTime(this.start, this.MARGIN); | |
+ this.drawTime(this.end, this.WIDTH - this.MARGIN); | |
+} | |
+ | |
+Timeline.prototype.addHour = function(time, firstOrLast) { | |
+ if(firstOrLast === 'first') { | |
+ this.drawTime(time, this.MARGIN + this.PADDING, 'above'); | |
+ this.connect(this.start, time); | |
+ } | |
+ else { | |
+ this.drawTime(time, this.WIDTH - this.MARGIN - this.PADDING, 'above'); | |
+ this.connect(time, this.end); | |
+ } | |
+} | |
+ | |
+Timeline.prototype.connect = function(first, second) { | |
+ var distanceInMinutes = (second.date - first.date) / 1000 / 60, color; | |
+ this.paper.text((first.left + second.left) / 2, 35, first.timeTo(second, this.timeInWords)); | |
+ color = this.colors.pop(); | |
+ this.paper.path("M" + first.left + " 45L " + second.left + " 45").attr('stroke', color); | |
+ this.printCallback(Math.floor(distanceInMinutes / 60), distanceInMinutes % 60, color, first, second); | |
+} | |
+ | |
+Timeline.prototype.addThresholds = function() { | |
+ var noon = new TimelineTime(0, 12), | |
+ secondNoon = new TimelineTime(0, 12), | |
+ midnight = new TimelineTime(0, 24), | |
+ previousTime = this.times[1], | |
+ hours = 0, | |
+ noThreshold = true; | |
+ | |
+ // E.g. 3pm to 5pm the next day has 2 noons | |
+ secondNoon.date.setDate(this.end.date.getDate()); | |
+ midnight.date.setDate(this.start.date.getDate() + 1); | |
+ | |
+ if((this.start.date < noon.date) && (this.end.date > noon.date)){ | |
+ hours = (noon.date - previousTime.date) / 1000 / 60 / 60; | |
+ this.drawTime(noon, (hours * this.hourWidth) + previousTime.left); | |
+ this.connect(this.times[this.times.indexOf(previousTime)], noon); | |
+ previousTime = noon; | |
+ noThreshold = false; | |
+ } | |
+ | |
+ if((this.start.date > noon.date) && (this.end.date > midnight.date )) { | |
+ hours = (midnight.date - previousTime.date) / 1000 / 60 / 60; | |
+ this.drawTime(midnight, (hours * this.hourWidth) + previousTime.left); | |
+ this.connect(this.times[this.times.indexOf(midnight) - 1], midnight); | |
+ previousTime = midnight; | |
+ noThreshold = false; | |
+ | |
+ if(this.end.date > secondNoon.date) { | |
+ hours = (secondNoon.date - previousTime.date) / 1000 / 60 / 60; | |
+ this.drawTime(secondNoon, (hours * this.hourWidth) + previousTime.left); | |
+ this.connect(midnight, secondNoon); | |
+ } | |
+ } | |
+ | |
+ if(noThreshold){ | |
+ nextHint(); | |
+ } | |
+ | |
+ | |
+} | |
+ | |
+Timeline.prototype.close = function() { | |
+ this.connect(this.times[this.times.length - 3], this.times[this.times.length - 2]); | |
+} | |
+ | |
+function TimelineTime(minutes, hours) { | |
+ this.date = new Date(1987, 1, 1, (hours || 0), (minutes || 0), 0, 0); | |
+ this.left = null; // Distance in pixels from the side | |
+} | |
+ | |
+TimelineTime.prototype.timeTo = function(otherTime, printer) { | |
+ var minutes = (otherTime.date - this.date) / 1000 / 60, | |
+ hours = Math.floor(minutes / 60); | |
+ | |
+ printer = printer || this.digitalClock; | |
+ return printer.call(this, minutes % 60, hours); | |
+} | |
+ | |
+TimelineTime.prototype.pretty = function(){ | |
+ var hours = this.date.getHours(), | |
+ minutes = this.date.getMinutes(), | |
+ m = 'am'; | |
+ | |
+ if(hours >= 12) { | |
+ hours -= 12; | |
+ m = 'pm'; | |
+ } | |
+ | |
+ if(hours === 0) { | |
+ hours = '12'; | |
+ } | |
+ | |
+ return this.digitalClock(minutes, hours, m) | |
+} | |
+ | |
+Timeline.prototype.timeInWords = function(minutes, hours) { | |
+ var output = []; | |
+ | |
+ if(minutes > 0) { | |
+ output.push(minutes + ' minutes'); | |
+ } | |
+ | |
+ if(hours > 0) { | |
+ output.push(hours + ' hours'); | |
+ } | |
+ | |
+ return output.join(' and '); | |
+} | |
+ | |
+TimelineTime.prototype.digitalClock = function(minutes, hours, m) { | |
+ if(minutes.toString().length < 2) { | |
+ minutes = '0' + minutes; | |
+ } | |
+ if(hours.toString().length < 2) { | |
+ hours = '0' + hours; | |
+ } | |
+ | |
+ return hours + ':' + minutes + (m || ''); | |
+} | |
+ | |
+ | |
+Function.prototype.partial = function(){ | |
+ var fn = this, args = Array.prototype.slice.call(arguments); | |
+ return function(){ | |
+ var arg = 0; | |
+ for ( var i = 0; i < args.length && arg < arguments.length; i++ ) | |
+ if ( args[i] === undefined ) | |
+ args[i] = arguments[arg++]; | |
+ return fn.apply(this, args); | |
+ }; | |
+}; | |
+ | |
+Exercise.checkAnswer = function() { | |
+ var answer = $('#answer').val(); | |
+ highlight_answer(); | |
+ | |
+ if(!answer.match(/^\d+:\d+$/)) { | |
+ window.alert("Your answer should be hours:minutes. For example, for 3 hours and 4 minutes answer 3:04."); | |
+ return Answer.INVALID; | |
+ } | |
+ | |
+ if(answer === correctAnswer) { | |
+ return Answer.CORRECT; | |
+ } | |
+ else { | |
+ return Answer.INCORRECT; | |
+ } | |
+} | |
+ | |
+nextHint = function(){ | |
+ if(Exercise.hints.length > 0) { | |
+ Exercise.hints.shift().call(Exercise.timeline) | |
+ } | |
+} | |
diff --git a/time_differences_1.html b/time_differences_1.html | |
new file mode 100644 | |
--- /dev/null | |
+++ b/time_differences_1.html | |
@@ -0,0 +1,32 @@ | |
+{% extends arithmetic_template %} | |
+ | |
+{% block maincode %} | |
+<script src="/javascript/exercises/time_differences_1.js?{{App.version}}"></script> | |
+<style> | |
+ ul#hints { | |
+ margin: 180px 0 100px 40px; | |
+ font-size: 15px; | |
+ } | |
+ | |
+ li.answer { | |
+ font-weight: bold; | |
+ font-size: 16px; | |
+ } | |
+</style> | |
+{% endblock maincode %} | |
+ | |
+{% block maincell %} | |
+ | |
+<script> | |
+ var exercise = new TimeDifferencesExercise(); | |
+ exercise.init(); | |
+</script> | |
+ | |
+<ul id="hints"> | |
+</ul> | |
+{% endblock maincell %} | |
+ | |
+{% block hintfunction %} | |
+nextHint() | |
+{% endblock hintfunction%} | |
+ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment