Création de tableaux à deux dimensions en Ruby

Gabriele Cirulli
L'article suivant fait partie d'une série. Pour plus d'articles de cette série, voir Cloner le jeu 2048 en Ruby. Pour le code complet et final, voir l'essentiel.
Maintenant que nous savons comment l'algorithme fonctionnera, il est temps de réfléchir aux données sur lesquelles cet algorithme fonctionnera. Il y a deux choix principaux ici : un appartement déployer d'un certain type, ou un tableau à deux dimensions. Chacun a ses avantages, mais avant de prendre une décision, nous devons tenir compte de quelque chose.
Casse-tête SEC
Une technique courante pour travailler avec des puzzles basés sur une grille où vous devez rechercher des modèles comme celui-ci consiste à écrire une version de l'algorithme qui fonctionne sur le puzzle de gauche à droite, puis de faire pivoter l'ensemble du puzzle environ quatre fois. De cette façon, l'algorithme ne doit être écrit qu'une seule fois et il ne doit fonctionner que de gauche à droite. Cette réduit considérablement la complexité et la taille de la partie la plus difficile de ce projet.
Puisque nous allons travailler sur le puzzle de gauche à droite, il est logique que les lignes soient représentées par des tableaux. Lors de la création d'un tableau à deux dimensions dans Rubis (ou, plus précisément, comment vous voulez qu'il soit adressé et ce que les données signifient réellement), vous devez décider si vous voulez une pile de lignes (où chaque ligne de la grille est représentée par un tableau) ou une pile de colonnes (où chaque colonne est un tableau). Puisque nous travaillons avec des lignes, nous allons choisir des lignes.
Comment ce tableau 2D est tourné, nous y reviendrons après avoir réellement construit un tel tableau.
Construire des tableaux à deux dimensions
La méthode Array.new peut prendre un argument définissant la taille du tableau que vous souhaitez. Par exemple, Tableau.nouveau(5) va créer un tableau de 5 objets nil. Le deuxième argument vous donne une valeur par défaut, donc Tableau.nouveau(5, 0) vous donnera le tableau [0,0,0,0,0] . Alors, comment créez-vous un tableau à deux dimensions?
La mauvaise façon, et la façon dont je vois les gens essayer souvent, c'est de dire Array.new( 4, Array.new(4, 0) ) . En d'autres termes, un tableau de 4 lignes, chaque ligne étant un tableau de 4 zéros. Et cela semble fonctionner au premier abord. Cependant, exécutez le code suivant :
Cela semble simple. Créez un tableau 4x4 de zéros, définissez l'élément en haut à gauche sur 1. Mais imprimez-le et nous obtenons…
Il a mis toute la première colonne à 1, qu'est-ce qui donne? Lorsque nous avons créé les tableaux, l'appel le plus interne à Array.new est appelé en premier, créant une seule ligne. Une seule référence à cette ligne est ensuite dupliquée 4 fois pour remplir le tableau le plus à l'extérieur. Chaque ligne fait alors référence au même tableau. Changez-en un, changez-les tous.
Au lieu de cela, nous devons utiliser le troisième façon de créer un tableau en Ruby. Au lieu de passer une valeur à la méthode Array.new, nous passons un bloc. Le bloc est exécuté chaque fois que la méthode Array.new a besoin d'une nouvelle valeur. Alors si tu disais Array.new(5) { obtient.chomp } , Ruby s'arrêtera et demandera une entrée 5 fois. Il nous suffit donc de créer un nouveau tableau à l'intérieur de ce bloc. Donc on finit par Tableau.nouveau(4) { Tableau.nouveau(4,0) } . Maintenant, essayons à nouveau ce cas de test.
Et il fait exactement ce à quoi vous vous attendiez.
Ainsi, même si Ruby ne prend pas en charge les tableaux à deux dimensions, nous pouvons toujours faire ce dont nous avons besoin. N'oubliez pas que le tableau de niveau supérieur contient références aux sous-tableaux, et chaque sous-tableau doit faire référence à un tableau de valeurs différent.
Ce que ce tableau représente dépend de vous. Dans notre cas, ce tableau est présenté sous forme de lignes. Le premier index est la ligne que nous indexons, de haut en bas. Pour indexer la rangée supérieure du puzzle, nous utilisons un[0] , pour indexer la ligne suivante, nous utilisons un[1] . Pour indexer une tuile spécifique dans la deuxième ligne, nous utilisons un[1][n] . Cependant, si nous avions opté pour des colonnes… ce serait la même chose. Ruby n'a aucune idée de ce que nous faisons avec ces données, et comme il ne prend techniquement pas en charge les tableaux à deux dimensions, ce que nous faisons ici est un hack. N'y accédez que par convention et tout tiendra ensemble. Oubliez ce que les données en dessous sont censées faire et tout peut s'effondrer très rapidement.