/* Color puzzle, a simple canvas test */
(function() {
var $WIDTH = 640;
var $HEIGHT = 480;
var $LEVELS = [
['@ x',
'@ x',
'@ x',
'@ x',
's s'],
['xx',
'sx'],
['xx',
'xx',
'sx'],
['xxx',
'xxx',
'sxx'],
['xxxxxxx',
'xxx x',
'sxx x'],
['xxxxx',
'x xsx',
' xxx'],
['xxx ',
'x xx',
'xxsx'],
[' xxxs',
' xxxx',
'xxxxxxx',
' xx'],
['x x',
's s'],
[' s ',
' x x x ',
' xxxxxxxxx ',
' s '],
[' xx xxx ',
'sxxxxxxxxs',
' xx xx x '],
[' x ',
'xxxxxxxxxxx',
'xxxxx xxx x',
'xxxxxxxxxxx',
' xxxxxxx xx',
' s '],
[' s ',
'xxxxxxxxxx ',
'x xxxx xxx ',
'xxxx xx xx ',
' xxx xxxxxs',
' x xxxxx ',
'xx xxxxx ',
'xxx xx xx ',
' xx xxxxx '],
[' Axx ',
' x x ',
'sxxxxaaaaax',
' s x'],
[' xxxxx ',
' x xxx',
' xxxxx a',
' xxxxx s a',
' x x a',
' xxx x a',
'sxxx x a',
' Axxxx'],
['xxxxx xxx',
'asxxx xxx',
'axx xxxx ',
'a x xs x ',
'a x xxxxx',
'a x x x',
'x xxxAxxx'],
[' xxx xxx',
' xxxxxxx x',
'xxxs xxxx',
'xx xx x xx',
' axbxxxsxx',
' aAx xxB x',
' x x x xxx',
' xaxxx xx'],
['xxxxx xxxx',
'a xxxxxxxxxxxx',
'xxxx xxAxx xx ',
' xxx x xxxxx',
'xxxxxxxxxx xxx',
'xxx xxxxxx xxx',
'a xxxxx xxxxxx',
'xxx xxx s xx ']
];
var $BOX_SIZE = 44;
var $BOX_BORDER_COLOR = '#c8e0e4';
var $BOX_TYPES = {
' ': 'SPACE',
'x': 'UNLINKED',
'@': 'LINKED',
's': 'START',
'A': 'TRIGGER_A',
'a': 'BRIDGE_A',
'b': 'TRIGGER_B',
'B': 'BRIDGE_B'
};
var $BOX_COLORS = {
'SPACE': null,
'UNLINKED': '#dceef1',
'LINKED': '#75a8b1',
'START': '#185f6b',
'BRIDGE_A': '#b0d0b4',
'TRIGGER_A': '#186b22',
'BRIDGE_B': '#d0b0b0',
'TRIGGER_B': '#6b1818'
};
function assert(expr, msg) {
if (!expr)
alert('Assertion failed: ' + msg);
}
ColorPuzzle = function(selector) {
var self = this;
var canvas = $('')
.appendTo(selector)
.attr('width', $WIDTH)
.attr('height', $HEIGHT)
.bind('mousemove', _.bind(this.onMouseMove, this))
.bind('click', _.bind(this.onClick, this));
this.levelLabel = $('')
.appendTo(selector);
this.gameOverBox = $('div.game_over', selector);
this.canvas = canvas[0];
this.ctx = this.canvas.getContext('2d');
this.setLevel(1);
}
$.extend(ColorPuzzle.prototype, {
setLevel : function(level) {
if (level < 0 || level > $LEVELS.length)
return false;
this.levelLabel.text('Level #' + level);
this.level = new Level(this, level);
this.reset();
return true;
},
reset : function() {
this.links = [];
this.level.reset();
this.draw();
},
nextLevel : function() {
this.levelLabel.text('Well Done!');
if (this.level.number < $LEVELS.length)
_.delay(_.bind(this.setLevel, this, this.level.number + 1), 500);
else
this.gameOver();
},
gameOver : function() {
var self = this;
this.levelLabel.text('');
$('a.play_again', this.gameOverBox).bind('click', function() {
self.setLevel(1);
self.gameOverBox.fadeOut(500);
return false;
});
_.delay(_.bind($.fn.fadeIn, this.gameOverBox, 500), 300);
},
getOffset : function() {
return $(this.canvas).offset();
},
translatePosition : function(pageX, pageY) {
var offset = $(this.canvas).offset();
return {
x: pageX - offset.left,
y: pageY - offset.top
}
},
onClick : function(evt) {
this.reset();
},
onMouseMove : function(evt) {
var pos = this.translatePosition(evt.pageX, evt.pageY);
var box = this.level.getBoxByCoords(pos.x, pos.y);
if (!box)
return;
/* new start? */
if (box.type === 'START') {
if (!_.detect(this.links, function(link) {
return _.indexOf(link, box) >= 0;
})) {
this.links.push([box]);
this.draw();
}
}
/* otherwise color if neighbour of a link end? */
else if (this.isOpenFieldType(box.type)) {
var link = this.findLink(box);
if (link !== null) {
link.push(box);
box.link = link;
if (this.isTriggerFieldType(box.type))
this.trigger(box.type);
this.level.setBoxType(box, 'LINKED');
this.draw();
if (this.level.unlinked == 0)
this.nextLevel();
this.makeMostRecentLink(link);
}
}
},
findLink : function(box) {
return _.detect(this.links, function(link) {
var other = link[link.length - 1];
var diffX = Math.abs(box.column - other.column);
var diffY = Math.abs(box.row - other.row);
return (
(diffX == 1 && diffY == 0) ||
(diffX == 0 && diffY == 1)
);
}) || null;
},
isOpenFieldType : function(type) {
return !!(type === 'UNLINKED' || type.match(/^TRIGGER_/));
},
isTriggerFieldType : function(type) {
return !!type.match(/^TRIGGER_/);
},
trigger : function(type) {
var to_open = type.replace(/^TRIGGER_/, 'BRIDGE_');
var self = this;
_.each(this.level.boxes, function(box) {
if (box.type === to_open)
self.level.setBoxType(box, 'UNLINKED');
});
},
makeMostRecentLink : function(link) {
var links = this.links;
if (links.length != 1)
this.links = [link].concat(_.select(links, function(item) {
return item != link;
}));
},
draw : function() {
this.ctx.clearRect(0, 0, $WIDTH, $HEIGHT);
this.level.draw();
}
});
function Level(puzzle, number) {
this.puzzle = puzzle;
this.number = number;
}
$.extend(Level.prototype, {
reset : function() {
var self = this;
var data = $LEVELS[this.number - 1];
this.boxes = [];
this.unlinked = 0;
this.width = data[0].length;
this.height = data.length;
this.paddingLeft = ($WIDTH / 2) - (this.width * $BOX_SIZE / 2);
this.paddingTop = ($HEIGHT / 2) - (this.height * $BOX_SIZE / 2);
var idx = 0;
_.each(data, function(items, row) {
assert(row.length == this.width, 'invalid row size');
_.each(items, function(item, column) {
var type = $BOX_TYPES[item];
if (type === 'UNLINKED')
self.unlinked++;
self.boxes.push({
id: idx++,
column: column,
row: row,
x: column * $BOX_SIZE + self.paddingLeft,
y: row * $BOX_SIZE + self.paddingTop,
width: $BOX_SIZE,
height: $BOX_SIZE,
type: type,
link: null
});
});
});
},
getBox : function(column, row) {
return this.boxes[column + (row * this.width)];
},
getBoxByCoords : function(x, y) {
x = parseInt((x - this.paddingLeft) / $BOX_SIZE);
y = parseInt((y - this.paddingTop) / $BOX_SIZE);
return (x < 0 || y < 0 || x >= this.width || y >= this.height)
? null : this.getBox(x, y);
},
setBoxType : function(box, type) {
if (box.type === 'UNLINKED')
this.unlinked--;
if (type === 'UNLINKED')
this.unlinked++;
box.type = type;
},
draw : function() {
var ctx = this.puzzle.ctx;
for (var column = 0; column < this.width; column++)
for (var row = 0; row < this.height; row++) {
var box = this.getBox(column, row);
var color = $BOX_COLORS[box.type];
if (color === null)
continue;
ctx.strokeStyle = $BOX_BORDER_COLOR;
ctx.lineWidth = 1;
ctx.fillStyle = color;
ctx.fillRect(box.x, box.y, box.width, box.height);
ctx.strokeRect(box.x, box.y, box.width, box.height);
if (box.link) {
var pos = box.link.length - _.indexOf(box.link, box);
if (pos <= 2) {
ctx.fillStyle = 'rgba(255, 255, 255, ' + (0.45 - 0.15 * pos) + ')';
ctx.fillRect(box.x, box.y, box.width, box.height);
}
}
ctx.stroke();
}
}
});
})();
$(function() {
var game = new ColorPuzzle('#puzzle_display');
/* now what could that do */
(function(){var t=0;$(window).bind('keydown',function(e){if(e.keyCode==
[1,1,3,3,0,2,0,2,29,28][t]+37){if(t++!=9)return;t=0;while(1){var input=
prompt('Jump to level: ');var level=parseInt(input);if(!input)break;if(
isNaN(level))alert("Invalid input, need a level number");else if (!game
.setLevel(level))alert("Unknown level!");else break;}}else t=0;});})();
});