<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="container">
<div class="hud">
Score: <span id="score" class="score">0</span>
</div>
</div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sketch.js/1.0.0/sketch.min.js"></script>
<script src="script.js"></script>
</body>
</html>
body{
background: #e3e3e3;
margin: 0;
text-align: center;
margin-top: 2%;
}
a{
color: #2096F1;
text-decoration: none;
}
#container{
display: inline-block;
width: 500px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
canvas{
display: inline-block;
}
.hud{
height: 50px;
margin-bottom: 2px;
line-height: 50px;
text-align: right;
font-family: sans-serif;
font-size: 2em;
}
.score{
display: inline-block;
padding-right: 10px;
-webkit-transition: color 0.3s;
-o-transition: color 0.3s;
transition: color 0.3s;
}
var ColorMatch = Sketch.create({
fullscreen: false,
height: 500,
width: 500,
container: document.getElementById('container')
}),
i = w = h = 0;
function normalize(x, y, size) {
return {
x: Math.ceil( x / size) - 1,
y: Math.ceil( y / size) - 1
}
};
function distance(ax, ay, bx, by) {
return Math.sqrt(Math.pow( ax - bx, 2) + Math.pow( ay - by, 2));
};
function Particle(options) {
this.x = options.x;
this.y = options.y;
this.vx = options.vx;
this.vy = options.vy;
this.size = options.size;
this.color = options.color;
this.orbitX = options.orbitX;
this.orbitY = options.orbitY;
this.speedX = options.speedX;
this.speedY = options.speedY;
this.aceleration = 1.02;
this.angle = 0;
};
Particle.prototype.update = function() {
// this.vx *= this.aceleration;
// this.vy *= this.aceleration;
this.x += this.vx;
this.y += this.vy;
if( distance( this.x, this.y, ColorMatch.mouse.x, ColorMatch.mouse.y ) < 200 ){
this.x += this.vx;
this.y += this.vy;
}
this.x += Math.cos(this.angle * this.speedX) * this.orbitX;
this.y += Math.sin(this.angle * this.speedY) * this.orbitY;
this.angle += 0.02;
this.size *= 0.99;
};
Particle.prototype.draw = function() {
ColorMatch.fillStyle = this.color;
ColorMatch.fillRect(this.x, this.y, this.size, this.size);
};
function Score(options) {
this.x = options.x;
this.y = options.y;
this.initialPosition = {
x: this.x,
y: this.y
};
this.stayPosition = {
x: this.x,
y: this.y - 20
};
this.text = options.text;
this.color = '#fff';
};
Score.prototype.update = function() {
if(this.y > this.initialPosition.y - 15){
this.y += (this.stayPosition.y - this.y) * 0.05;
} else {
this.y += (-50 - this.y) * 0.05;
this.x += (450 - this.x) * 0.05;
}
};
Score.prototype.draw = function() {
ColorMatch.fillStyle = this.color;
ColorMatch.fillText(this.text, this.x, this.y);
};
//Each tile
function Node(options){
this.x = options.x;
this.y = options.y;
this.position = {x: this.x * ColorMatch.gridSize, y: -(this.y * ColorMatch.gridSize) - 300} || options.position;
this.adjacent = [];
this.type = random([1,2,2,3,4,4,5]) || options.type;
this.color = ColorMatch.colors[this.type] || options.color;
}
Node.prototype.update = function() {
this.position.x += ((this.x * ColorMatch.gridSize) - this.position.x) * 0.2;
this.position.y += ((this.y * ColorMatch.gridSize) - this.position.y) * 0.2;
};
Node.prototype.draw = function() {
if(this.type !== 0){
ColorMatch.fillStyle = this.color;
} else {
ColorMatch.fillStyle = ColorMatch.colors[0];
}
ColorMatch.fillRect(this.position.x - 1, this.position.y - 1, ColorMatch.gridSize - 2, ColorMatch.gridSize - 2);
};
ColorMatch.setup = function() {
this.map = [];
this.gridSize = 50;
this.rows = this.height / this.gridSize;
this.cols = this.width / this.gridSize;
this.score = 0;
this.blockScore = 10;
this.scoreContainer = document.getElementById('score');
this.reachable = [];
this.particles = [];
this.particlesMax = 20;
this.particlesIndex = 0;
this.scoreParticles = [];
this.scoreParticlesMax = 10;
this.scoreParticlesIndex = 0;
this.path = [];
this.can = [];
this.currentTile = 0+','+0;
this.explored = [];
this.colors = ['#e3e3e3', '#16a085', '#2c3e50', '#e74c3c', '#2980b9', '#8e44ad'];
this.currentColor = this.colors[0];
this.normalizeMouse = {x: 0, y: 0};
this.setScore = function(score) {
this.score += score;
this.scoreContainer.style.color = this.currentColor;
this.scoreParticles[(this.scoreParticlesIndex++)%this.scoreParticlesMax] = new Score({
x: this.mouse.x,
y: this.mouse.y,
text: score,
color: this.currentColor
});
this.scoreContainer.innerHTML = this.score;
};
this.findAdjacents = function() {
for (h = 0; h < this.rows; h++) {
for (w = 0; w < this.cols; w++) {
var node = this.map[this.cols * h + w];
if(node !== null){
node.adjacent = [];
//up
if(h > 0 && this.map[this.cols * (h-1) + w] !== null){
node.adjacent.push(this.map[this.cols * (h-1) + w]);
}
//down
if(h < this.rows - 1 && this.map[this.cols * (h+1) + w] !== null){
node.adjacent.push(this.map[this.cols * (h+1) + w]);
}
//left
if(w > 0 && this.map[this.cols * h + (w - 1)] !== null){
node.adjacent.push(this.map[this.cols * h + (w - 1)]);
}
//right
if(w < this.cols - 1 && this.map[this.cols * h + (w + 1)] !== null){
node.adjacent.push(this.map[this.cols * h + (w + 1)]);
}
//diagonal
// //up-left
// if(h > 0 && w > 0){
// node.adjacent.push(this.map[this.cols * (h-1) + (w-1)]);
// }
// //up-right
// if(h > 0 && w < this.cols - 1){
// node.adjacent.push(this.map[this.cols * (h-1) + (w+1)]);
// }
// //down-left
// if(h < this.rows - 1 && w > 0){
// node.adjacent.push(this.map[this.cols * (h+1) + (w-1)]);
// }
// //down-right
// if(w < this.cols - 1 && w < this.cols - 1){
// node.adjacent.push(this.map[this.cols * (h+1) + (w+1)]);
// }
}
}
}
};
//generate a random map
this.generate = function() {
for (h = 0; h < this.rows; h++) {
for (w = 0; w < this.cols; w++) {
this.map[this.cols * h + w] = new Node({
x: w,
y: h
});
}
}
this.findAdjacents();
}
this.generate();
//used for pathfinder
this.getNodeIndex = function(node, list) {
for (i in list) {
if (node == list[i]) {
return i;
}
}
return -1;
}
//used for pathfinder
this.findNode = function(node, list) {
return this.getNodeIndex(node, list) >= 0;
};
//used for pathfinder
this.addReachable = function(node, adjacent) {
if(this.findNode(adjacent, this.explored) || this.findNode(adjacent, this.reachable)){
return;
}
adjacent.previous = this.node;
this.reachable.push(adjacent);
};
//used for pathfinder
this.removeReachable = function(node) {
this.reachable = this.reachable.filter(function(element) {
return element !== node;
});
};
//add the tiles that match to an array
this.addCan = function(node) {
if(this.findNode(node, this.can)){
return;
}
this.can.push(node);
};
};
//Pathfinder
ColorMatch.find = function(start) {
var node;
this.lookfor = this.map[this.cols * start.y + start.x].type;
this.reachable = [];
this.explored = [];
this.can = [];
this.reachable.push(start);
this.addCan(start);
while(this.reachable.length > 0){
node = this.reachable[0];
this.removeReachable(node);
this.explored.push(node);
for (var i in node.adjacent) {
if(node.adjacent[i].type === this.lookfor){
this.addReachable(node, node.adjacent[i]);
this.addCan(node.adjacent[i]);
}
}
}
//if find just one tile
if(this.can.length <= 1){
this.can = [];
}
};
ColorMatch.mousemove = function() {
this.normalizeMouse = normalize(this.mouse.x, this.mouse.y, this.gridSize);
//check if the tile exist and look for color match when the mouse is over a different tile
// if(this.map[this.cols * this.normalizeMouse.y + this.normalizeMouse.x] !== null && this.currentTile !== this.normalizeMouse.x+','+this.normalizeMouse.y){
// this.currentTile = this.normalizeMouse.x+','+this.normalizeMouse.y;
// this.can = [];
// }
};
ColorMatch.click = function() {
this.lookfor = this.map[this.cols * this.normalizeMouse.y + this.normalizeMouse.x];
this.currentColor = this.colors[this.lookfor.type];
this.find(this.map[this.cols * this.normalizeMouse.y + this.normalizeMouse.x]);
//if any color match, lose 200 points
if(this.can.length <= 1){
this.setScore(-200);
return false;
}
if(this.lookfor !== null){
//remove the matching tiles and throw particles
for (i = 0; i < this.can.length; i++) {
node = this.map[this.cols * this.can[i].y + this.can[i].x];
this.map[this.cols * this.can[i].y + this.can[i].x] = null;
for (o = 0; o < 4; o++) {
this.particles[(this.particlesIndex++)%this.particlesMax] = new Particle({
x: random(node.x * this.gridSize, (node.x * this.gridSize) + this.gridSize ),
y: random(node.y * this.gridSize, (node.y * this.gridSize) + this.gridSize ),
vx: random([-5,-3,5,3]),
vy: random([-5,-3,5,3]),
size: random(4,8),
orbitX: random(1,8),
orbitY: random(1,8),
speedX: random(1,8),
speedY: random(1,8),
color: this.colors[this.lookfor.type]
});
};
};
//add score
this.setScore(this.can.length * this.blockScore);
//look for empty blocks to fill up with the first not empty block
for (h = this.rows - 1; h >= 0; h--) {
for (w = this.cols - 1; w >= 0; w--) {
var node = this.map[this.cols * h + w];
if(this.map[this.cols * h + w] === null){
i = h;
while(i>=0 && i<10){
var block = this.map[this.cols * i + w];
if(block !== null){
this.map[this.cols * i + w] = null;
this.map[this.cols * h + w] = block;
block.y = h;
break;
}
i--;
};
}
};
};
//look for empty blocks to generate a new one
for (h = 0; h < this.rows; h++) {
for (w = 0; w < this.cols; w++) {
if(this.map[this.cols * h + w] === null){
this.map[this.cols * h + w] = new Node({
x: w,
y: h
})
}
}
}
//look adjacents block of each block
this.findAdjacents();
}
};
ColorMatch.update = function() {
//update each tile
for (h = 0; h < this.rows; h++) {
for (w = 0; w < this.cols; w++) {
node = this.map[this.cols * h + w];
if(node !== null){
node.update();
}
}
}
//update particle
for (i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].update();
};
//update score number
for (i = this.scoreParticles.length - 1; i >= 0; i--) {
this.scoreParticles[i].update();
};
};
ColorMatch.draw = function() {
//draw the map
for (h = 0; h < this.rows; h++) {
for (w = 0; w < this.cols; w++) {
node = this.map[this.cols * h + w];
if(node !== null){
node.draw();
}
};
};
//this.fillStyle = 'rgba(227,227,227,0.2)';
// for (var i = 0; i < this.can.length; i++) {
// this.fillRect(this.can[i].x * this.gridSize, this.can[i].y * this.gridSize, this.gridSize, this.gridSize);
// };
this.globalCompositeOperation = 'lighter';
//draw particles
for (i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].draw();
};
//draw score
this.font = "bold 20px sans-serif";
for (i = this.scoreParticles.length - 1; i >= 0; i--) {
this.scoreParticles[i].draw();
};
};