I'm building a tetris-like game, where in stead of removing just one line when you've got a full line I remove all the connected pieces. This has got me stumped on the hard-drop after clearing the pieces.
See this example for a quick and dirty version of what I'm trying to do.
function Board (width, height) {
this.width = width;
this.height = height;
this.board = [];
this.pieces = [];
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
if (!this.board[y]) {
this.board[y] = [];
}
this.board[y][x] = null;
}
}
this.canPlace = function(piece, at) {
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if ((y+at.y >= this.height) || this.board[y+at.y][x+at.x]) {
return false;
}
}
}
return true;
}
this.hasFullLine = function(line) {
for (var x = 0; x < this.width; x++) {
if (!this.board[line][x]) {
return false;
}
}
return true;
}
this.place = function(piece) {
var position = piece.getPosition();
var shape = piece.getShape();
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (shape[y][x]) {
this.board[y+position.y][x+position.x] = piece;
}
}
}
if (this.pieces.indexOf(piece) === -1) {
this.pieces.push(piece);
}
piece.render();
}
this.hardDropPieces = function() {
var pieces = this.pieces.slice();
pieces = pieces.sort(function(a,b) {
var aBottom = a.getPosition().y+a.getHeight();
var bBottom = b.getPosition().y+b.getHeight();
return bBottom-aBottom;
});
for (var i = 0; i < pieces.length; i++) {
this.hardDrop(pieces[i]);
}
}
this.hardDrop = function(piece) {
var position = piece.getPosition();
this.clearArea(piece);
while(this.canPlace(piece, {x: piece.getPosition().x, y: piece.getPosition().y+1})) {
piece.setPosition(piece.getPosition().x, piece.getPosition().y+1);
}
this.place(piece);
}
this.clearArea = function(piece) {
var position = piece.getPosition();
var shape = piece.getShape();
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (shape[y][x]) {
this.board[y+position.y][x+position.x] = null;
}
}
}
}
this.remove = function(piece) {
this.clearArea(piece);
this.pieces.splice(this.pieces.indexOf(piece),1);
}
this.clearPiecesOnLine = function(line) {
var piecesToClear = [];
for (var x = 0; x < this.width; x++) {
var piece = this.board[line][x];
if (piecesToClear.indexOf(piece) === -1) {
piecesToClear.push(piece);
}
}
for (var i = 0; i < piecesToClear.length; i++) {
this.remove(piecesToClear[i]);
}
return piecesToClear;
}
this.toString = function() {
var str = "";
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
str += this.board[y][x] ? "1" : "0";
}
str += "\n";
}
return str;
}
}
function Piece (shape, fill, stroke, paper, cellWidth) {
this.shape = shape;
this.fill = fill;
this.stroke = stroke;
this.cellWidth = cellWidth;
this.svgGroup = paper.g().append();
this.position = {x:0, y:0};
this.width = this.shape[0].length;
this.height = this.shape.length;
this.removed = false;
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
if (this.shape[y][x]) {
var rect = paper.rect(x*cellWidth, y*cellWidth, cellWidth, cellWidth);
rect.attr({
fill: this.fill,
stroke: this.stroke
});
rect.appendTo(this.svgGroup);
}
}
}
this.setPosition = function(x, y) {
this.position.x = x;
this.position.y = y;
}
this.getPosition = function() {
return this.position;
}
this.render = function() {
var matrix = new Snap.Matrix();
matrix.translate(this.position.x*cellWidth, this.position.y*cellWidth);
this.svgGroup.attr({
transform: matrix
});
}
this.getWidth = function() {
return this.width;
}
this.getHeight = function() {
return this.height;
}
this.getShape = function() {
return this.shape;
}
this.delete = function() {
this.svgGroup.remove();
}
this.isRemoved = function() {
return this.removed;
}
}
var shapes = [
[
[0,1,0],
[1,1,1]
],
[
[1,1,1,1]
],
[
[1,1,1],
[0,1,0],
[1,1,1]
],
[
[1,1],
[1,1]
],
[
[1,1,1],
[0,1,1],
[0,1,1],
[1,1,1]
],
[
[1,1,1,1],
[1,1,1,1],
[1,1,1,1],
[1,1,1,1]
],
[
[1,0,1],
[1,1,1]
]
];
var width = 10;
var height = 20;
var cellWidth = 20;
var paper = Snap("#svg");
var board = new Board(width, height);
var tick = 500;
paper.attr({
width: cellWidth*width,
height: cellWidth*height
});
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var rect = paper.rect(x*cellWidth, y*cellWidth, cellWidth, cellWidth);
rect.attr({
fill: "#ccc",
stroke: "#ddd"
});
}
}
var piece = new Piece(shapes[0], "red", "white", paper, cellWidth);
piece.setPosition(0, 18);
board.place(piece);
piece = new Piece(shapes[1], "orange", "white", paper, cellWidth);
piece.setPosition(3, 19);
board.place(piece);
piece = new Piece(shapes[2], "yellow", "white", paper, cellWidth);
piece.setPosition(2, 8);
board.place(piece);
piece = new Piece(shapes[3], "green", "white", paper, cellWidth);
piece.setPosition(0, 17);
board.place(piece);
piece = new Piece(shapes[4], "blue", "white", paper, cellWidth);
piece.setPosition(2, 15);
board.place(piece);
piece = new Piece(shapes[5], "indigo", "white", paper, cellWidth);
piece.setPosition(1, 11);
board.place(piece);
piece = new Piece(shapes[6], "violet", "white", paper, cellWidth);
piece.setPosition(7, 17);
piece.render();
function update() {
if (piece.isRemoved()) {
return;
}
var position = piece.getPosition();
if (board.canPlace(piece, {x:position.x,y:position.y+1})) {
piece.setPosition(position.x,position.y+1);
board.place(piece);
for (var y = 0; y < piece.getHeight(); y++) {
if (board.hasFullLine(piece.getPosition().y+y)) {
var removed = board.clearPiecesOnLine(piece.getPosition().y+y);
setTimeout(function() {
for (var i = 0; i < removed.length; i++) {
removed[i].delete();
}
board.hardDropPieces();
},tick);
}
}
}
}
setTimeout(update, tick);
That's pretty much the gist of the Board-logic. Placed pieces kept by reference in an array, after clearing I sort the pieces that are not removed by their lowest point and then drop each one of them as far as they can go.
This works when no pieces are interconnected, but I just can't figure out how to do it when they are, as in this example.
Obviously, the blue piece is the lowest point, but it cannot move downards since the green piece is inside of it. I thought about merging them and dropping them, but that leads to other problems. Like what would happen in this case?
I'm pretty sure I'm just being thick, and there's a relatively easy way of fixing this...? Any help would be much appreciated!
All the pieces are automatically generated, and there's way too many, and more could be added any time, to not make a general solution.