Heavy figures in a tetris game

I create a game similar to Tetris, where instead of deleting one line, when you have a full line, I delete all the related parts. This led me to a standstill 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); 

This is largely the essence of rule-logic. The placed pieces are stored by reference in the array, after cleaning, I sort the pieces that are not deleted by their lowest point, and then throw each of them as far as they can go.

This works when no fragments are connected to each other, but I just can’t figure out how to do this when they are, as in this example .

Obviously, the blue piece is the lowest point, but it cannot move down, since the green piece is inside it. I thought about combining and lowering them, but this leads to other problems. Like what happens in this case ?

I'm sure I'm just fat and there is a relatively easy way to fix this ...? Any help would be greatly appreciated!

All parts are automatically generated, and there are too many, and much more can be added at any time, so as not to create a common solution.

+4
source share
1 answer

I found two parts that had missing logic. The first part is where you made the drops. You need to do this in one step for each block, and then continue to do this until you can no longer refuse. Like this

 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; }); var canStillDrop = true; while (canStillDrop) { // Keep going until we can't drop no more canStillDrop = false; for (var i = 0; i < pieces.length; i++) { canStillDrop = this.hardDrop(pieces[i]) ? true : canStillDrop; } } } this.hardDrop = function(piece) { var didDrop = false; var position = piece.getPosition(); this.clearArea(piece); if(this.canPlace(piece, {x: position.x, y: position.y+1})) { piece.setPosition(position.x, position.y+1); didDrop = true; // Oh, I see we have dropped } this.place(piece); return didDrop; // Did we drop a spot? Then we should keep going } 

The second part is that you can use a little recursion to check if any of the tiles that drop you are actually connected to the floor. This one you already know:

 this.canPlace = function(piece, at) { // Will it fall below the floor? Then it a no-go if (piece.getHeight()+at.y > this.height) { return false; } // Loop through shape for (var y = 0; y < piece.getHeight(); y++) { for (var x = 0; x < piece.getWidth(); x++) { // Ignore non-shape positions if (!piece.shape[y][x]) continue; // Get piece at current shape position var pieceAtPos = this.board[y+at.y][x+at.x]; // Is the piece (or any that it resting on) connected to the floor? if (pieceAtPos && pieceAtPos!==piece && this.isPieceGrounded(pieceAtPos, [piece]) ){ return false; } } } return true; } 

But also welcome isPieceGrounded .

 this.isPieceGrounded = function(piece, testedPieces) { // Check all positions BELOW the piece var at = { x: piece.getPosition().x, y: piece.getPosition().y+1 }; // Is it connected to the floor? if (piece.getHeight()+at.y+1 >= this.height) { return true; } // *Sigh* Loop through THIS whole piece for (var y = 0; y < piece.getHeight(); y++) { for (var x = 0; x < piece.getWidth(); x++) { if (!piece.shape[y][x]) continue; var pieceAtPos = this.board[y+at.y][x+at.x]; if (pieceAtPos && pieceAtPos!==piece && testedPieces.indexOf(pieceAtPos) < 0) { // Keep a list of all tested pieces so we don't end up in an infinite loop by testing them back and forth testedPieces.push(pieceAtPos); // Let test that one and all its connected ones as well if (this.isPieceGrounded(pieceAtPos, testedPieces)) { return true; }; } } } return false; } 

http://jsfiddle.net/971yvc8r/2/

I am sure that there are many different solutions, but I think that something like this might be the most effective.

+2
source

All Articles