INFO505 Programmation C
Loading...
Searching...
No Matches
TP2 - Mini-projet Tetris simplifié

1 - Objectifs

L'objectif de ce TP, ainsi que du prochain, est de réaliser une version simplifiée du jeu Tetris. Si vous ne connaissez pas ce jeu, vous pouvez en apprendre les principes à l'adresse :

http://fr.wikipedia.org/wiki/Tetris

Il est important de LIRE ATTENTIVEMENT ET EN ENTIER CE DOCUMENT AVANT DE COMMENCER A CODER!!

L'idée ici est de faire un jeu Tetris console, qui codera toute la logique du jeu, sans les aspects graphiques ni temps réel. Ces deux derniers aspects seront traités dans le TP suivant (TP3 - Mini-tetris graphique avec GTK). Ici, l'objectif est de vous faire travailler sur les structures de données, avec d'abord une représentation tableau à 2 dimensions, puis ensuite une représentation liste, pour stocker la grille du tetris.

Plusieurs contraintes sur la structure de votre programme vous sont imposées.

Pour ce TP, vous devrez remettre au moins trois fichiers sous forme d'archive: tp2-tableau.c, tp2-liste.c, et un README.txt. Les points suivants seront évalués :

  • Le README.txt doit indiquer quelles questions vous avez complètement traitées, et quelles questions ont été abordées (en précisant les problèmes ou erreurs). Vous perdrez des points sans ce fichier README, où si les informations données ne coincident pas avec le code rendu.
  • La lisibilité de votre programme (choix pertinent pour les noms de variables, indentation, etc.),
  • Chaque fichier devra comporter un commentaire au début avec vos nom, prénom, filière, intitulé du cours et numéro de TP,
  • Votre code devra compiler avec gcc sans erreurs ni warnings, et fonctionner sous Linux.
  • Toute mémoire allouée devra être libérée.
  • Ce TP peut être fait par binôme (mais mettez les deux noms !).
  • Vous m'enverrez une première version de votre TP à la fin de votre séance de TP (dépend des groupes), via TPLab. Vous aurez 7 jours ensuite pour envoyer une version finale (dépend des groupes aussi). Il faudra une archive (ZIP, ou TAR.GZ) nommée TP2-[votre ou vos nom(s)] contenant tous les fichiers sources, entêtes, le README mis à jour, éventuellement un makefile.

2 - Une version simplifiée du jeu Tetris

Dans le cadre de ce TP, le jeu sera implémenté uniquement en texte et son déroulement se fera entièrement dans le terminal. Vous aurez ainsi toute la partie logique du jeu qui fonctionne à la fin du TP. Pour cette partie, vous écrirez tout dans un fichier tp2-[votre nom]-tableau.c

Note
Vous étofferez votre jeu en mode graphique temps-réel à la séance suivante.

Tout d'abord nous allons simplifier grandement le déroulement du jeu.

  1. L'interactivité du jeu sera réduite au minimum :
  • La lecture du clavier étant effectuée à l'aide de la commande "scanf" aucun délai n'est imposé au joueur.
int colonne;
scanf( "%d", &colonne );
  • Lorsqu'un joueur a choisi la colonne dans laquelle faire tomber la pièce, celle-ci est directement ajouté à la position la plus basse qu'elle peut atteindre.

    Voici un exemple :

    @
    @@@
    || ||
    || ||
    || ||
    || ||
    || ||
    || ||
    || ||
    || ||
    || ||
    || ||
    |||||||||||||||||||
    012345678901234
    Dans quelle colonne placer cette piece?
    >

Si le joueur choisit, par exemple, la colonne 8 alors la pièce est insérée de manière à ce que sa colonne la plus à gauche (celle indiquée par une flèche dans l'exemple) soit dans la colonne #8.

Dans quelle colonne placer cette piece?
> 8
l
l
l
l
|| ||
|| ||
|| ||
|| ||
|| ||
|| ||
|| ||
|| ||
|| @ ||
|| @@@ ||
|||||||||||||||||||
012345678901234
Dans quelle colonne placer cette piece?
>

Par convention, la grille de jeu est modélisée de bas en haut, c'est-à-dire que la ligne 0 est en bas, la ligne 1 est juste au-dessus, etc. Attention, pour l'affichage sur la console, il faudra donc partir de la dernière ligne et afficher jusqu'à la première ligne. Ci-dessous, vous avez à gauche le numéro de ligne et en bas le numéro de colonne de chaque case. Les barres | ne font pas partie de la grille et sont juste affichés.

9 || ||
8 || ||
7 || ||
6 || ||
5 || ||
4 || ||
3 || ||
2 || ||
1 || @ ||
0 || @@@ ||
|||||||||||||||||||
0 1
012345678901234
Note
Dans un premier temps, les pièces sont dessinées de manière à ce que la ligne la plus basse soit continue et au moins aussi large que toutes les autres au-dessus.
l
l R LL Z
@ l $$ R @@@ L ZZ
@@@ l $$ RR ... @ L Z
pièces valides pièces non-valides

Comme nous le verrons plus tard, cette contrainte simplifie grandement le positionnement des pièces.

Remarques :

  • Les bords de la grille de jeu sont représentés par le symbole '|' (pipe)
  • Les pièces sont représentées à l'aide de n'importe quels caractères autre qu'un espace (' ') ou un pipe ('|').

3 - Réalisation du programme en C

3.1 - Constantes

Déclarez 4 constantes avec la directive #define :

  • HAUTEUR : le nombre de lignes de l'espace de jeu. On la fixe à 10.
  • LARGEUR : le nombre de colonnes de l'espace de jeu. On la fixe à 15.
  • NB_PIECES : le nombre de pièces différentes. À fixer en fonction du nombre de pièces que vous déciderez d'include.
  • HAUTEUR_MAX_DES_PIECES : le nombre maximum de lignes qui peut occuper une pièce. On fixe cette valeur à 4.
    #define HAUTEUR 4
    ...
    typedef char Grille[ HAUTEUR ][ LARGEUR ];

3.2 - Nouveaux types

  • Piece : une struct contenant les champs suivants
    • hauteur : le nombre de lignes qu'occupe la pièce.
    • largeur : le nombre de colonnes qu'occupe la pièce.
    • forme : un tableau de chaînes de caractères, permettant de dessiner la pièce. Ce sera un tableau de HAUTEUR_MAX_DES_PIECES cases, chaque case étant de type char*, qui pointe donc vers une chaîne de caractères.
struct SPiece {
int hauteur;
int largeur;
char* forme[ HAUTEUR_MAX_DES_PIECES ];
};
typedef struct SPiece Piece;
  • Grille : Un tableau bidimensionnel de caractères qui représente l'espace de jeu.

3.3 - Initialisation et affichage de la grille

  • Écrivez une procédure void initialiseGrille( Grille G ) qui remplit une grille de jeu G passé en paramètre avec le caractère ' ' (espace).
  • Écrivez une procédure char lireCase( Grille G, int i, int j ) qui, pour une grille, un numéro de ligne et un numéro de colonne passés en paramètre, retourne le contenu de la case correspondante de cette grille. Vous devez toujours utiliser cette procédure lorsque vous voulez lire le contenu d'une case la grille de jeu. Ajoutez dans cette procédure un test vérifiant que la ligne et la colonne sont valides. Si tel n'est pas le cas, une erreur doit être affichée.
  • Écrivez une procédure void afficheGrille( Grille G ) afin d'afficher le contenu d'une grille donnée en entrée en l'encadrant à gauche, à droite et en dessous avec le caractère '|' (pipe). Ajoutez également des nombres qui indiquent le numéro de chacune des colonnes (voir l'exemple plus haut).
  • Écrivez une procédure main qui vous permet de tester le fonctionnement des deux procédures précédentes. C'est au début du main qu'il faudra instancier une grille.

    TESTEZ VOTRE PROGRAMME À CHAQUE ÉTAPE DE SON DÉVELOPPEMENT!!

3.4 - Génération et affichage des pièces

  • Écrivez une procédure générerPieces qui initialise un tableau de pièce avec chacune des pièces qui apparaîtront dans le jeu. Les pièces sont écrites une par une "à la main" dans cette fonction. Voici un exemple d'initilisation de pièces :
    tabPiece[0].hauteur = 4;
    tabPiece[0].largeur = 1;
    tabPiece[0].forme[3] = "I";
    tabPiece[0].forme[2] = "I";
    tabPiece[0].forme[1] = "I";
    tabPiece[0].forme[0] = "I";
    tabPiece[1].hauteur = 2;
    tabPiece[1].largeur = 3;
    tabPiece[1].forme[1] = " @ ";
    tabPiece[1].forme[0] = "@@@";
    ...

Il est important que chacune des chaînes de caractères du tableau forme aient exactement la même taille, cette taille étant la valeur donnée au champs largeur (1 ou 3 dans cet exemple). Votre jeu doit fournir au moins cinq pièces différentes, à vous de décider lesquelles. Notez que comme le joueur ne peut pas encore tourner les pièces, les quatre pièces suivantes sont considérées comme étant différentes :

# #
# # # #
## ## ### ###
  • Faites impérativement la pièce verticale de 4 et la pièce 2x2, elles sont bien pratiques pour tester le tetris.
    l
    l
    l    %%
    l    %%
    
  • Écrivez une procédure affichePiece qui affiche une pièce passée en paramètre et ajoute une flèche (ou tout autre symbole) sous la colonne la plus à gauche de manière à indiquer au joueur où sera inséré la pièce.
    @
    @@@

3.5 - Écriture dans la grille

  • Écrivez une procédure void ecrireCase( Grille G, int i, int j, char c ) qui, pour une grille, un numéro de ligne et un numéro de colonne passés en paramètre, inscrit dans la case correspondante de la grille de jeu un caractère également spécifié en paramètre. Vous devez toujours utiliser cette procédure pour écrire dans la grille de jeu. Il est là aussi fortement conseillé de tester la validité des numéros de ligne et colonne et d'afficher une erreur dans le cas contraire.

Lorsqu'un joueur décide de placer une pièce dans une colonne donnée, il faut être en mesure de déterminer à quelle hauteur la pièce sera déposée. On a imposé une forme particulière aux pièces de manière à simplifier cette étape. Comme la ligne la plus basse d'une pièce est forcément la plus large, il suffit de déterminer, pour chacune des colonnes que va occuper cette pièce, quelle est hauteur de la plus haute case occupée.

  • Écrivez une procédure int hauteurPlat( Grille g, int c1, int c2 ) qui, étant donné une grille et un intervalle de colonnes, retourne la ligne la plus basse où on peut placer une pièce sans écraser une pièce déjà placée. C'est équivalent à retourner un plus la hauteur maximale où se trouve une case occupée entre les colonnes spécifiées. Cette procédure sera mise à jour dans un deuxième temps pour prendre en compte des pièces plus complexes (voir hauteurExacte plus loin).
  • Écrivez une procédure void ecrirePiece( Grille G, Piece P, int c, int h ) qui reçoit en paramètre une grille, une pièce, un numéro de colonne ainsi qu'une hauteur et ajoute cette pièce à la grille de manière à ce que la colonne la plus à gauche de la pièce corresponde au numéro de colonne spécifiée.
    Warning
    Faites bien attention à ne rien écrire là où la pièce est vide (ie. un espace). Ce sera important pour la section 3.9 - Pièces générales.

3.6 - Lecture des entrées

  • Écrivez une procédure pieceAleatoire qui choisit une pièce au hasard parmi celles que vous avez définies. Pour choisir un nombre aléatoirement dans l'ensemble {0,1,2,...,n-1} on peut utiliser la commande suivante :
    #include <stdlib.h>
    ...
    int alea = (int)(((double)rand()/((double)RAND_MAX)) * (NB_PIECES));
  • Modifiez la fonction main en y ajoutant une boucle principale de manière à :
    • Afficher une pièce choisie aléatoirement,
    • Affiche la grille de jeu,
    • Demande à l'utilisateur d'entrer le numéro de la colonne où il veut mettre la pièce,
    • Ajoute la pièce à la grille de jeu.
    • Répéter les étapes précédentes jusqu'à ce que l'utilisateur entre la valeur -1 indiquant qu'il souhaite quitter le programme.
Note
Vous noterez que l'aléatoire ne semble pas si aléatoire que ça, avec toujours la même suite de pièces successives. C'est voulu ! En fait, ce sont des suites pseudo-aléatoires, c'est-à-dire qu'elles donnent des résultats déterministes. Si on veut un peu plus d'aléatoire, on change alors la graine (seed en anglais) du générateur pseudo-aléatoire pour l'initialiser à une valeur différente à chaque run. Par exemple, on peut mettre au début de la fonction main :
#include <time.h>
...
int main(...)
{
srand( time( NULL ) );
...

3.7 - Détection de la fin de la partie

  • Modifiez votre code de manière à ce qu'avant de faire une appel à ecrirePiece votre programme détecte si la pièce va dépasser de la grille de jeu. Si tel est le cas, on affiche un message informant le joueur qu'il a perdu la partie ainsi que le nombre de pièces qu'il a réussi à placer. La grille est alors réinitialisée et une nouvelle partie démarre.

3.8 - Effacement de lignes

  • Écrivez une procédure void supprimerLigne( Grille G, int i ) qui efface le contenu d'une ligne dont le numéro est passé en paramètre et fait descendre toutes celles au-dessus. Plus précisément, lorsqu'une ligne est supprimée, le contenu de chacune des lignes au dessus de celle-ci doit être recopié dans la ligne d'en dessous et la ligne la plus haute de la grille de jeu est remplacée par une ligne vide.
  • Écrivez une fonction int nettoyer( Grille G ) qui supprime toutes les lignes ne contenant aucune case vide et qui retourne le nombre de lignes supprimées (utile si on veut gérer le score).
  • Modifiez votre code de manière à ce qu'à chaque fois que le joueur place une pièce, un appel à nettoyer soit effectué.

Vous devriez avoir maintenant une version jouable. Malheureusement, les pièces étant toutes plus larges à la base, vous allez voir qu'il est difficile de gagner à ce Tetris.

3.9 - Pièces générales

On autorise maintenant le type de pièces ci-dessous:

LL Z
@@@ L ZZ
@ L Z

Il n'est plus suffisant d'utiliser hauteurPlat pour détecter la position où va tomber la pièce. On peut juste dire que hauteurPlat donne la "pire" hauteur possible, mais potentiellement la pièce peut aller plus bas. Ecrivez donc la fonction

int hauteurExacte( Grille g, int col_gauche, Piece* piece );

qui vous retourne cette hauteur exacte. Substituez ensuite hauteurExacte à hauteurMax dans le code. Si la procédure ecrirePiece est correctement écrite, alors cela devrait fonctionner du premier coup.

Note
L'idée est de calculer colonne par colonne de la pièce à quelle hauteur on peut poser la pièce. Ensuite, la hauteur où on peut poser est le maximum des hauteurs calculées pour chaque colonne.
H   01      colonne piece
    ##        0 : hauteur_grille(2+0) = 3, hauteur_piece(0) = 2
     #          => hauteur_exacte(0) = 3 - 2 = 1
     #        1 : hauteur_grille(2+1) = 2, hauteur_piece(1) = 0
                => hauteur_exacte(1) = 2 - 0 = 2
3        L    => hauteur_exacte = max(1,2) = 2
2  AA  AAL
1  LLLLAAL
0 BBAABBAA
  01234567  colonne grille     

3.10 - Tourner les pieces

On peut aussi implémenter les rotations. Cela se fait simplement en créant les rotations d'une pièce comme autant de pièces différentes. On mémorise ensuite dans un tableau rotD que la rotation d'une pièce à droite donne le numéro d'une autre pièce et similairement avec un tableau rotG. Enfin, l'utilisateur tape 'g' ou 'd' pour tourner la pièce avant de choisir la colonne. La lecture au clavier se fera plutôt en lisant une chaîne de caractères et en l'analysant. En pseudo-code, ça peut ressembler à ça:

...
int colonne;
char str[ 8 ];
while ( 1 ) {
printf( "(g)auche, (d)roite ou (0-14) colonne: " );
if ( scanf( "%7s", str ) == 1 ) {
if ( str[ 0 ] == 'g' ) { tourne à gauche la piece }
else if ( str[ 0 ] == 'd' ) { tourne à droite la piece }
else {
colonne = atoi( str );
break;
}
réaffiche la pièce
}
...
Note
Les tableaux rotD et rotG sont de simples tableaux d'entiers, pour aller de numéro de pièces en numéro de pièces. Par exemple, dans la situation suivante, on aurait :
# ##
# # # ### %%
## ### # # %% ...
n° 0 1 2 3 4
rotG 1 2 3 0 4
rotD 3 0 1 2 4 ...

4 - Implémentation à l'aide d'une liste chaînée

Cette partie est à faire dans un deuxième temps, une fois que le tetris "tableau" fonctionne (en effet, il servira pour le tetris graphique).

On propose une autre structure de données pour représenter la grille de jeu, où chaque ligne est indépendante des autres et où elles sont placées dans une liste chaînée. Ainsi, lorsqu'une ou plusieurs lignes seront supprimées, il suffira de supprimer un ou des éléments de la liste, les autres lignes seront donc automatiquement déplacées. Pour cela, nous allons remplacer le tableau servant à représenter la grille de jeu par une liste doublement chaînée.

  • Commencez par effectuer une copie de sauvegarde de votre code. Appelez ce fichier
    tp2-[votre nom]-tableau.c

Avant de modifier votre code, récupérez les codes suivants (Liste.h, Liste.c, test-Liste.c), compilez-les avec

gcc Liste.c test-Liste.c -o test-Liste
Definit le noeud d'une liste doublement chaînée.
Definition Liste.h:7

et exécutez le programme.

// Liste.h
#ifndef _LISTE_H_
#define _LISTE_H_
typedef double Elem; /* Vous changerez après ce type lorsque vous l'utiliserez pour le tetris */
struct SCellule {
Elem val;
struct SCellule* pred;
struct SCellule* succ;
};
typedef struct SCellule Cellule;
typedef Cellule* Adr;
typedef Cellule Liste;
/* Ici, la liste vide est une liste avec un élément (non utilisé). */
/* alloue dynamiquement une liste et retourne son adresse */
extern Liste* Liste_creer();
/* initialise correctement la liste donnée en paramètre, comme si elle était vide. */
extern void Liste_init( Liste* L );
/* détruit tous les éléments stockés dans la liste L. La liste est vide après.*/
extern void Liste_termine( Liste* L );
/* détruit tous les éléments stockés dans la liste L, et libère l'espace mémoire de la liste. */
extern void Liste_detruire( Liste* L );
/* retourne l'adresse du premier élément. */
extern Adr Liste_debut( Liste* L );
/* retourne l'adresse après le dernier élément. */
extern Adr Liste_fin( Liste* L );
/* passe à l'élément suivant. */
extern Adr Liste_suivant( Liste* L, Adr A );
/* passe à l'élément précédent. */
extern Adr Liste_precedent( Liste* L, Adr A );
/* insère devant l'élément A un nouvel élément de valeur v dans L. */
extern Adr Liste_insere( Liste* L, Adr A, Elem v );
/* supprime l'élément A dans L. */
extern void Liste_supprime( Liste* L, Adr A );
/* retourne la valeur stockée dans l'élément A de la liste L. */
extern Elem Liste_valeur( Liste* L, Adr A );
/* modifie la valeur stockée dans l'élément A de la liste L, en lui assignant la valeur v. */
extern void Liste_modifie( Liste* L, Adr A, Elem v );
#endif
struct SCellule * pred
pointeur vers le noeud précédent
Definition Liste.h:9
struct SCellule * succ
pointeur vers le noeud suivant
Definition Liste.h:10
Elem val
valeur stockée dans le noeud
Definition Liste.h:8
// Liste.c
#include <stdlib.h>
#include "Liste.h"
Liste* Liste_creer()
{
Liste* L = (Liste*) malloc( sizeof( Liste ) );
Liste_init( L );
return L;
}
void Liste_init( Liste* L )
{
L->succ = L;
L->pred = L;
}
void Liste_termine( Liste* L )
{
while ( Liste_debut( L ) != Liste_fin( L ) )
Liste_supprime( L, Liste_debut( L ) );
}
void Liste_detruire( Liste* L )
{
Liste_termine( L );
}
Adr Liste_debut( Liste* L )
{
return L->succ;
}
Adr Liste_fin( Liste* L )
{
return L;
}
Adr Liste_suivant( Liste* L, Adr A )
{
return A->succ;
}
Adr Liste_precedent( Liste* L, Adr A )
{
return A->pred;
}
Adr Liste_insere( Liste* L, Adr A, Elem v )
{
Adr ncell = (Adr) malloc( sizeof( Cellule ) );
ncell->val = v;
ncell->succ = A;
return ncell;
}
void Liste_supprime( Liste* L, Adr A )
{
A->pred->succ = A->succ;
A->succ->pred = A->pred;
}
Elem Liste_valeur( Liste* L, Adr A )
{
return A->val;
}
void Liste_modifie( Liste* L, Adr A, Elem v )
{
A->val = v;
}
// test-Liste.c
#include <stdio.h>
#include "Liste.h"
void affiche( Liste* L )
{
Adr A;
for ( A = Liste_debut( L );
A != Liste_fin( L );
A = Liste_suivant( L, A ) ) {
printf( "%f ", Liste_valeur( L, A ) );
}
printf( "\n" );
}
int main( void )
{
Liste* L = Liste_creer( );
Adr A = Liste_debut( L );
double x = 1.0;
while ( x < 100000.0 )
{
A = Liste_insere( L, A, x );
A = Liste_suivant( L, A );
x = 1.5*x;
}
affiche( L );
Liste_detruire( L );
return 0;
}

On constate que, contrairement à ce que l'on espérerait, la commande affiche(L) n'affiche rien du tout.

  • Utilisez un débogueur (par exemple : ddd), afin de corriger cette implémentation de liste chaînées.
  • Assurez-vous que ces listes libèrent toute la mémoire allouée en utilisant l'outil valgrind de la manière suivante :
valgrind --leak-check=full ./test-Liste

Lorsque vos listes fonctionnent correctement, vous pouvez les adapter afin de les utiliser pour représenter efficacement la grille de jeu de votre Tetris. Chaque noeud de votre liste représentera une ligne du jeu tetris, la première ligne (celle du bas) étant la première cellule utile de votre liste chaînée. Supprimer une ligne se fera donc en supprimant la cellule correspondante et vous devrez rajouter une ligne composée d'espaces en fin de liste. Voilà à quoi ressemble votre liste pour représenter le tetris ci-contre.

||        ||
...
||   l    ||
||  @l$$  ||
|| @@@$$  ||
  • Définissez un type pour représenter chacune des lignes de la grille de jeu.
  • Redéfinissez le type Grille de manière à remplacer le tableau par une liste chaînée dont chaque cellule représente une ligne de la grille.

À ce stade, la compilation de votre programme échouera. C'est normal. Il faut maintenant modifier les procédures qui manipulent la grille de jeu.

  • Afin de pouvoir compiler votre code, commentez le corps des procédures ecrireCase et lireCase. Il peut s'avérer pratique d'ajouter un return bidon (ex : return ' ';) à la procédure lireCase.

Votre programme devrait maintenant compiler à nouveau tout en restant inutilisable.

  • Écrivez une procédure construireGrille qui construit la grille de jeu.
  • Écrivez une procédure détruireGrille qui libère la mémoire occupée par la grille.
  • Modifiez le corps des procédures lireCase et ecrireCase afin de les adapter à la nouvelle structure de données employée pour la grille de jeu. Ces dexu procédures nécessite de parcourir la liste pour arriver à la bonne ligne.

Votre programme devrait maintenant être redevenu fonctionnel.

  • Modifier le corps de la procédure supprimerLigne de manière à ne plus recopier la grille case par case mais plutôt en supprimant l'élément approprié de la liste chaînée et en ajoutant une nouvelle ligne vide en bout de liste.
  • Renommez votre fichier source

    tp2-[votre nom]-liste.c
  • Tout devrait fonctionner.
  • Si vous avez terminé les étapes précédentes et que tout fonctionne bien, vous pouvez améliorer votre code en modifiant les procédures afficherGrille, initialiser, ecrirePiece et hauteurMax afin de remplacer les appels à ecrireCase, lireCase par une utilisation séquentielle de la liste chaînée.

5 - Améliorations optionnelles

#define COLOR(X) printf("\033["X"m")
...
// essayez 40, 41, 42, 43 ... et 0 pour revenir à par défaut.
if (c == '#') { COLOR("41"); printf( " " ); }
  • N'oubliez pas de faire un certain nombre de pièces distinctes, c'est plus sympa pour jouer.
  • Vous pouvez apporter tout autre modification qui rendra votre programme plus semblable au vrai Tetris ;-): toutes les pièces, score (en fonction du nombre de lignes supprimé en même temps). L'aspect temps réél sera abordé dans le TP suivant.

6 - Notes

  • Pour tester plus facilement votre tetris, n'oubliez pas la pièce verticale dans la liste des pièces possibles.
  • Ayez une version fonctionnelle du tetris tableau, avant de faire la version liste. Vous aurez besoin d'une version fonctionnelle pour le tetris graphique.
  • Dans la version liste du Tetris, une erreur commune est d'initialiser chaque ligne par une affectation à la chaîne "____________" (où les '_' sont des espaces). Le problème est que c'est une chaîne constante, donc non modifiable. Il faut donc bien allouer dynamiquement une chaîne de la bonne longueur et la remplir d'espaces.