Si vous développiez un logiciel pour résoudre un cube Rubik, comment le représenteriez-vous?
Ce ACM Paper décrit plusieurs manières différentes qu’il a utilisées pour représenter un cube de rubik et les compare les unes aux autres. Malheureusement, je n'ai pas de compte pour obtenir le texte intégral, mais la description indique:
Sept représentations alternatives du cube de Rubik sont présentées et comparées: un tableau de 3 sur 3 sur 3 d'entiers de 3 chiffres; un tableau de littéraux de 6 sur 3 sur 3; une matrice de 5 par 12 littéraux; une matrice littérale creuse ll-by-ll; un vecteur à 54 éléments; un tableau à 4 dimensions; et un tableau imbriqué 3 x 3 x 3. Les fonctions APL sont données pour les mouvements d’orientation et les quarts de tour, ainsi que plusieurs outils utiles pour résoudre le cube.
De plus, ce fichier RubiksCube.Java contient une représentation assez nette ainsi que le code correspondant à la rotation des sections (si vous recherchez du code réel). Il utilise une cellule et un tableau de faces.
Une façon serait de se concentrer sur l'aspect visuel.
Un cube a six faces et chaque face est un ensemble de carrés de trois sur trois. Alors
Color[][][] rubik = new Color[6][3][3];
Ensuite, chaque mouvement est une méthode qui permute un ensemble spécifique de carrés colorés.
Éviter l'optimisation; faites-le orienté objet. Un aperçu de classe de pseudocode que j'ai utilisé est:
class Square
+ name : string
+ accronym : string
class Row
+ left_square : square
+ center_square : square
+ right_square : square
class Face
+ top_row : list of 3 square
+ center_row : list of 3 square
+ bottom_row : list of 3 square
+ rotate(counter_clockwise : boolean) : nothing
class Cube
+ back_face : face
+ left_face : face
+ top_face : face
+ right_face : face
+ front_face : face
+ bottom_face : face
- rotate_face(cube_face : face, counter_clockwise : boolean) : nothing
La quantité de mémoire utilisée est si petite et le traitement si minime que l'optimisation est totalement inutile, en particulier lorsque vous sacrifiez la convivialité du code.
Le logiciel "Cube Explorer" utilise une méthode intéressante pour représenter le cube. En utilisant beaucoup de maths intelligents, cette méthode peut représenter le cube en utilisant seulement 5 entiers. L'auteur explique les maths derrière son programme sur son website . Selon l'auteur, la représentation est adaptée à la mise en oeuvre de résolveurs rapides.
Il y a plusieurs façons de le faire. Certains moyens utilisent plus efficacement la mémoire que d’autres.
J'ai vu des gens utiliser un tableau 3 x 3 x 3 d'objets cuboïdes, où l'objet cuboïde doit stocker des informations de couleur (et oui, cet objet central n'est jamais utilisé). J'ai vu des gens utiliser 6 tableaux, dont chacun est un tableau 3 x 3 de cuboïdes. J'ai vu un tableau de 3 x 18 cuboids. Il y a beaucoup de possibilités.
Une autre préoccupation majeure est probablement de savoir comment représenter les différentes transformations. La rotation d'une seule face d'un cube physique (tous les déplacements de cube sont essentiellement des rotations d'une seule face) devrait être représentée en permutant de nombreux objets cuboïdes.
Votre choix doit être logique pour l’application que vous écrivez. Il se peut que vous ne fassiez que rendre le cube. Il se peut qu'il n'y ait pas d'interface utilisateur. Vous pouvez résoudre le cube.
Je choisirais le tableau 3 x 18.
Il y a 20 cubes qui comptent. Donc, une façon de le faire est un tableau de 20 chaînes. Les chaînes contiendraient 2 ou 3 caractères indiquant les couleurs. Un seul mouvement affecte 7 des cubes. Donc, vous avez juste besoin d'un remappeur pour chacun des six côtés.
Remarque: cette solution ne parvient pas à retenir l'orientation de l'autocollant du logo qui se trouve sur le centre blanc.
En passant, j’ai aidé quelqu'un à faire un logiciel Rubik's cube une fois, il y a peut-être 15 ans, mais je ne me souviens plus comment nous le représentions.
Vous pouvez imaginer le cube comme trois listes chaînées circulaires verticales, qui croisent trois listes chaînées horizontales.
Chaque fois qu'une certaine rangée du cube est pivotée, il vous suffit de faire pivoter les pointeurs correspondants.
Cela ressemblerait à ceci:
struct cubeLinkedListNode {
cubedLinkedListNode* nextVertical;
cubedLinkedListNode* lastVertical;
cubedLinkedListNode* nextHorizontal;
cubedLinkedListNode* lastHorizontal;
enum color;
}
Vous pourriez ne pas avoir réellement besoin des 2 derniers "pointeurs".
[Je l'ai fait avec C, mais cela pourrait être fait en Java ou en C # en utilisant simplement une classe simple pour cubeLinkedListNode, chaque classe contenant des références à d'autres nœuds. ]
N'oubliez pas qu'il existe six listes de liens circulaires imbriquées. 3 verticales 3 horizontales.
Pour chaque rotation, il vous suffit de parcourir la liste des liens circulaires correspondants en décalant séquentiellement les liens du cercle en rotation, ainsi que les cercles de connexion.
Quelque chose comme ça, au moins ...
Les autres décrivent bien le cube physique, mais en ce qui concerne l'état du cube ... J'essaierais d'utiliser un tableau de transformations vectorielles pour décrire les modifications du cube. De cette façon, vous pouvez conserver l'historique du cube rubiks au fur et à mesure des modifications. Et je me demande si vous pourriez multiplier les vecteurs dans une matrice de transformation pour trouver la solution la plus simple?
Comme une permutation des 48 faces qui peuvent bouger. Les rotations de base sont aussi des permutations, et les permutations peuvent être composées, elles forment un groupe.
Dans un programme, une telle permutation serait représentée par un tableau de 48 éléments contenant les nombres de 0 à 47. Les couleurs correspondant aux nombres étant fixes, une représentation visuelle peut être calculée à partir de la permutation et inversement.
J'ai implémenté un programme Rubik's Cube qui rend le cube en utilisant OpenGL, et il a un solveur intégré. Le solveur doit générer des millions de déplacements et comparer chaque état de cube des millions de fois. La structure sous-jacente doit donc être rapide. J'ai essayé les structures suivantes:
Chaque côté du cube est composé de 9 autocollants, mais le centre est fixe, il ne faut donc en stocker que 8. Et il y a 6 couleurs, de sorte que chaque couleur rentre dans un octet. Étant donné ces définitions de couleurs:
enum class COLOR : uchar {WHITE, GREEN, RED, BLUE, ORANGE, YELLOW};
Un visage pourrait ressembler à ceci, stocké dans un seul entier 64 bits:
00000000 00000001 00000010 00000011 00000100 00000101 00000000 00000001
Qui est décodé comme:
WGR
G B
WYO
L’utilisation de cette structure présente l’avantage de pouvoir utiliser les opérateurs binaires rolq
et rorq
pour déplacer un visage. Le roulement de 16 bits effectue une rotation de 90 degrés; rouler sur 32 bits donne un virage à 180 degrés. Les pièces adjacentes doivent être entretenues manuellement - c.-à-d. Après la rotation de la face supérieure, il faut également déplacer la couche supérieure des faces avant, gauche, arrière et droite. Tourner les visages de cette manière est vraiment rapide. Par exemple, rouler
00000000 00000001 00000010 00000011 00000100 00000101 00000000 00000001
rendements de 16 bits
00000000 00000001 00000000 00000001 00000010 00000011 00000100 00000101
Décodé, cela ressemble à ceci:
WGW
Y G
OBR
Un autre avantage est que la comparaison des états de cube peut dans certains cas être effectuée à l'aide de masques de bits intelligents et de comparaisons d'entiers standard. Cela peut être une très grosse accélération pour un solveur.
Quoi qu'il en soit, mon implémentation est sur github: https://github.com/benbotto/rubiks-cube-cracker/tree/master/Model See RubiksCubeModel.{h,cpp}
.
Comme mentionné, le programme rend également le cube. J'ai utilisé une structure différente pour cette partie. J'ai défini une classe "cubie", qui est un carré avec 1, 2 ou 3 faces colorées pour les pièces de centre, de bord et de coin, respectivement. Le Rubik's Cube est alors composé de 26 cubes. Les faces sont tournées à l'aide de quaternions. Le code pour les cubies et le cube est ici: https://github.com/benbotto/rubiks-cube-cracker/tree/master/Model/WorldObject
Note de côté: mon solveur n'est pas le plus rapide. Cela ressemble à l'algorithme de Thistlethwaite, mais mon objectif était de résoudre le cube de manière humaine, et non de le résoudre en un minimum de déplacements. Ma réponse concerne les structures de données sous-jacentes et non la résolution du cube.