Skip to content

Instantly share code, notes, and snippets.

@daohodac
Last active August 29, 2015 14:16
Show Gist options
  • Save daohodac/a67ce178b770683017d0 to your computer and use it in GitHub Desktop.
Save daohodac/a67ce178b770683017d0 to your computer and use it in GitHub Desktop.
a game to learn multiply tables
<!DOCTYPE html>
<meta charset="utf-8">
<style>
text {
text-anchor: middle;
font-family: verdana;
}
.selX {
fill: #e6550d;
}
.selY {
fill: #e6550d;
}
</style>
<body></body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
function getWindowSize() {
var winW = 630, winH = 460;
if (document.body && document.body.offsetWidth) {
winW = document.body.offsetWidth;
winH = document.body.offsetHeight;
}
if (document.compatMode==='CSS1Compat' &&
document.documentElement &&
document.documentElement.offsetWidth ) {
winW = document.documentElement.offsetWidth;
winH = document.documentElement.offsetHeight;
}
if (window.innerWidth && window.innerHeight) {
winW = window.innerWidth;
winH = window.innerHeight;
}
return [winW, winH];
}
function fullsize() {
var winSize = getWindowSize();
return Math.min(winSize[0], winSize[1]);
}
//the size of the canvas
var size = fullsize();
//the size of each tile
var square = size/11;
//init the tiles
var data = [];
var maxInt = 10;
for (var i = 1; i < maxInt; i++) {
for (var j = i; j < maxInt; j++) {
data.push({a:i, b:j, ongoing:false, found: false});
}
}
var foundScale = d3.scale.linear()
.domain(d3.scale.ordinal()
.domain(d3.range(4))
.rangeRoundPoints([0, 81]).range()
)
.range(["#dadaeb", "#bcbddc", "#9e9ac8", "#756bb1"]);
var opScale = d3.scale.linear()
.domain(d3.scale.ordinal()
.domain(d3.range(4))
.rangeRoundPoints([0, 10]).range()
)
.range(["#d9d9d9", "#bdbdbd", "#969696", "#636363"]);
data=shuffle(data);
foundCount = 0;
data[data.length-1].ongoing=true;
var toFind = data[data.length-1].a*data[data.length-1].b;
//some margins
var margin = {top: 0, right: 0, bottom: 0, left: 0};
//scale to nicely reorder the tiles
var x = d3.scale.ordinal().rangeBands([0, size-margin.left]),
y = d3.scale.ordinal().rangeBands([0, size-margin.top]);
x.domain(d3.range(maxInt));
y.domain(d3.range(maxInt));
//a scale for the shuffle.
var randScale = d3.scale.linear();
randScale.domain([0, 1]).range([square*2,size-square*2]);
//function to shuffle the tiles
function shuffle(o){ //v1.0
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
function randomPos(d) {
return 'translate('+randScale(Math.random())+', '+randScale(Math.random())+') rotate('+(Math.random()*90-45)+')';
};
function finalPos(d) {
return 'translate('+x(d.a)+','+y(d.b)+')';
};
//create the SVG
var svg = d3.select("body").append("svg")
.attr("width", size)
.attr("height", size);
var tileGroup = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var colorizeTile = function(d) {
this
.transition()
.duration(function(d) {return Math.random()*1000;})
.attr("transform", function(d) {
if (d.found) {
return finalPos(d);
} else {
return randomPos(d);
}})
.attr("opacity", function(d) {
if (d.found && foundCount<data.length) {
return 0;
} else {
return 1;
}
})
.select('rect')
.attr("fill", function(d) {
if (d.found) {
return foundScale(d.a*d.b);
} else if (d.ongoing) {
return "#e6550d";
} else {
return "#aec7e8";
}
})
};
var createRect = function(sel, klass, color, transform, txt) {
var tile = sel.append('g')
.attr("transform", transform)
.classed(klass, true);
tile.append("rect")
.attr("fill", color)
.attr("width", square)
.attr("height",square);
tile.append('text')
.text(txt)
.attr('alignment-baseline', 'middle')
.attr("text-anchor", "middle")
.attr("fill", "white")
.attr("font-size", (square*.75)+"px")
.attr('x', square/2)
.attr('y', square/2);
return tile;
}
var createTile = function(sel) {
var tile = createRect(sel, "tile",
"black",
randomPos,
function(d) { return d.a*d.b;});
tile.classed("toFind", function(d) {
return d.ongoing;
}).call(colorizeTile)
}
var createXOperand = function(sel) {
var tile = createRect(sel,"opX",
function(d) {return opScale(d)},
function(d) {
return 'translate(0, '+y(d)+')';
},
function(d) { return d;});
tile.on('click', function(d) {
selX = d3.select(this);
d3.selectAll(".selX").classed("selX", false);
selX.select('rect').classed("selX", true);
validate();
});
}
var createYOperand = function(sel) {
var tile = createRect(sel,"opY",
function(d) {return opScale(d)},
function(d) {
return 'translate('+x(d)+', 0)';
},
function(d) { return d;});
tile.on('click', function(d) {
selY = d3.select(this);
d3.selectAll(".selY").classed("selY", false);
selY.select('rect').classed("selY", true);
validate();
});
}
//append the operands
svg.selectAll("#operands")
.data(d3.range(maxInt))
.enter().call(createXOperand).call(createYOperand);
//create the tiles
var tiles = tileGroup.selectAll(".tile")
.data(data)
.enter().call(createTile)
var validate = function() {
var comp = selX.data()[0]*selY.data()[0];
if (comp === toFind) {
congrats();
} else {
nonono();
}
}
var nonono = function() {
d3.select(".toFind").transition().duration(function(d) {return Math.random()*3000;}).attr("transform", randomPos);
d3.selectAll(".tile").call(colorizeTile);
}
var congrats = function() {
selX.classed("selX", false);
selY.classed("selY", false);
d3.select(".toFind").classed("toFind", false)
.transition().attr("transform", finalPos)
.select('rect').attr("fill", "green");
data[data.length-foundCount-1].ongoing=false;
data[data.length-foundCount-1].found=true;
foundCount++;
if (data.length-foundCount-1>=0) {
data[data.length-foundCount-1].ongoing=true;
toFind = data[data.length-foundCount-1].a*data[data.length-foundCount-1].b;
}
//d3.selectAll(".tile").select('rect').attr('fill', colorizeTile)
d3.selectAll(".tile").call(colorizeTile);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment