web-dev-qa-db-fra.com

Algorithme de rotation des pièces Tetris

Quels sont les meilleurs algorithmes (et explications) pour représenter et faire pivoter les éléments d'un jeu de tetris? Je trouve toujours les schémas de rotation et de représentation des pièces déroutants.

La plupart des jeux de tetris semblent utiliser un naïf "refaire le tableau de blocs" à chaque rotation:

http://www.codeplex.com/Project/ProjectDirectory.aspx?ProjectSearchText=tetris

Cependant, certains utilisent des nombres codés pré-construits et un décalage de bits pour représenter chaque morceau:

http://www.codeplex.com/wintris

Existe-t-il une méthode pour ce faire en utilisant les mathématiques (pas sûr que cela fonctionnerait sur un tableau de cellules)?

43
user21826

Il y a un nombre limité de formes, j'utiliserais donc une table fixe et aucun calcul. Cela fait gagner du temps.

Mais il existe des algorithmes de rotation. 

Choisissez un point central et faites pivoter pi/2. 

Si un bloc commence à (1,2), il se déplace dans le sens des aiguilles d'une montre vers (2, -1) et (-1, -2) et (-1, 2) . est tourné.

Chaque x est le y précédent et chaque y - le x précédent. Ce qui donne la matrice suivante:

[  0   1 ]
[ -1   0 ]

Pour une rotation dans le sens antihoraire, utilisez:

[  0  -1 ]
[  1   0 ]
31
Toon Krijthe

Lorsque j'essayais de comprendre comment les rotations fonctionneraient pour mon jeu de tetris, c'était la première question que je trouvais sur le débordement de pile. Même si cette question est ancienne, je pense que mon apport aidera les autres à essayer de résoudre ce problème de manière algorithmique. Tout d'abord, je ne suis pas d'accord pour dire que coder en dur chaque pièce et la rotation seront plus faciles. La réponse de Gamecat est correcte, mais je voulais développer. Voici les étapes que j'ai suivies pour résoudre le problème de rotation en Java.

  1. Pour chaque forme, déterminez l’origine de son origine. J'ai utilisé les points sur le diagramme de cette page pour assigner mes points d'origine. Gardez à l'esprit que, selon votre implémentation, vous devrez peut-être modifier l'Origine chaque fois que l'utilisateur déplace la pièce.

  2. La rotation suppose que l'origine est située au point (0,0). Vous devrez donc traduire chaque bloc avant de pouvoir le faire pivoter. Par exemple, supposons que votre origine se situe actuellement au point (4, 5). Cela signifie qu'avant de pouvoir faire pivoter la forme, chaque bloc doit être converti -4 dans la coordonnée x et -5 dans la coordonnée y pour être relatif à (0,0).

  3. En Java, un plan de coordonnées typique commence par le point (0,0) dans le coin supérieur gauche, puis augmente vers la droite et vers le bas. Pour compenser cela dans mon implémentation, j'ai multiplié chaque point par -1 avant la rotation.

  4. Voici les formules que j'ai utilisées pour déterminer les nouvelles coordonnées x et y après une rotation dans le sens anti-horaire. Pour plus d'informations à ce sujet, je voudrais consulter la page Wikipedia sur Rotation Matrix . x 'et y' sont les nouvelles coordonnées:

    x '= x * cos (PI/2) - y * sin (PI/2) et y' = x * sin (PI/2) + y * cos (PI/2) .

  5. Pour la dernière étape, je viens de passer par les étapes 2 et 3 dans l’ordre inverse. Donc, j'ai multiplié mes résultats par -1 à nouveau et ensuite converti les blocs à leurs coordonnées d'origine.

Voici le code qui a fonctionné pour moi (en Java) pour avoir une idée de la façon de le faire dans votre langue:

public synchronized void rotateLeft(){

    Point[] rotatedCoordinates = new Point[MAX_COORDINATES];

    for(int i = 0; i < MAX_COORDINATES; i++){

        // Translates current coordinate to be relative to (0,0)
        Point translationCoordinate = new Point(coordinates[i].x - Origin.x, coordinates[i].y - Origin.y);

        // Java coordinates start at 0 and increase as a point moves down, so
        // multiply by -1 to reverse
        translationCoordinate.y *= -1;

        // Clone coordinates, so I can use translation coordinates
        // in upcoming calculation
        rotatedCoordinates[i] = (Point)translationCoordinate.clone();

        // May need to round results after rotation
        rotatedCoordinates[i].x = (int)Math.round(translationCoordinate.x * Math.cos(Math.PI/2) - translationCoordinate.y * Math.sin(Math.PI/2)); 
        rotatedCoordinates[i].y = (int)Math.round(translationCoordinate.x * Math.sin(Math.PI/2) + translationCoordinate.y * Math.cos(Math.PI/2));

        // Multiply y-coordinate by -1 again
        rotatedCoordinates[i].y *= -1;

        // Translate to get new coordinates relative to
        // original Origin
        rotatedCoordinates[i].x += Origin.x;
        rotatedCoordinates[i].y += Origin.y;

        // Erase the old coordinates by making them black
        matrix.fillCell(coordinates[i].x, coordinates[i].y, Color.black);

    }
    // Set new coordinates to be drawn on screen
    setCoordinates(rotatedCoordinates.clone());
}

Cette méthode suffit à faire pivoter votre forme vers la gauche, ce qui s'avère beaucoup plus petit (selon votre langue) que de définir chaque rotation pour chaque forme.

25
Jason

C'est ce que j'ai fait récemment dans un jeu de tetris basé sur jQuery/CSS.

Déterminez le centre du bloc (à utiliser comme point de pivot), c’est-à-dire le centre de la forme du bloc . Appelez-le (px, py).

Chaque brique constituant la forme du bloc tournera autour de ce point . Pour chaque brique, vous pouvez appliquer le calcul suivant ...

Lorsque la largeur et la hauteur de chaque brique sont q, l'emplacement actuel de la brique (du coin supérieur gauche) est (x1, y1) et le nouvel emplacement de la brique est (x2, y2):

x2 = (y1 + px - py)

y2 = (px + py - x1 - q)

Pour faire pivoter le sens opposé:

x2 = (px + py - y1 - q)

y2 = (x1 + py - px)

Ce calcul est basé sur une transformation de matrice affine 2D . Si vous êtes intéressé par la façon dont je suis arrivé à ceci, faites le moi savoir.

12
Dave Cluderay

Personnellement, je viens toujours de représenter les rotations à la main - avec très peu de formes, il est facile de coder de cette façon. Fondamentalement j'avais (comme pseudo-code)

class Shape
{
    Color color;
    ShapeRotation[] rotations;
}

class ShapeRotation
{
    Point[4] points;
}

class Point
{
    int x, y;
}

Au moins conceptuellement - un tableau multidimensionnel de points directement en forme ferait l'affaire aussi :)

11
Jon Skeet

Vous pouvez faire pivoter une matrice uniquement en y appliquant des opérations mathématiques. Si vous avez une matrice, dites:

Mat A = [1,1,1]
        [0,0,1]
        [0,0,0]

Pour le faire pivoter, multipliez-le par sa transposée puis par cette matrice ([I] dentity [H] orizontaly [M] irrored):

IHM(A) = [0,0,1]
         [0,1,0]
         [1,0,0]

Ensuite, vous aurez:

Mat Rotation = Trn(A)*IHM(A) = [1,0,0]*[0,0,1] = [0,0,1]
                               [1,0,0] [0,1,0] = [0,0,1]
                               [1,1,0] [1,0,0] = [0,1,1]

Remarque: Le centre de rotation sera le centre de la matrice, dans ce cas à (2,2).

7
Ricardo Sousa

Puisqu'il n'y a que 4 orientations possibles pour chaque forme, pourquoi ne pas utiliser un tableau d'états pour la forme et la rotation de CW ou de CCW ne fait qu'augmenter ou décroître l'index de l'état de la forme (avec un enveloppement pour l'indice)? Je pense que cela pourrait être plus rapide que d’effectuer des calculs de rotation et ainsi de suite.

6
AgentThirteen

J'ai dérivé un algorithme de rotation à partir de rotations de matrice ici . Pour résumer: Si vous avez une liste de coordonnées pour toutes les cellules qui composent le bloc, par ex. [(0, 1), (1, 1), (2, 1), (3, 1)] ou [(1, 0), (0,1,), (1, 1), ]:

 0123       012
0....      0.#.
1####  or  1###
2....      2...
3....

vous pouvez calculer les nouvelles coordonnées en utilisant

x_new = y_old
y_new = 1 - (x_old - (me - 2))

pour la rotation dans le sens horaire et

x_new = 1 - (y_old - (me - 2))
y_new = x_old

pour la rotation dans le sens anti-horaire. me est l'étendue maximale du bloc, c'est-à-dire 4 pour les blocs I, 2 pour les blocs O et 3 pour tous les autres blocs.

3
mdm

Si vous faites cela en python, à base de cellules plutôt que de paires de coordonnées, il est très simple de faire pivoter une liste imbriquée.

rotate = lambda tetrad: Zip(*tetrad[::-1])

# S Tetrad
tetrad = rotate([[0,0,0,0], [0,0,0,0], [0,1,1,0], [1,1,0,0]])
2
BeMasher

Représentation

Représentez chaque pièce dans la matrice minimale où les 1 représentent les espaces occupés par le tétrimino et les 0 les espaces vides. Exemple:

originalMatrix = 
[0,   0,   1]
[1,   1,   1]

 enter image description here

Formule de rotation

clockwise90DegreesRotatedMatrix = reverseTheOrderOfColumns(Transpose(originalMatrix))

anticlockwise90DegreesRotatedMatrix = reverseTheOrderOfRows(Transpose(originalMatrix))

Illustration

originalMatrix = 
  x    y    z
a[0,   0,   1]
b[1,   1,   1]

transposed = transpose(originalMatrix)
  a   b
x[0,  1]
y[0,  1]
z[1,  1]

counterClockwise90DegreesRotated = reverseTheOrderOfRows(transposed)
  a   b
z[1,  1]
y[0,  1]
x[0,  1]

 enter image description here

clockwise90DegreesRotated = reverseTheOrderOfColumns(transposed)
  b   a
x[1,  0]
y[1,  0]
z[1,  1]

 enter image description here

2
ferit

Si nous supposons que le carré central du tétromino a des coordonnées (x0, y0) qui restent inchangées, la rotation des 3 autres carrés en Java ressemblera à ceci:

private void rotateClockwise()
{
    if(rotatable > 0)   //We don't rotate tetromino O. It doesn't have central square.
    {
        int i = y1 - y0;
        y1 = (y0 + x1) - x0;
        x1 = x0 - i;
        i = y2 - y0;
        y2 = (y0 + x2) - x0;
        x2 = x0 - i;
        i = y3 - y0;
        y3 = (y0 + x3) - x0;
        x3 = x0 - i;  
    }
}

private void rotateCounterClockwise()
{
    if(rotatable > 0)
    {
        int i = y1 - y0;
        y1 = (y0 - x1) + x0;
        x1 = x0 + i;
        i = y2 - y0;
        y2 = (y0 - x2) + x0;
        x2 = x0 + i;
        i = y3 - y0;
        y3 = (y0 - x3) + x0;
        x3 = x0 + i;
    }
}
2
Playmen Paychek

pour des morceaux de tetris de taille 3x3 retournez x et y de votre pièce puis échangez les colonnes extérieures c'est ce que j'ai compris quelque temps

1
JaMaZz

J'ai utilisé une position de forme et un ensemble de quatre coordonnées pour les quatre points de toutes les formes. Comme il s'agit d'un espace 2D, vous pouvez facilement appliquer une matrice de rotation 2D aux points.

Les points sont divs, leur classe css est donc activée. (Ceci est après avoir effacé la classe css de leur dernier tour.)

0
mspmsp

Si la taille du tableau est 3 * 3, le moyen le plus simple de le faire pivoter, par exemple dans le sens anti-horaire, est le suivant:

oldShapeMap[3][3] = {{1,1,0},
                     {0,1,0},
                     {0,1,1}};

bool newShapeMap[3][3] = {0};
int gridSize = 3;

for(int i=0;i<gridSize;i++)
    for(int j=0;j<gridSize;j++)
        newShapeMap[i][j] = oldShapeMap[j][(gridSize-1) - i];
/*newShapeMap now contain:    
                               {{0,0,1},
                                {1,1,1},
                                {1,0,0}};

*/ 
0
Mickey Tin

Python:

pieces = [
    [(0,0),(0,1),(0,2),(0,3)],
    [(0,0),(0,1),(1,0),(1,1)],
    [(1,0),(0,1),(1,1),(1,2)],
    [(0,0),(0,1),(1,0),(2,0)],
    [(0,0),(0,1),(1,1),(2,1)],
    [(0,1),(1,0),(1,1),(2,0)]
]

def get_piece_dimensions(piece):
    max_r = max_c = 0
    for point in piece:
        max_r = max(max_r, point[0])
        max_c = max(max_c, point[1])
    return max_r, max_c

def rotate_piece(piece):
    max_r, max_c = get_piece_dimensions(piece)
    new_piece = []
    for r in range(max_r+1):
        for c in range(max_c+1):
            if (r,c) in piece:
                new_piece.append((c, max_r-r))
    return new_piece
0
Vincent