If you'd like, you can see Arab Spring Tetris as a commentary on how multiple moving parts need to line up in order to wipe away the dictators of years past. Or you can see it as a brilliant way to procrastinate on a dreary Monday. Whatever you choose, enjoy this addictive creation, courtesy of Håkon Dreyer and Karl Sharro.
Dictatoris - The Arab Spring Tetris
body { font-family: Helvetica, sans-serif; }
#title { font-size: 1.8em; font-weight:bold; }
#subtitle { font-size: 1.4em; font-style:oblique; }
#tetris { background-color: #edeeed; font-size: 1.2em; width: 560px; height:720px; position:relative; }
#canvas { position:absolute; vertical-align: top; background: url
[www.foreignpolicy.com] box-shadow: 8px 8px 8px #999; border: 2px solid #333; left:3px; width: 320px; height: 640px;}
#menu { position:absolute; right:3px; vertical-align: top; width: 225px; height: 640px; }
#menu p { margin: 0.5em 0; text-align: center; }
#menu p a { text-decoration: none; color: black; }
#upcoming { display: block; margin: 0 auto; background-color: #E0E0E0; width: 160px; height: 160px;}
#controls { width:100%; position:absolute; bottom:0px; }
#start { width:100%; position: absolute; bottom: 180px; text-align:center; }
#creators { font-size: 0.7em; line-height: 1.2; position:absolute; bottom:8px; text-align:center; }
#score { color: red; font-weight: bold; vertical-align: middle; }
#rows { color: blue; font-weight: bold; vertical-align: middle; }
#level { color: Fuchsia; font-weight: bold; vertical-align: middle; }
#credits { font-size: 0.6em; line-height: 1.2; position:absolute; bottom:5px; }
Dictatoris
The Arab Spring Tetris
Sorry, game cannot be run
because your browser does
not support <canvas>
score 00000
rows 0
level 1
Press Space to Play.
Arrow keys to navigate.
Idea and art by Karl Sharro
@KarlreMarks
Made by Håkon Dreyer.
@haakon_d
Credits:
Background images CC by
Denis Bocquet and CC-BY-SA by
Courtney Radsch
Dictatoris is a modification of
@jakesgordon's
Javascript Tetris
//-------------------------------------------------------------------------
// base helper methods
//-------------------------------------------------------------------------
function get(id) { return document.getElementById(id); };
function hide(id) { get(id).style.visibility = 'hidden'; };
function show(id) { get(id).style.visibility = 'visible'; };
function html(id, html) { get(id).innerHTML = html; };
function timestamp() { return new Date().getTime(); };
function random(min, max) { return (min + (Math.random() * (max - min))); };
function randomChoice(choices) { return choices[Math.round(random(0, choices.length - 1))]; };
if (!window.requestAnimationFrame) { //
[paulirish.com] window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback, element) {
window.setTimeout(callback, 1000 / 60);
}
}
//-------------------------------------------------------------------------
// IE stuff
//-------------------------------------------------------------------------
var G_vmlCanvasManager; // so non-IE won't freak out in canvasInit
var canvas = get('canvas'),
ucanvas = get('upcoming');
if (typeof (G_vmlCanvasManager) != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
ucanvas = G_vmlCanvasManager.initElement(ucanvas);
}
var ctx = canvas.getContext('2d'),
uctx = ucanvas.getContext('2d');
//-------------------------------------------------------------------------
// game constants
//-------------------------------------------------------------------------
var KEY = { ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 },
DIR = { UP: 0, RIGHT: 1, DOWN: 2, LEFT: 3, MIN: 0, MAX: 3 },
speed = { start: 0.6, decrement: 0.05, min: 0.1 }, // how long before piece drops by 1 row (seconds)
nx = 10, // width of tetris court (in blocks)
ny = 20, // height of tetris court (in blocks)
nu = 5, // width/height of upcoming preview (in blocks)
oldIE = false; //true if IE version 0; bit = bit >> 1) {
if (blocks & bit) {
fn(x + col, y + row);
}
if (++col === 4) {
col = 0;
++row;
}
}
};
//-----------------------------------------------------
// check if a piece can fit into a position in the grid
//-----------------------------------------------------
function occupied(type, x, y, dir) {
var result = false
eachblock(type, x, y, dir, function (x, y) {
if ((x = nx) || (y = ny) || getBlock(x, y))
result = true;
});
return result;
};
function unoccupied(type, x, y, dir) {
return !occupied(type, x, y, dir);
};
//-----------------------------------------
// start with 4 instances of each piece and
// pick randomly until the 'bag is empty'
//-----------------------------------------
var pieces = [];
function randomPiece() {
if (pieces.length == 0)
pieces = [dii, dii, dii, dii, dij, dij, dij, dij, dil, dil, dil, dil, dio, dio, dio, dio, dis, dis, dis, dis, dit, dit, dit, dit, diz, diz, diz, diz];
var type = pieces.splice(random(0, pieces.length - 1), 1)[0];
return { type: type, dir: DIR.UP, x: Math.round(random(0, nx - type.size)), y: 0 };
};
//-------------------------------------------------------------------------
// GAME LOOP
//-------------------------------------------------------------------------
function run() {
addEvents(); // attach keydown and resize events
var last = now = timestamp();
function frame() {
now = timestamp();
update(Math.min(1, (now - last) / 1000.0)); // using requestAnimationFrame have to be able to handle large delta's caused when it 'hibernates' in a background or non-visible tab
draw();
last = now;
requestAnimationFrame(frame, canvas);
}
resize(); // setup all our sizing information
reset(); // reset the per-game variables
frame(); // start the first frame
};
function addEvents() {
if (!document.addEventListener) {
document.attachEvent('onkeydown', keydown);
oldIE = true;
}
else {
document.addEventListener('keydown', keydown, false);
}
};
function resize(event) {
canvas.width = canvas.clientWidth; // set canvas logical size equal to its physical size
canvas.height = canvas.clientHeight; // (ditto)
ucanvas.width = ucanvas.clientWidth;
ucanvas.height = ucanvas.clientHeight;
dx = canvas.width / nx; // pixel size of a single tetris block
dy = canvas.height / ny; // (ditto)
invalidate();
invalidateNext();
};
function keydown(ev) {
var handled = false;
if (playing) {
switch (ev.keyCode) {
case KEY.LEFT: actions.push(DIR.LEFT); handled = true; break;
case KEY.RIGHT: actions.push(DIR.RIGHT); handled = true; break;
case KEY.UP: actions.push(DIR.UP); handled = true; break;
case KEY.DOWN: actions.push(DIR.DOWN); handled = true; break;
case KEY.ESC: lose(); handled = true; break;
}
}
else if (ev.keyCode == KEY.SPACE) {
play();
handled = true;
}
if (handled) {
if (!oldIE)
ev.preventDefault(); // prevent arrow keys from scrolling the page (supported in IE9+ and all other browsers)
else
ev.returnValue = false;
}
};
function ctrlclicked(ctl) {
if (playing) {
switch (ctl) {
case 'LEFT': actions.push(DIR.LEFT); break;
case 'RIGHT': actions.push(DIR.RIGHT); break;
case 'UP': actions.push(DIR.UP); break;
case 'DOWN': actions.push(DIR.DOWN); break;
case 'ESC': lose(); break;
}
}
else if (ctl = 'SPACE') {
play();
}
};
//-------------------------------------------------------------------------
// GAME LOGIC
//-------------------------------------------------------------------------
function play() {
hide('start'); reset(); playing = true; lost = false;
canvas.style.background = "url
[www.foreignpolicy.com] };
function lose() {
show('start'); setVisualScore(); playing = false; lost = true;
};
function setVisualScore(n) { vscore = n || score; invalidateScore(); };
function setScore(n) { score = n; setVisualScore(n); };
function addScore(n) { score = score + n; };
function clearScore() { setScore(0); };
function clearRows() { setRows(0); };
function setRows(n) { rows = n; level = 1 + Math.floor((rows+0.5)/10); step = Math.max(speed.min, speed.start - (speed.decrement * level)); invalidateRows(); };
function addRows(n) { setRows(rows + n); };
function getBlock(x, y) { return (blocks && blocks[x] ? blocks[x][y] : null); };
function setBlock(x, y, type) { blocks[x] = blocks[x] || []; blocks[x][y] = type; invalidate(); };
function clearBlocks() { blocks = []; invalidate(); }
function clearActions() { actions = []; };
function setCurrentPiece(piece) { current = piece || randomPiece(); invalidate(); };
function setNextPiece(piece) { next = piece || randomPiece(); invalidateNext(); };
function reset() {
dt = 0;
clearActions();
clearBlocks();
clearRows();
clearScore();
setCurrentPiece(next);
setNextPiece();
};
function update(idt) {
if (playing) {
if (vscore step) {
dt = dt - step;
drop();
}
}
};
function handle(action) {
switch (action) {
case DIR.LEFT: move(DIR.LEFT); break;
case DIR.RIGHT: move(DIR.RIGHT); break;
case DIR.UP: rotate(); break;
case DIR.DOWN: drop(); break;
}
};
function move(dir) {
var x = current.x, y = current.y;
switch (dir) {
case DIR.RIGHT: x = x + 1; break;
case DIR.LEFT: x = x - 1; break;
case DIR.DOWN: y = y + 1; break;
}
if (unoccupied(current.type, x, y, current.dir)) {
current.x = x;
current.y = y;
invalidate();
return true;
}
else {
return false;
}
};
function rotate(dir) {
var newdir = (current.dir == DIR.MAX ? DIR.MIN : current.dir + 1);
if (unoccupied(current.type, current.x, current.y, newdir)) {
current.dir = newdir;
invalidate();
}
};
function drop() {
if (!move(DIR.DOWN)) {
addScore(10);
dropPiece();
removeLines();
setCurrentPiece(next);
setNextPiece(randomPiece());
clearActions();
if (occupied(current.type, current.x, current.y, current.dir)) {
lose();
}
}
};
function dropPiece() {
eachblock(current.type, current.x, current.y, current.dir, function (x, y) {
setBlock(x, y, current.type);
});
};
function removeLines() {
var x, y, complete, n = 0;
for (y = ny; y > 0; --y) {
complete = true;
for (x = 0; x 0) {
addRows(n);
addScore(100 * Math.pow(2, n - 1)); // 1: 100, 2: 200, 3: 400, 4: 800
canvas.style.background = "url
[www.foreignpolicy.com] + level % 10 + ".jpg)";
}
};
function removeLine(n) {
var x, y;
for (y = n; y >= 0; --y) {
for (x = 0; x