Skip to content

Instantly share code, notes, and snippets.

@Berkmann18
Created December 7, 2014 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Berkmann18/cbee913aa2fba14c3c21 to your computer and use it in GitHub Desktop.
Save Berkmann18/cbee913aa2fba14c3c21 to your computer and use it in GitHub Desktop.
My Rubik's Cube Simulator made on Processing for my final ISN exam
/*
Projet final d'ISN 2013-2014
auteur: Berkmann Maximilian
*/
//import java.processing.opengl;
float M[][] = new float[4][4];
//descritif des styles de cubes
color cubeFaceColors[] = {//défaut
//D, B, R, F, L, U
color(255, 255, 0), color(255, 127, 0), color(0, 0, 255), color(255, 0, 0), color(0, 255, 0), color(255)
}
, vCubeFC[] = {//VCube
color(127), color(255, 127, 0), color(0, 0, 255), color(255, 0, 0), color(0, 255, 0), color(0)
}
, girlCFC[] = {//fille
color(255, 255, 0), color(255, 127, 0), color(255, 0, 255), color(255, 16, 120), color(0, 255, 0), color(255)
}
, cF2C[] = {//blanc et noir
color(255), color(255), color(0), color(0), color(255), color(0)
}
, cF3C[] = {//rouge, vert et bleu
color(0, 255, 0), color(255, 0, 0), color(0, 0, 127), color(127, 0, 0), color(0, 0, 255), color(0, 127, 0)
}
, MirrorFC[] = {//couleur argent du cube Mirror
color(210), color(210), color(210), color(210), color(210), color(210)
}
, MinxFC[] = {//cyan, magenta et orange
color(0, 255, 0), color(255, 0, 0), color(0, 255, 255), color(255, 255, 0), color(0, 0, 255), color(255, 0, 255)
}
, OldStyle[] = {//noir, gris et blanc
color(255), color(127), color(0), color(255), color(127), color(0)
}
, WCAClrSch[] = {//Schéma de couleur de la WCA
color(255, 255, 0), color(0, 0, 255), color(255, 0, 0), color(0, 255, 0), color(255, 128, 0), color(255)
}
, TwoFC[] = {//seulement 2 faces
color(255, 255, 0), color(128, 64), color(128, 64), color(128, 64), color(128, 64), color(255)
}
, Visual[] = {//Schéma de couleur de Visual Cube
color(255, 255, 0, 127), color(0, 0, 255, 127), color(255, 0, 0, 127), color(0, 255, 0, 127), color(255, 128, 0, 127), color(255, 127)
};
//axe (0-3), tranche (0-3), direction (1: sens des aiguilles d'une montre; -1: sens inverse des aiguilles d'une montre)
int axis=0, slice=2, direction=1, speed=10, nbOfMoves = 0;//vitesse: défaut=3; max=16;
float theta = 0.0;//angle theta
boolean orient=false;//cube orienté
boolean firstRun = true;//début de l'animation
float xRot=-35.264, yRot=45, zRot=0;//rotation en x, en y et en z
PVector frontBound[], topBound[], rightBound[], downBound[], backBound[], leftBound[];//attachement des faces avec le centres du cube
float backViewDisp;//position de la vue arrière
stack stackOfMoves = new stack();//stack de mouvement
rubikCube rc;//Rubik's cube
String moveStr, algorithm = "", solverStat = "";//chaine de mouvement, algorithme affiché, état du solveur, algorithme pour undo algorithm
boolean moveStrValid = false;//validité du mouvement pour éviter la répétition involontaire de mouvements
int strMoveCount=0;//nombre de mouvements
boolean inMove = false;//indication du cube si c'est en mouvement ou non
int thetaCount = 0, cubeStyleSelected = 0;//comptage de l'angle, style du cube sélectionner
String[] solvingMethod = {
"undo", "Beginner\'s", "Fridrich", "Petrus", "Roux", "EdgeFirst", "CornerFirst", "ZZ", "VH", "M2 3OP"
}
, //méthode de résolution
cubeStyles = {
"classic", //bordure noir, couleur de base (normale)
"VCube", //bordure blanche, noir au lieu de blanc et gris au lieu de jaune
"girlish", //bordure jaune, magenta au lieu de bleu et rose au lieu de rouge
"whiteAndBlack", //cube blanc et noir
"rgb", //rouge, vert et bleu
"mirror", //même couleur que le Mirror Cube (BLD cube)
"minx", //Couleur du Megaminx en plus des couleurs classiques
"OldStyle", //sans couleur
"Heise\'s", //cube de Ryan Heise
"WCA", //cube de la WCA
"2F", //Couleur de 2 faces
"Visual" //transparent
};
char[] undoStr;
color cubiesBorder = color(255);//couleur de la bordure du cube
void setup() {
chCubeStyle(0);//change le style du cube
size(1210, 600, OPENGL);//Taille et moteur de rendu
ortho();//orthogonalité des formes
strokeJoin(ROUND);//jointure ronde
//frame.setTitle("Rubik's Cube Simulator");//titre
rc = new rubikCube(height/8);//création du cube
if (cubeStyleSelected==8) rc.setSize(5); //100 par défaut (séparation des cubies)
rc.cubePosition = new PVector(width/3, height/2.0, 0.0);//position du cube
backViewDisp = width/3;//vue arrière au tier de la fenêtre en longueur
for (int i=0;i<8;i++) {//dessin des faces
rot(rc.frontY, 1, rc.boundary.points[i]);//face avant en Y
rot(rc.frontX, 0, rc.boundary.points[i]);//face avant en X
trans(rc.cubePosition.x, rc.cubePosition.y, rc.cubePosition.z, rc.boundary.points[i]);//coin avant
rot(rc.backY, 1, rc.boundary2.points[i]);//face arrière en Y
rot(rc.backX, 0, rc.boundary2.points[i]);//face arrière en X
trans(rc.cubePosition.x + backViewDisp, rc.cubePosition.y, rc.cubePosition.z, rc.boundary2.points[i]);//coin arrière
}
computeBoundaries();//attachement et frottement des cubies entre eux et entre le centre
//scramble();
}
void draw() {
//if(cubeStyleSelected<cubeStyles.length) cubeStyleSelected++;
//else cubeStyleSelected=0;
background(200);//effacement du fond
if (!inMove && moveStrValid) strOfMoves();//autorisation de l'exécution des mouvements
move(false);//reste sur le premier mouvement
strokeWeight(5.0);//épaisseur des bordures
//rc.drawBound(); bordure fixe
pushMatrix();//matrice géométrique lancer
translate(rc.cubePosition.x, rc.cubePosition.y, rc.cubePosition.z);
pushMatrix();//sous matrice géométrique (cube avant)
rotateX(radians(rc.frontX));//rotation du cube en X
rotateY(radians(rc.frontY));//... en Y
rc.drawRubikCube(theta);//dessine le cube avant (le 1er à gauche)
popMatrix();//fermeture de la sous matrice
translate(backViewDisp, 0, 0);//cube arrière
rotateX(radians(rc.backX));
rotateY(radians(rc.backY));
rc.drawRubikCube(0.0); //dessine le cube arrière (le deuxième à droite)
popMatrix();//ferme la matrice
textSize(20);
text(nbOfMoves+" moves", width/2, height-40);//affiche le nombre de mouvement sur le sketch
text("scramble: "+algorithm, 20, 40);//affiche le mélange
textSize(15);
text(rc.codeString(), width/2-80, height-20);
fill(255, 0, 0);
textSize(24);
if (rc.isSolved()) text("Solved", 60, height/2);//si le cube est résolu alors ça l'indique
fill(0);
textSize(30);
text(min+":"+s+"."+ms, 50, height-50);//affiche le temps
textSize(24);
text("cube style: "+cubeStyles[cubeStyleSelected], width-250, height-45);//indique le style choisi
//slowMove();
}
class cube {//cubie
PVector pos;//position
int id, size;//numéro d'identification, taille
PVector points[];//points de construction
float angX, angY, angZ;//angles
String code;//code
cube(int id, int size) {//constructeur de cube
this.id = id;//assignation du paramètre à l'attribut
pos = new PVector(0, 0, 0);//initialisation vectoriel
points = new PVector[8];//création du tableau vectoriel
for (int i=0;i<8;i++) points[i] = new PVector();//remplissage du tableau cellule par cellule
this.size = size;
for (int i=0;i<8;i++) {//position des points celon les signes de chaque coordonnées
int xSign = (i%2==0)?1:-1;
int ySign = (i/4 == 0)?1:-1;
int zSign = ((i/2)%2 == 0)?1:-1;
points[i].x = xSign*size/2.0;
points[i].y = ySign*size/2.0;
points[i].z = zSign*size/2.0;
}
angX = angY = angZ = 0.0;//remise à zero des angles (angZ=0;angY=angZ;angX=angY;)
code = new String("");//code vide
}
void setPos(PVector p) {//assigne la position
for (int i=0;i<8;i++) trans(p.x, p.y, p.z, points[i]);
}
void findCenter() {//trouve les centres pour les correspondances cubies-centres
PVector temp;//vecteur temporaire
temp = PVector.add(points[0], points[7]);//ajout des points d'extremité du vecteur
temp.div(2.0);//division en 2
}
void drawCube(PVector t) {//dessine le cubie
int idx[] = {//tableau à numéro d'identification de cubie
0, 1, 3, 2, 3, 7, 6, 2, 0, 2, 6, 4, 0, 4, 5, 1, 1, 5, 7, 3, 4, 6, 7, 5
};
for (int i=0;i<8;i++) rot(angY, 1, points[i]);//rotation en Y
for (int i=0;i<8;i++) rot(angX, 0, points[i]);//rotation en X
for (int i=0;i<8;i++) rot(angZ, 2, points[i]);//rotation en Z
angX = angY = angZ = 0.0;
for (int i=0;i<6;i++) {
if ((this.id & (1<<i)) != 0) fill(cubeFaceColors[i]);
else fill(0);
stroke(cubiesBorder);//bordure du cubie
beginShape(QUADS);//déclaration d'une forme de type quadrilatère
for (int j=0;j<4;j++) {
int pt = idx[i*4+j];//chaque point recoive un id
vertex(this.points[pt].x, this.points[pt].y, this.points[pt].z);//créer un vertex à chaque endroits spécifié dans le tableau de points
}
endShape();//fermeture de la déclaration
}
findCenter();//recherche de centre pour assurer une cohérence des faces
}
PVector getCenter() {//retourne le centre
return this.pos;//la position du cubie
}
}
class stack {//stack de donné
char data[];//tableau de donné
int top;//seuil
stack() {//constructeur de stack
//initialisation des attributs
top = -1;
data = new char[999];
for (int i=0; i<data.length; i++) {
data[i] = ' ';
}
}
void push(char v) {//ajout d'un caractère
println("push");
top++;
if (top < 999) data[top] = v;//si le seuil n'est pas au maximum
}
char pop() {//enl����ve un caractère
println("pop top="+top);
char tmp = ' ';
if (top>=0) {
tmp = data[top];
top--;
}
println("pop après top="+top);
return tmp;
}
boolean isEmpty() {//indique si le stack est vide
return (top<0);//si top<0: top=-1; ce qui signifie que top n'a pas était modifier donc pas d'ajout ou de suppression de donné
}
void reset() {//réinitialise le stack
top = -1;
for (int i=0; i<data.length; i++) {
data[i] = ' ';
}
}
}
class rubikCube {//Rubik's cube
cube grid[][][];//grille à cubie
cube boundary, boundary2;//frontière avant et arrière
PVector cubePosition;//vecteur de position du cube
char UP, FRONT, RIGHT, BACK, LEFT, DOWN;//face
int GREEN, BLUE, WHITE, YELLOW, ORANGE, RED, size;//id des couleur, taille
float frontX, frontY, backX, backY;//angle de vue avant/arrière en X/Y
rubikCube(int sz) {//constructeur de Rubik's cube (sz: écartement de cubie)
//attribution des id au couleur
WHITE = 32;
RED = 8;
BLUE = 4;
ORANGE = 2;
GREEN = 16;
YELLOW = 1;
grid = new cube[3][][];//établissement de la grille en X
for (int i=0;i<3;i++) {//.. en Y
grid[i] = new cube[3][];
for (int j=0;j<3;j++) {//.. en Z
grid[i][j] = new cube[3];
}
}
int code=0;//code de correspondance avec les couleurs
//coloration des cubies
for (int x=0;x<3;x++) {
for (int y=0;y<3;y++) {
for (int z=0;z<3;z++) {
if (x==0) code |= GREEN;//si c'est la face gauche (L)
else if (x==2) code |= BLUE;//sinon si c'est la face droite (R)
if (y==0) code |= YELLOW;//si c'est la face du bas (D)
else if (y==2) code |= WHITE;//sinon si c'est la face du haut (U)
if (z==2) code |= RED;//si c'est la face avant (F)
else if (z==0) code |= ORANGE;//sinon si c'est la face arrière (B)
grid[x][y][z] = new cube(code, sz);//remplissage de la grille par des cubies
code = 0;//remise à zero du code
}
}
}
//association face-mouvement
UP = 'U';
DOWN = 'D';
FRONT = 'F';
BACK = 'B';
RIGHT = 'R';
LEFT = 'L';
//mise des valeurs des angles de vue
frontY = -45.0;
frontX = -23.26;
backX = -frontX;
backY = frontY+180;
setSize(sz);//met l'écartement à sz
assignCode();//assignation de code propre à chaque cubie
//création des frontières
boundary = new cube(0, 3*size);
boundary2 = new cube(0, 3*size);
}
void assignCode() {//assignation de code à chaque cubie de la grille
//arrêtes (2 lettres==2 stickers)
grid[1][2][2].code = new String("UF");
grid[2][2][1].code = new String("UR");
grid[1][2][0].code = new String("UB");
grid[0][2][1].code = new String("UL");
grid[1][0][2].code = new String("DF");
grid[2][0][1].code = new String("DR");
grid[1][0][0].code = new String("DB");
grid[0][0][1].code = new String("DL");
grid[2][1][2].code = new String("FR");
grid[0][1][2].code = new String("FL");
grid[2][1][0].code = new String("BR");
grid[0][1][0].code = new String("BL");
//coins
grid[2][2][2].code = new String("UFR");
grid[2][2][0].code = new String("URB");
grid[0][2][0].code = new String("UBL");
grid[0][2][2].code = new String("ULF");
grid[2][0][2].code = new String("DRF");
grid[0][0][2].code = new String("DFL");
grid[0][0][0].code = new String("DLB");
grid[2][0][0].code = new String("DBR");
}
void lTurn() {//rotation en L
grid[0][2][2].code = rotateStr(grid[0][2][2].code, 1);//ULF
grid[0][2][0].code = rotateStr(grid[0][2][0].code, 2);//UBL
grid[0][0][0].code = rotateStr(grid[0][0][0].code, 1);//DLB
grid[0][0][2].code = rotateStr(grid[0][0][2].code, 2);//DFL
}
void rTurn() {//rotation en R
grid[2][2][2].code = rotateStr(grid[2][2][2].code, 2);//UFR
grid[2][2][0].code = rotateStr(grid[2][2][0].code, 1);//URB
grid[2][0][0].code = rotateStr(grid[2][0][0].code, 2);//DBR
grid[2][0][2].code = rotateStr(grid[2][0][2].code, 1);//DRF
}
void fTurn() {//rotation en F
grid[1][2][2].code = flipEdge(grid[1][2][2].code);//UF
grid[1][0][2].code = flipEdge(grid[1][0][2].code);//DF
grid[2][1][2].code = flipEdge(grid[2][1][2].code);//FR
grid[0][1][2].code = flipEdge(grid[0][1][2].code);//FL
grid[2][2][2].code = rotateStr(grid[2][2][2].code, 1);//UFR
grid[0][0][2].code = rotateStr(grid[0][0][2].code, 1);//DFL
grid[2][0][2].code = rotateStr(grid[2][0][2].code, 2);//DRF
grid[0][2][2].code = rotateStr(grid[0][2][2].code, 2);//ULF
}
void bTurn() {//.. en B
grid[1][2][0].code = flipEdge(grid[1][2][0].code);//UB
grid[1][0][0].code = flipEdge(grid[1][0][0].code);//DB
grid[2][1][0].code = flipEdge(grid[2][1][0].code);//BR
grid[0][1][0].code = flipEdge(grid[0][1][0].code);//BL
grid[2][2][0].code = rotateStr(grid[2][2][0].code, 2);//URB
grid[0][2][0].code = rotateStr(grid[0][2][0].code, 1);//UBL
grid[0][0][0].code = rotateStr(grid[0][0][0].code, 2);//DLB
grid[2][0][0].code = rotateStr(grid[2][0][0].code, 1);//DBR
}
String codeString() {//affiche la chaine de code au format singmaster (utiliser dans les fichier .sgm)
return new String(grid[1][2][2].code + " " + grid[2][2][1].code + " " + grid[1][2][0].code + " " + grid[0][2][1].code + " " + grid[1][0][2].code + " " + grid[2][0][1].code
+ " " + grid[1][0][0].code + " " + grid[0][0][1].code + " " + grid[2][1][2].code + " " + grid[0][1][2].code + " " + grid[2][1][0].code + " " + grid[0][1][0].code
+ " " + grid[2][2][2].code + " " + grid[2][2][0].code + " " + grid[0][2][0].code + " " + grid[0][2][2].code + " " + grid[2][0][2].code + " " + grid[0][0][2].code
+ " " + grid[0][0][0].code + " " + grid[2][0][0].code);
}
boolean isSolved() {//si le cube est résolu ça retourne vrai sinon faux
//c'est-à-dire si chaque code des cubies corresponde avec celui à l'état résolu
return grid[1][2][2].code.equals("UF") && grid[2][2][1].code.equals("UR") && grid[1][2][0].code.equals("UB") && grid[0][2][1].code.equals("UL")
&& grid[1][0][2].code.equals("DF") && grid[2][0][1].code.equals("DR") && grid[1][0][0].code.equals("DB") && grid[0][0][1].code.equals("DL")
&& grid[2][1][2].code.equals("FR") && grid[0][1][2].code.equals("FL") && grid[2][1][0].code.equals("BR") && grid[0][1][0].code.equals("BL")
&& grid[2][2][2].code.equals("UFR") && grid[2][2][0].code.equals("URB") && grid[0][2][0].code.equals("UBL") && grid[0][2][2].code.equals("ULF")
&& grid[2][0][2].code.equals("DRF") && grid[0][0][2].code.equals("DFL") && grid[0][0][0].code.equals("DLB") && grid[2][0][0].code.equals("DBR");
}
void setSize(int size) {//écartement cubie-cubie
PVector p;
//décalage en coordonné
int xOff, yOff, zOff;
xOff = -size;
for (int x=0;x<3;x++) {
yOff = size;
for (int y=0;y<3;y++) {
zOff = -size;
for (int z=0;z<3;z++) {
p = new PVector();
p.x = xOff;
p.y = yOff;
p.z = zOff;
grid[x][y][z].setPos(p);//position individuelle
zOff += size;
}
yOff -= size;
}
xOff += size;
}
this.size = size;
}
void drawRubikCube(float th) {//dessine le cube en fonction de la valeur de l'angle theta
for (int x=0;x<3;x++)
for (int y=0;y<3;y++)
for (int z=0;z<3;z++) {
if (axis==0 && x==slice) grid[x][y][z].angX = th;
else if (axis==1 && y==slice) grid[x][y][z].angY = -th;
else if (axis==2 && z==slice) grid[x][y][z].angZ = th;
grid[x][y][z].drawCube(cubePosition);//dessine chaque cubie selon la position du cube
}
}
void updateRotate(int a, int s, int d) {//met à jour les rotations (a: axis (axe); s: slice (tranche), d: direction)
cube temp;//cubie temporaire
//PVector tpos;
if (d == 1) {//sens des aiguilles d'une montre
if (a == 0) {//première axe
//affection en queue (grid[s][0][0]->grid[s][2][0]->grid[s][2][2]->grid[s][0][2]->temp)
temp = grid[s][0][0];//dernier valeur à affecter donc mise dans une variable temporaire pour ne pas la perdre
grid[s][0][0] = grid[s][2][0];
grid[s][2][0] = grid[s][2][2];
grid[s][2][2] = grid[s][0][2];
grid[s][0][2] = temp;
temp = grid[s][1][0];
grid[s][1][0] = grid[s][2][1];
grid[s][2][1] = grid[s][1][2];
grid[s][1][2] = grid[s][0][1];
grid[s][0][1] = temp;
}
else if (a == 1) {//axe centrale
temp = grid[0][s][2];
grid[0][s][2] = grid[2][s][2];
grid[2][s][2] = grid[2][s][0];
grid[2][s][0] = grid[0][s][0];
grid[0][s][0] = temp;
temp = grid[0][s][1];
grid[0][s][1] = grid[1][s][2];
grid[1][s][2] = grid[2][s][1];
grid[2][s][1] = grid[1][s][0];
grid[1][s][0] = temp;
}
else {//dernier axe
temp = grid[0][0][s];
grid[0][0][s] = grid[2][0][s];
grid[2][0][s] = grid[2][2][s];
grid[2][2][s] = grid[0][2][s];
grid[0][2][s] = temp;
temp = grid[1][0][s];
grid[1][0][s] = grid[2][1][s];
grid[2][1][s] = grid[1][2][s];
grid[1][2][s] = grid[0][1][s];
grid[0][1][s] = temp;
}
}
else {
if (a == 0) {
temp = grid[s][0][2];
grid[s][0][2] = grid[s][2][2];
grid[s][2][2] = grid[s][2][0];
grid[s][2][0] = grid[s][0][0];
grid[s][0][0] = temp;
temp = grid[s][0][1];
grid[s][0][1] = grid[s][1][2];
grid[s][1][2] = grid[s][2][1];
grid[s][2][1] = grid[s][1][0];
grid[s][1][0] = temp;
}
else if (a == 1) {
temp = grid[0][s][0];
grid[0][s][0] = grid[2][s][0];
grid[2][s][0] = grid[2][s][2];
grid[2][s][2] = grid[0][s][2];
grid[0][s][2] = temp;
temp = grid[1][s][0];
grid[1][s][0] = grid[2][s][1];
grid[2][s][1] = grid[1][s][2];
grid[1][s][2] = grid[0][s][1];
grid[0][s][1] = temp;
}
else {
temp = grid[0][2][s];
grid[0][2][s] = grid[2][2][s];
grid[2][2][s] = grid[2][0][s];
grid[2][0][s] = grid[0][0][s];
grid[0][0][s] = temp;
temp = grid[0][1][s];
grid[0][1][s] = grid[1][2][s];
grid[1][2][s] = grid[2][1][s];
grid[2][1][s] = grid[1][0][s];
grid[1][0][s] = temp;
}
}
//mise à jour des faces concerné pour éviter de le faire à tout le cube pouvant causé une invalidité du cube
if (a == 2 && s == 2) fTurn();
else if (a == 2 && s == 0) bTurn();
else if (a == 0 && s == 2) rTurn();
else if (a == 0 && s == 0) lTurn();
}
void drawBound() {//dessine les frontières
noFill();//pas de remplissage
stroke(0);//bordure noir
beginShape();//début de forme
for (int i=0;i<4;i++) vertex(leftBound[i].x, leftBound[i].y, leftBound[i].z);//vertex que comporte un carré
endShape(CLOSE);//fermeture
}
void rotateOnU() {//y
char temp = FRONT;//on sauvegarde la face F
FRONT = RIGHT;//on la remplace avec la face R
RIGHT = BACK;//on remplace la face R avec la face B
BACK = LEFT;//on remplace la face B avec la face L
LEFT = temp;//on remplace la face L avec la sauvegarde (de la face F)
frontY -= 90;//on décale l'avant en Y
backY = frontY + 180;//et l'arrière
}
void rotU() {//identique à la fonction précédente mais sans décaler les zones d'actions des mouvements
moveStr = "Ued";//U E' D' sur un vrai
moveStrValid = true;
}
void rotateOnD() {//y'
char temp = FRONT;
FRONT = LEFT;
LEFT = BACK;
BACK = RIGHT;
RIGHT = temp;
frontY += 90;
backY = frontY + 180;
}
void rotD() {
moveStr = "uED";
moveStrValid = true;
}
void rotateOnR() {//x
char temp = FRONT;
FRONT = DOWN;
DOWN = BACK;
BACK = UP;
UP = temp;
frontY -= 90;
backY = frontY + 180;
}
void rotR() {
moveStr = "Rml";
moveStrValid = true;
}
void rotateOnL() {//x'
char temp = FRONT;
FRONT = UP;
UP = BACK;
BACK = DOWN;
DOWN = temp;
frontY -= 90;
backY = frontY + 180;
}
void rotL() {
moveStr = "rML";
moveStrValid = true;
}
void rotateDown() {//Tourne dans l'axe des ordonnées (en Y) de FR quand 2*rotateDown() = z x' z
char temp = FRONT;
FRONT = UP;
UP = BACK;
BACK = DOWN;
DOWN = temp;
frontX -= 90;
backX = -frontX;
}
void rotateUp() {//idem que rotateDown() mais dans le sens inverse soit 2*rotateUp() = z' x z'
char temp = FRONT;
FRONT = DOWN;
DOWN = BACK;
BACK = UP;
UP = temp;
frontX += 90;
backX = -frontX;
}
void rotateOnF() {//z
char temp = LEFT;
LEFT = DOWN;
DOWN = RIGHT;
RIGHT = UP;
UP = temp;
frontX += 180;
backY = frontY + 180;
}
void rotF() {
moveStr = "FSb";
moveStrValid = true;
}
void rotB() {
moveStr = "fsB";
moveStrValid = true;
}
String flipEdge(String c) {//retourne une arrête (ex: flipEdge("FU")="UF")
char temp[] = {//sauvegarde les tout les caractère
c.charAt(1), c.charAt(0)//le deuxi��me, le premier
};
return new String(temp);//retourne le résultat de l'inversion
}
String rotateStr(String c, int b) {//retourne une chaine de caractère (décale les caractère de b rang (cf. principe de cryptographie Vegenère))
char temp[] = {
't', 't', 't'//remplissage pour éviter l'erreur: création d'un tableau vide qu'on essais de remplir tout en une fois (sans passer par le remplissage cellule par cellule)
};
for (int i=0;i<3;i++) {//interchange les cellules celon la valeur de décalage b
temp[(i+b)%3] = c.charAt(i);//tout en ayant bien les caractères de c et non pas 't' de temp[]
}
return new String(temp);
}
}
void rot(float th, int ax, PVector v) {//th: angle theta, ax: axe; v: vecteur;
for (int i=0;i<4;i++) {
for (int j=0;j<4;j++) {
if (i==j) M[i][j] = 1.0;
else M[i][j] = 0.0;
}
}
float cosTh = cos(radians(th));//cosinus(theta)
float sinTh = sin(radians(th));//sinus(theta)
if (ax==0) {
M[1][1] = M[2][2] = cosTh;
M[1][2] = -sinTh;
M[2][1] = sinTh;
}
else if (ax==1) {
M[0][0] = M[2][2] = cosTh;
M[0][2] = sinTh;
M[2][0] = -sinTh;
}
else if (ax==2) {
M[1][1] = M[0][0] = cosTh;
M[0][1] = -sinTh;
M[1][0] = sinTh;
}
float tp[] = {//coordonné du vecteur
v.x, v.y, v.z, 1.0
};
float res[] = {//tableau de résultat
0, 0, 0, 0
};
for (int i=0;i<4;i++) {
for (int k=0;k<4;k++) {
res[i] += M[i][k] * tp[k];
}
}
//mise à jour sur les coordonné du vecteur v
v.x = res[0];
v.y = res[1];
v.z = res[2];
}
void trans(float dx, float dy, float dz, PVector v) {//équivaut à translate()
v.x += dx;//direction en abscisse
v.y += dy;//direction en ordonné
v.z += dz;//direction en profondeur/côte
}
void scal(float sx, float sy, float sz, PVector v) {//redimension équivaut à scale()
v.x *= sx;
v.y *= sy;
v.z *= sz;
}
void move(boolean newCall) {//en mouvement ou pas
if (newCall) {//si vraie
if (inMove || thetaCount != 0) return;//si en mouvement et que l'angle n'est pas nulle, ça retourne rien vue que cette fonction est du type void
if (!inMove && newCall) {//si ce n'est pas en mouvement et qu'il ya eu une nouvelle appel
inMove = true;//on change son statut en "en mouvement"
thetaCount = 0;//l'angle theta revien à zero
}
}
if (inMove) {//si en mouvement
if (thetaCount > 90/speed) {//si le compte d'angle dépasse l'angle droit divisé par la vitesse
rc.updateRotate(axis, slice, direction);//mise à jour des rotations
inMove = false;
theta = 0;
thetaCount = 0;
return;
}
theta = direction*speed;//pour suivre les mouvements dans l'espace
thetaCount++;//en conséquence le comptage de theta aussi
}
}
void fastMove() {//mouvement rapide
if (!inMove) {//mise à jour à l'état de repos
rc.drawRubikCube(90.0*direction);//dessine le cube
rc.updateRotate(axis, slice, direction);//met à jour les rotations
}
}
void slowMove() {//mouvement lent
if (!inMove) {
rc.drawRubikCube(direction);
rc.updateRotate(axis, slice, direction);
}
}
void scramble() {//mélangeur
String j[] = {//tableau à mélanges spécifique et aléatoire
new String("FBUDlrfbudLR"), new String("LRFBUDlr"), new String("FBUDlr"), new String("FFBBRRLLudlrbf"), new String(randAlgo(8, 50)), new String(randAlgo2(4, 25)), new String(randomAlgo(50)), new String(randomAlgo2(25)),
new String(extremAlgo()), new String(extremAlgo2()), new String("FFyFyFyF"), new String("URRFBRBBRUULBBRudRRFrLBBUUFF"), //superflip
new String("FRUFRUFRU"), new String("MMEESS")/*damier*/, new String("drDRdrDRdrDR"), new String("FRUruRUruRUruf"), new String("FSRUruRUruRUrufs"), new String("MUMUMUMUMUMUMU"), new String("FrFrFrFrFrFrFrFrFr"), new String("MESMESMESMES")
};
moveStr = j[int(random(100.0))%j.length];//sélection aléatoire
//moveStr = j[11];//sélection du 11e mélange
algorithm = moveStr;//le mélange est affecter à l'algorithme affich����
moveStrValid = true;//validité
if (rc.isSolved()) text("Solved", 60, height/2);//au cas où ça ne s'affiche pas lors que le cube est résolu
}
String randAlgo(int min, int max) {//extension de randomAlgo() avec la possibilité de générer un algorithme aléatoire avec un nombre aléatoire
int r=(int) random(min, max);//r est un nombre aléatoire compris entre min et max
return randomAlgo(r);//génere l'algorithme aléatoire à r mouvement
}
void WCAScramble() {//mélange valide de la WCA
String randomState="";//état aléatoire (algorithme du mélange)
String[] allmoves = {
"F", "f", "FF", "L", "l", "LL", "R", "r", "RR", "U", "u", "UU", "D", "d", "DD", "B", "b", "BB"
}; //liste des mouvement autorisés (18 mouvements)
for (int i=0; i<20; i++) {//génération d'un algorithme à 20 mouvements mais cela varie car les mouvements à 180° (2x la même lettre) sont comptées comme 2
randomState += allmoves[int(random(allmoves.length))]+" ";//ajoute un mouvement de liste aléatoirement seulon le numéro de la cellule compris entre 0 et allmoves.length (longueur du tableau allmoves = 18)
}
moveStr = join(split(randomState, " "), "");//affiche le mélange sans espace pour éviter les ambiguïté de l'IA (exemple: prendre les espaces pour des répétition de mouvements, de pause, d'arrêt, ...)
moveStrValid = true;
if (rc.isSolved()) text("Solved", 60, height/2);
}
String randomAlgo(int moves) {//fonction qui retourne un algorithme avec moves mouvements (mouvement simple: pas de mouvements à 180°, pas de double mouvement)
//regroupement de tout movement lorsque le rubik's cube est fixe et notamment les mouvements
String[] allmoves = {
"F", "f", "L", "l", "R", "r", "U", "u", "D", "d", "B", "b", "S", "s", "M", "m", "E", "e", "X", "x", "Y", "y", "Z", "z"
}; //24 mouvements (mouvements simples/de tranches et rotations)
String algo = "";//création de la variable algo contenant l'algorithme
for (int i=0; i<moves; i++) algo += allmoves[int(random(allmoves.length))];//ajout de mouvements choisi aléatoirement en fonction du nombres de mouvements
return algo;//retourne l'algorithme
}
String randAlgo2(int min, int max) {
int r=(int) random(min, max);
return randomAlgo2(r);
}
String randomAlgo2(int moves) {//identique à randomAlgo() mais avec tout les mouvements à une couche (donc pas des mouvements du genre: Rm)
String[] allmoves = {
"F", "f", "FF", "L", "l", "LL", "R", "r", "RR", "U", "u", "UU", "D", "d", "DD", "B", "b", "BB", "S", "s", "SS", "M", "m", "MM", "E", "e", "EE", "X", "x", "XX", "Y", "y", "YY", "Z", "z", "ZZ"
}; //27
String algo = "";//création de la variable algo contenant l'algorithme
for (int i=0; i<moves; i++) {//ajout de mouvements choisi aléatoirement en fonction du nombres de mouvements
algo += allmoves[int(random(allmoves.length))]+" ";
}
return algo;//retourne l'algorithme
}
String extremAlgo() {//génération à l'extreme
return randomAlgo(100);
}
String extremAlgo2() {//moins extrême que la précédente car il y plus de mouvements possible
return randomAlgo2(80);
}
void solve(String method) {//méthode à utiliser pour résoudre le cube
solverStat = "inspection ...";
println(solverStat);
if (!rc.isSolved()) {//si le cube n'est pas résolu
println("code: "+rc.codeString());//affiche le code du cube
if (method.toLowerCase().equals("undo") || method.toLowerCase().equals(solvingMethod[0])) {//méthode du robot
println("lancement de la méthode undo, top="+stackOfMoves.top);
while (stackOfMoves.top>=0) {//tant que le cube n'est pas résolue l'IA undo les mouvements
undo();
}
}
else if (method.toLowerCase().equals("beginner") || method.toLowerCase().equals("lbl") || method.toLowerCase().equals(solvingMethod[1])) {//méthode des débutant
//Scanne du cube pour trouver les cubies
solverStat = "scan pour trouver UF, UR, UB et UL";
int[] UFpos = getCubie("UF"), URpos = getCubie("UR"), UBpos = getCubie("UB"), ULpos = getCubie("UL");//positions des 4 premières arrêtes ciblé par la 1er étape de cette méthode
if (UFpos[0]>=3 || UFpos[0]<0) UFpos = getCubie("FU");//si le cubie n'est pas dans/sur le cube
if (URpos[0]>=3 || URpos[0]<0) URpos = getCubie("RU");
if (UBpos[0]>=3 || UBpos[0]<0) UBpos = getCubie("BU");
if (ULpos[0]>=3 || ULpos[0]<0) ULpos = getCubie("LU");
//indique les positions des cubies désirer
solverStat = "recherche d'un solution ...\nUF("+UFpos[0]+", "+UFpos[1]+", "+UFpos[2]+"), UR("+URpos[0]+", "+URpos[1]+", "+URpos[2]+"), UB("+UBpos[0]+", "+UBpos[1]+", "+UBpos[2]+"), UL("+ULpos[0]+", "+ULpos[1]+", "+ULpos[2]+")";
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
println(solverStat);//affiche l'état du solveur
solverStat = "1ère étape: la croix";
//UF
//whoIs("UF");//à quel place se trouve le cubie UF
if (!cubieInPlace2(UFpos, "UF") || !cubieInPlace("UF")) {//si le cubie n'est pas à sa place
solverStat = "place UF";
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
println(solverStat);
if (cubieIsOriented("UF")) {//bonne arrête
//recherche de l'emplacement du cubie (cf. fiche)
solverStat = "UF est orienter";
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
println(solverStat);
if (UFpos[0]==1 && UFpos[1]==0 && UFpos[2]==2) moveStr = "FF";//MDDm si les coins (UFR et ULF) sont résolu
else if (UFpos[0]==0 && UFpos[1]==1 && UFpos[2]==2) moveStr = "Ulu";
else if (UFpos[0]==2 && UFpos[1]==1 && UFpos[2]==2) moveStr = "uRU";
else if (UFpos[0]==0 && UFpos[1]==0 && UFpos[2]==1) moveStr = "DFF";
else if (UFpos[0]==2 && UFpos[1]==0 && UFpos[2]==1) moveStr = "dFF";
else if (UFpos[0]==1 && UFpos[1]==0 && UFpos[2]==0) moveStr = "DDFF";
else if (UFpos[0]==0 && UFpos[1]==2 && UFpos[2]==1) moveStr = "u";
else solverStat = "je peux pas trouver UF";
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
println(solverStat);
algorithm=moveStr;
moveStrValid=true;
}
else {//mauvaise arrête (mal orienté)
if (cubieInPlace2(UFpos, "FU") || cubieInPlace("FU")) solverStat = "UF in place but misoriented";
else solverStat = "UF have a problem";
solverStat = "UF isn't oriented";
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
println(solverStat);
if (UFpos[0]==1 && UFpos[1]==0 && UFpos[2]==2) moveStr = "DMdm";//dMDm (mirroir)
else if (UFpos[0]==0 && UFpos[1]==1 && UFpos[2]==2) moveStr = "F";
else if (UFpos[0]==2 && UFpos[1]==1 && UFpos[2]==2) moveStr = "f";
else if (UFpos[0]==0 && UFpos[1]==0 && UFpos[2]==1) moveStr = "lFl";
else if (UFpos[0]==2 && UFpos[1]==0 && UFpos[2]==1) moveStr = "Rfr";
else if (UFpos[0]==1 && UFpos[1]==2 && UFpos[2]==2) moveStr = "FuRU";//fUlu (mirroir) ou FFDMdm ou ffdMDm
else if (UFpos[0]==0 && UFpos[1]==2 && UFpos[2]==1) moveStr = "u";
else solverStat = "I can't find FU";
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
println(solverStat);
algorithm=moveStr;
moveStrValid=true;
}
}//else rc.rotU();//Y pour passer au cubie suivant
algorithm = moveStr;
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
println(solverStat);
/*//UR
if(!cubieInPlace("UR")){
solverStat = "placing UR";
if(cubieIsOriented("UR")){//bonne arrêtes
if(URpos[0]==1 && URpos[1]==0 && URpos[2]==2) moveStr = "FF";
else if(URpos[0]==0 && URpos[1]==1 && URpos[2]==2) moveStr = "F";
else if(URpos[0]==2 && URpos[1]==1 && URpos[2]==2) moveStr = "f";
else if(URpos[0]==0 && URpos[1]==0 && URpos[2]==1) moveStr = "DFF";
else if(URpos[0]==2 && URpos[1]==0 && URpos[2]==1) moveStr = "dFF";
else solverStat = "I can't find UR";
moveStrValid=true;
}else{//mauvaise arrête
if(URpos[0]==1 && URpos[1]==0 && URpos[2]==2) moveStr = "DMdm";
else if(URpos[0]==0 && URpos[1]==1 && URpos[2]==2) moveStr = "Ulu";
else if(URpos[0]==2 && URpos[1]==1 && URpos[2]==2) moveStr = "uRU";
else if(URpos[0]==0 && URpos[1]==0 && URpos[2]==1) moveStr = "lFl";
else if(URpos[0]==2 && URpos[1]==0 && URpos[2]==1) moveStr = "Rfr";
else if(URpos[0]==1 && URpos[1]==2 && URpos[2]==2) moveStr = "FuRU";
else solverStat = "I can't find RU";
moveStrValid=true;
}
}else rc.rotU();
algorithm = moveStr;moveStrValid = true;
if(!cubieInPlace("UB")){
solverStat = "placing UB";
if(cubieIsOriented("UB")){
if(UBpos[0]==1 && UBpos[1]==0 && UBpos[2]==2) moveStr = "FF";
else if(UBpos[0]==0 && UBpos[1]==1 && UBpos[2]==2) moveStr = "F";
else if(UBpos[0]==2 && UBpos[1]==1 && UBpos[2]==2) moveStr = "f";
else if(UBpos[0]==0 && UBpos[1]==0 && UBpos[2]==1) moveStr = "DFF";
else if(UBpos[0]==2 && UBpos[1]==0 && UBpos[2]==1) moveStr = "dFF";
else solverStat = "I can't find UB";
moveStrValid=true;
}else{
if(UBpos[0]==1 && UBpos[1]==0 && UBpos[2]==2) moveStr = "DMdm";
else if(UBpos[0]==0 && UBpos[1]==1 && UBpos[2]==2) moveStr = "Ulu";
else if(UBpos[0]==2 && UBpos[1]==1 && UBpos[2]==2) moveStr = "UBU";
else if(UBpos[0]==0 && UBpos[1]==0 && UBpos[2]==1) moveStr = "lFl";
else if(UBpos[0]==2 && UBpos[1]==0 && UBpos[2]==1) moveStr = "Rfr";
else if(UBpos[0]==1 && UBpos[1]==2 && UBpos[2]==2) moveStr = "FUBU";
else solverStat = "I can't find BU";
moveStrValid=true;
}
}else rc.rotU();
algorithm = moveStr;moveStrValid = true;
if(!cubieInPlace("UL")){
solverStat = "placing UL";
if(cubieIsOriented("UL")){
if(ULpos[0]==1 && ULpos[1]==0 && ULpos[2]==2) moveStr = "FF";
else if(ULpos[0]==0 && ULpos[1]==1 && ULpos[2]==2) moveStr = "F";
else if(ULpos[0]==2 && ULpos[1]==1 && ULpos[2]==2) moveStr = "f";
else if(ULpos[0]==0 && ULpos[1]==0 && ULpos[2]==1) moveStr = "DFF";
else if(ULpos[0]==2 && ULpos[1]==0 && ULpos[2]==1) moveStr = "dFF";
else solverStat = "I can't find UL";
moveStrValid=true;
}else{
if(ULpos[0]==1 && ULpos[1]==0 && ULpos[2]==2) moveStr = "DMdm";
else if(ULpos[0]==0 && ULpos[1]==1 && ULpos[2]==2) moveStr = "Ulu";
else if(ULpos[0]==2 && ULpos[1]==1 && ULpos[2]==2) moveStr = "ULU";
else if(ULpos[0]==0 && ULpos[1]==0 && ULpos[2]==1) moveStr = "lFl";
else if(ULpos[0]==2 && ULpos[1]==0 && ULpos[2]==1) moveStr = "Rfr";
else if(ULpos[0]==1 && ULpos[1]==2 && ULpos[2]==2) moveStr = "FULU";
else solverStat = "I can't find LU";
moveStrValid=true;
}
}else rc.rotU();
solverStat = "Cross finished";
solverStat = "scanning to find UFR, URB, UBL and ULF";
int[] UFRpos = getCubie("UFR"), URBpos = getCubie("URB"), UBLpos = getCubie("UBL"), ULFpos = getCubie("ULF");//positions des 4 premières coins ciblé par la 2������������������me étape de cette méthode*/
}
else if (method.toLowerCase().equals("fridrich") || method.toLowerCase().equals("cfop") || method.toLowerCase().equals(solvingMethod[2])) {//méthode utiliser par des speedcubers
}
else if (method.toLowerCase().equals("petrus") || method.toLowerCase().equals(solvingMethod[3])) {//méthode utiliser par des speedcubers
}
else if (method.toLowerCase().equals("roux") || method.toLowerCase().equals(solvingMethod[4])) {//...
}
else if (method.toLowerCase().equals("edgefirst") || method.toLowerCase().equals(solvingMethod[5])) {
}
else if (method.toLowerCase().equals("cornerfirst") || method.toLowerCase().equals(solvingMethod[6])) {
}
else if (method.toLowerCase().equals("zz") || method.toLowerCase().equals(solvingMethod[7])) {
}
else if (method.toLowerCase().equals("vh") || method.toLowerCase().equals(solvingMethod[8])) {
}
else if (method.toLowerCase().equals("m2 3op") || method.toLowerCase().equals(solvingMethod[9])) {//méthode utiliser par des speedcubers à l'aveugle
}
else {
println("La méthode choisie pour résoudre le cube n'est pas disponible pour l'instant.\nLes méthodes disponible sont: "+solvingMethod);//indique que la méthode choisi n'est pas disponible et celles disponibles
}
}
else {//si le cube est déjà résolu, ça l'annonce si ce n'était pas le cas et de modifie pas le cube
solverStat = "cube solved";
fill(255, 0, 0);
textSize(24);
text("Solved", 60, height/2);
save("data/cubeStyle-"+cubeStyleSelected+".png");//pour garder un aperçu du style en cours
}
println(solverStat);//affiche l'état du solveur
//frame.setTitle("Rubik's Cube Simulator - Solveur: "+solverStat);
}/*
String[][][] getCubiePos(int[] fpos, String code){
String[][][] gpos = {{{code}}};
gpos[fpos[0]][fpos[1]][fpos[2]] = code;
return gpos;
}*/
void whoIs(String ccode) {//qui est le cubie
int[] gC = getCubie(ccode);
if (cubieInPlace(ccode)) println(ccode+" is in is place");//si le cubie est lui-même (s'il est à sa place)
else println(ccode+" is in the place of "+rc.grid[gC[0]][gC[1]][gC[2]].code);//sinon on indique quelle cubie appartient à la place où se trouve ce cubie
}/*
String[][][] findCubie(String ccode){
int[] pos = {0};
String[][][] cpos;
for(int x=0; x<3; x++){
for(int y=0; y<3; y++){
for(int z=0; z<3; z++){
//int[][][] pos = {x{y{z}}};
if(rc.grid[x][y][z].code.equals(ccode)){pos[0] = x; pos[1] = y; pos[2] = z;}
}
}
}
cpos = getCubiePos(pos, ccode);
return cpos;
}*/
int[] getCubie(String code) {//scanne pour trouver le cubie ayant le code
println("getting cubie "+code+" ...");//debug+indication de l'action effectué par l'IA
//recherche individuelle des coordonnées du cubie pour éviter de passer par 3 fonctions (celle-ci y compris, dont les deux autre servent à retourner 3 résultat ce qui est complexe à faire) pour savoir où se trouve le cubie (défaut des autres fonctions en commentaire)
int x=findCubieX(code), y=findCubieY(code), z=findCubieZ(code);
if (x>=3||x<0||y>=3||y<0||z>=3||z<0) {//si le cubie est inexistant ou hors du cube
println("Ho the cubie has a problem !");//indique qu'il y a problème que l'IA vient de rencontrer
if (code.length()==2) code=rc.flipEdge(code);//si le cubie en question est une arrête
else code=rc.rotateStr(code, 1);//sinon ce cubie est un coin
println("new code: "+code);//indique le nouveau code pour assurer un bon déroulement de cette arrangement au yeux de l'IA
x=findCubieX(code);
y=findCubieY(code);
z=findCubieZ(code);//on recherche les coordonné qui étaient jugées mauvaises
if (code.length()==3) getCubie(code);//vérification au cas où le code du coin est toujour pas trouvable (mauvais) car un coin à 3 code possible (celon son orientation) exemple: UFR/FRU/RUF;
}
int[] p= {
x, y, z
};//on case toute les coordonnés (donné tri-dimentionnel du cubie) dans une variable pour faciliter le retour et l'exploitation du résultat
println("cubie "+code+" finded");//indique que la recherche du cubie en question à bien été fait
return p;
}
boolean cubieInPlace(String ccode) {//Si le cubie à besoin d'être permuter ou non (si c'est en place)
int[] p=getCubie(ccode);//on récupère les coordonné du cubie
if (rc.grid[p[0]][p[1]][p[2]].code.equals(ccode)) return true;//s'il est à sa place alors ça retourne vrai
else if (ccode.length()==2 && rc.flipEdge(rc.grid[p[0]][p[1]][p[2]].code).equals(ccode)) return true;
else if (ccode.length()==3 && rc.rotateStr(rc.grid[p[0]][p[1]][p[2]].code, 1).equals(ccode)) return true;
else if (ccode.length()==3 && rc.rotateStr(rc.grid[p[0]][p[1]][p[2]].code, 2).equals(ccode)) return true;
else return false;//sinon faux
}
boolean cubieInPlace2(int[] p, String c) {//Si le cubie à besoin d'être permuter ou non (si c'est en place)
if (rc.grid[p[0]][p[1]][p[2]].code.equals(c)) return true;//s'il est à sa place alors ��a retourne vrai
else if (c.length()==2 && rc.flipEdge(rc.grid[p[0]][p[1]][p[2]].code).equals(c)) return true;
else if (c.length()==3 && rc.rotateStr(rc.grid[p[0]][p[1]][p[2]].code, 1).equals(c)) return true;
else if (c.length()==3 && rc.rotateStr(rc.grid[p[0]][p[1]][p[2]].code, 2).equals(c)) return true;
else return false;//sinon faux
}
boolean cubieIsOriented(String ccode) {//Si le cubie à besoin d'être orienter ou non (si les stickers sont bien placés)
int[] p=getCubie(ccode);
if (rc.grid[p[0]][p[1]][p[2]].code.equals(ccode)) return true;
else {
if (ccode.length()==2) ccode=rc.flipEdge(ccode);
else ccode=rc.rotateStr(ccode, 1);
if (rc.grid[p[0]][p[1]][p[2]].code.equals(ccode)) return false;
else if (ccode.length()==3) {//au cas où ce n'est toujours pas bon
ccode=rc.rotateStr(ccode, 1);
if (rc.grid[p[0]][p[1]][p[2]].code.equals(ccode)) return false;
}
return false;
}
}
int findCubieX(String code) {//cherche la coordonné X du cubie voulu
int x=0;
//navigue à traver le cube au niveau des 3 dimensions pour une suretté maximale
for (x=0; x<3; x++) {
for (int y=0; y<3; y++) {
for (int z=0; z<3; z++) {
if (rc.grid[x][y][z].code.equals(code)) return x;
}
}
}
return x;
}
int findCubieY(String code) {//cherche la coordonné Y du cubie voulu
int y=0;
for (int x=0; x<3; x++) {
for (y=0; y<3; y++) {
for (int z=0; z<3; z++) {
if (rc.grid[x][y][z].code.equals(code)) return y;
}
}
}
return y;
}
int findCubieZ(String code) {//cherche la coordonné Z du cubie voulu
int z=0;
for (int x=0; x<3; x++) {
for (int y=0; y<3; y++) {
for (z=0; z<3; z++) {
if (rc.grid[x][y][z].code.equals(code)) return z;
}
}
}
return z;
}
void reset() {//réinitialise le cube
algorithm = "";
background(200);//efface le sketch (la fenêtre du programme)
rc = new rubikCube(height/8);//construit à nouveau le cube
//et tout ce qu'il en suit
rc.cubePosition = new PVector(width/3, height/2.0, 0.0);
backViewDisp = width/3;
for (int i=0;i<8;i++) {
rot(rc.frontY, 1, rc.boundary.points[i]);
rot(rc.frontX, 0, rc.boundary.points[i]);
trans(rc.cubePosition.x, rc.cubePosition.y, rc.cubePosition.z, rc.boundary.points[i]);
rot(rc.backY, 1, rc.boundary2.points[i]);
rot(rc.backX, 0, rc.boundary2.points[i]);
trans(rc.cubePosition.x + backViewDisp, rc.cubePosition.y, rc.cubePosition.z, rc.boundary2.points[i]);
}
nbOfMoves = 0;//pas de triche (passive ou par undo /!\ pas la méthode) et nécessaire pour les résolutions en FM (Fewest Moves: le moins de mouvements possible) c'est-à-dire pour ne pas pénaliser l'utilisateur/l'IA avec en ayant mis plus de mouvements qu'il n'en a fallut
}
void keyPressed() {//au moment où une touche est appuyée
println(key+":"+keyCode);//affiche la touche et son code pour les débuggage et pour vérifier l'identité d'une touche
if (keyCode == 70 || keyCode == 86 || keyCode == 82 || keyCode == 84 || keyCode == 85 || keyCode == 73//touche "mouvement" car si la validité se fait pour toute touches,
|| keyCode == 66 || keyCode == 78 || keyCode == 76 || keyCode == 77 || keyCode == 68 || keyCode == 67//s'il n'y a pas d'exécution d'un quelquonque mouvement le mouvement précedemment exécuter est répéter
|| keyCode == 65 || keyCode == 49 || keyCode == 81 || keyCode == 50 || keyCode == 69 || keyCode == 51
|| keyCode == 79 || keyCode == 80 || keyCode == 72 || keyCode == 74 || keyCode == 75 || keyCode == 44 || keyCode == 53 || keyCode == 54) {
if (!rc.isSolved() && min==0 && s==0 && ms==0) startTimer();//si le cube est mélanger et que le temps n'est pas en cours (d'évolution) le chrono s'active
//liste de mouvement aisni que les touches dédié à un mouvement spécifique
if (key == 'F' || keyCode == 70) moveStr = "F";//F
else if (key == 'V' || keyCode == 86) moveStr = "f";//F'
else if (key == 'R' || keyCode == 82) moveStr = "R";//R
else if (key == 'T' || keyCode == 84) moveStr = "r";//R'
else if (key == 'U' || keyCode == 85) moveStr = "U";//U
else if (key == 'I' || keyCode == 73) moveStr = "u";//U'
else if (key == 'B' || keyCode == 66) moveStr = "B";//B
else if (key == 'N' || keyCode == 78) moveStr = "b";//B'
else if (key == 'L' || keyCode == 76) moveStr = "L";//L
else if (key == 'M' || keyCode == 77) moveStr = "l";//L'
else if (key == 'D' || keyCode == 68) moveStr = "D";//D
else if (key == 'C' || keyCode == 67) moveStr = "d";//D'
else if (key == 'A' || keyCode == 65) moveStr = "M";//M
else if (key == '1' || keyCode == 49 || keyCode == '&') moveStr = "m";//M'
else if (key == 'Q' || keyCode == 81) moveStr = "S";//S
else if (key == '2' || keyCode == 50) moveStr = "s";//S'
else if (key == 'E' || keyCode == 69) moveStr = "E";//E
else if (key == '3' || keyCode == 51) moveStr = "e";//E'
else if (key == 'O' || keyCode == 79) moveStr = "LM";//l
else if (key == 'P' || keyCode == 80) moveStr = "lm";//l'
else if (key == 'H' || keyCode == 72) moveStr = "Rm";//r
else if (key == 'J' || keyCode == 74) moveStr = "rM";//r'
else if (key == 'K' || keyCode == 75) moveStr = "Ue";//u
else if (/*key == '?' || key == ',' || */keyCode == 44) moveStr = "uE";//u'
else if ( key == '5' /*|| key == '(' */|| keyCode == 53) moveStr = "FS";//f
else if ( key == '6' /*|| key == '-' */|| keyCode == 54) moveStr = "fs";//f'
/*
if (nbOfMoves>0 && algorithm != "" && inMove) println("moves: "+nbOfMoves);//dans la console
if (inMove) println("scramble: "+algorithm);//affiche le mélange dans la console*/
moveStrValid = true;
algorithm += moveStr;//bien sur en conserve une trace écrite des mouvements fait
println("moveStr="+moveStr);
stackOfMoves.push(moveStr.charAt(0));
println("moveStr="+moveStr+" après");
println("pile="+stackOfMoves.data[0]+stackOfMoves.data[1]+" top="+stackOfMoves.top);
}
if (key==ENTER || keyCode == 10) solve("beginner");//activation manuelle du solveur
else if (key=='S' || keyCode == 83) {
if (cubeStyleSelected==9 || cubeStyleSelected==11) WCAScramble();
else scramble();
}//lancement du mélangeur et si le cube est le n°10 ou 12 alors le mélange est de catégorie WCA
else if (/*key == CONTROL || */keyCode == 17) reset();//retour à l'état résolu
else if (key == 'X' || keyCode == 88) rc.rotR();//x
else if (key == 'W' || keyCode == 87) rc.rotL();//x'
else if (key == 'Y' || keyCode == 89) rc.rotU();//y
else if (key == 'G' || keyCode == 71) rc.rotD();//y'
else if (key == 'Z' || keyCode == 90) rc.rotateUp();//z
else if (key == '4' || keyCode == 52) rc.rotateDown();//z'
else if (key == '0' || keyCode == 48) println("Code du cube: "+rc.codeString());//indique le code du cube au format Singmaster
else if (key == BACKSPACE || keyCode == BACKSPACE || keyCode == 8) undo();//undo 1 seul mouvement
else if (key == UP || keyCode == 38 && keyCode != 49) chCubeStyle(1);//va plus haut dans la table à style
else if (key == DOWN || keyCode == 40 && keyCode != 53) chCubeStyle(-1);//va plus bas ...
}
void chCubeStyle(int n) {//change le style du cube par n rang
println("changement de style par "+n);
//réinitialisation au cas où une ou plusieurs face(s) d'un style remplace celle(s) des autres styles pouvant ainsi gaché les autres en question
vCubeFC[0] = color(127);//D
vCubeFC[1] = color(255, 127, 0);//B
vCubeFC[2] = color(0, 0, 255);//R
vCubeFC[3] = color(255, 0, 0);//F
vCubeFC[4] = color(0, 255, 0);//L
vCubeFC[5] = color(0);//U
girlCFC[0] = color(255, 255, 0);
girlCFC[1] = color(255, 127, 0);
girlCFC[2] = color(255, 0, 255);
girlCFC[3] = color(255, 16, 120);
girlCFC[4] = color(0, 255, 0);
girlCFC[5] = color(255);
cF2C[0] = color(255);
cF2C[1] = color(255);
cF2C[2] = color(0);
cF2C[3] = color(0);
cF2C[4] = color(255);
cF2C[5] = color(0);
cF3C[0] = color(0, 255, 0);
cF3C[1] = color(255, 0, 0);
cF3C[2] = color(0, 0, 127);
cF3C[3] = color(127, 0, 0);
cF3C[4] = color(0, 0, 255);
cF3C[5] = color(0, 127, 0);
MirrorFC[0] = color(210);
MirrorFC[1] = color(210);
MirrorFC[2] = color(210);
MirrorFC[3] = color(210);
MirrorFC[4] = color(210);
MirrorFC[5] = color(210);
MinxFC[0] = color(0, 255, 0);
MinxFC[1] = color(255, 0, 0);
MinxFC[2] = color(0, 255, 255);
MinxFC[3] = color(255, 255, 0);
MinxFC[4] = color(0, 0, 255);
MinxFC[5] = color(255, 0, 255);
OldStyle[0] = color(255);
OldStyle[1] = color(127);
OldStyle[2] = color(0);
OldStyle[3] = color(255);
OldStyle[4] = color(127);
OldStyle[5] = color(0);
WCAClrSch[0] = color(255, 255, 0);
WCAClrSch[1] = color(0, 0, 255);
WCAClrSch[2] = color(255, 0, 0);
WCAClrSch[3] = color(0, 255, 0);
WCAClrSch[4] = color(255, 128, 0);
WCAClrSch[5] = color(255);
TwoFC[0] = color(255, 255, 0);
TwoFC[1] = color(128, 64);
TwoFC[2] = color(128, 64);
TwoFC[3] = color(128, 64);
TwoFC[4] = color(128, 64);
TwoFC[5] = color(255);
Visual[0] = color(255, 255, 0, 127);
Visual[1] = color(0, 0, 255, 127);
Visual[2] = color(255, 0, 0, 127);
Visual[3] = color(0, 255, 0, 127);
Visual[4] = color(255, 128, 0, 127);
Visual[5] = color(255, 127);
cubiesBorder = color(255);//couleur de la bordure du cube
if (cubeStyleSelected<cubeStyles.length && n==1) cubeStyleSelected++;//si le style n'est pas le dernier de la liste et que le rang de changement est de 1
else if (cubeStyleSelected>0 && n==-1) cubeStyleSelected--;//si le style n'est pas le premier de la liste et que le rang de changement est de -1
if (cubeStyleSelected==0) {//si le style sélectionné est le premier
cubeFaceColors[0] = color(255, 255, 0);
cubeFaceColors[1] = color(255, 127, 0);
cubeFaceColors[2] = color(0, 0, 255);
cubeFaceColors[3] = color(255, 0, 0);
cubeFaceColors[4] = color(0, 255, 0);
cubeFaceColors[5] = color(255);
cubiesBorder = color(0);//bordure du cube de couleur noir
}
else if (cubeStyleSelected==1) {//si le style sélectionné est le deuxième
cubiesBorder = color(255);//bordure du cube de couleur blanche
cubeFaceColors = vCubeFC;//le schéma de couleur est celui de vCubeFC
}
else if (cubeStyleSelected==2) {//...
cubiesBorder = color(255, 255, 0);
cubeFaceColors = girlCFC;
}
else if (cubeStyleSelected==3) {
cubiesBorder = color(32);
cubeFaceColors = cF2C;
}
else if (cubeStyleSelected==4) {
cubiesBorder = color(180);
cubeFaceColors = cF3C;
}
else if (cubeStyleSelected==5) {
cubiesBorder = color(0);
cubeFaceColors = MirrorFC;
}
else if (cubeStyleSelected==6) {
cubiesBorder = color(0);
cubeFaceColors = MinxFC;
}
else if (cubeStyleSelected==7) {
cubiesBorder = color(180);
cubeFaceColors = OldStyle;
}
else if (cubeStyleSelected==8) {
cubiesBorder = color(127, 0);//pas de bordure
cubeFaceColors[0] = color(255, 255, 0);
cubeFaceColors[1] = color(255, 127, 0);
cubeFaceColors[2] = color(0, 0, 255);
cubeFaceColors[3] = color(255, 0, 0);
cubeFaceColors[4] = color(0, 255, 0);
cubeFaceColors[5] = color(255);
}
else if (cubeStyleSelected==9) {
cubiesBorder = color(0);
cubeFaceColors = WCAClrSch;
}
else if (cubeStyleSelected==10) {
cubiesBorder = color(0, 200);//a peine transparent
cubeFaceColors = TwoFC;
}
else if (cubeStyleSelected==11) {
cubiesBorder = color(64, 127);//semi opaque
cubeFaceColors = Visual;
}
if (cubeStyleSelected==8) rc.setSize(5);//particularité visuelle/physique du style n°9
else reset();//rc.setSize(height/8); pour éviter le sur écartement cubie-cubie sur la globalité des style
}
void faceMove(char face) {//mouvement de face
char pVal;
switch(face) {//condition multiple sur la valeur de face
case 'R': //mouvement R
axis = 0;//premier axe
slice = 2;//dernière tranche
direction = 1;//sens des aiguïlles d'une montre
pVal = 'r';//mouvement pour l'undo
break;//evite la boucle dans switch()
case 'r':
axis = 0;
slice = 2;
direction = -1;//sens inverse ..
pVal = 'R';
break;
case 'M':
axis = 0;
slice = 1;
direction = -1;
pVal = 'm';
break;
case 'm':
axis = 0;
slice = 1;
direction = 1;
pVal = 'M';
break;
case 'L':
axis = 0;
slice = 0;
direction = -1;
pVal = 'l';
break;
case 'l':
axis = 0;
slice = 0;
direction = 1;
pVal = 'L';
break;
case 'X':
axis = 0;//R
slice = 2;
direction = 1;
axis = 0;//M'
slice = 1;
direction = 1;
axis = 0;//L'
slice = 2;
direction = 1;
pVal = 'x';
break;
case 'x':
axis = 0;//R'
slice = 2;
direction = -1;
axis = 0;//M
slice = 1;
direction = -1;
axis = 0;//L
slice = 2;
direction = -1;
pVal = 'X';
break;
case 'D':
axis = 1;
slice = 0;
direction = -1;
pVal = 'd';
break;
case 'd':
axis = 1;
slice = 0;
direction = 1;
pVal = 'D';
break;
case 'E':
axis = 1;
slice = 1;
direction = -1;
pVal = 'e';
break;
case 'e':
axis = 1;
slice = 1;
direction = 1;
pVal = 'E';
break;
case 'U':
axis = 1;
slice = 2;
direction = 1;
pVal = 'u';
break;
case 'u':
axis = 1;
slice = 2;
direction = -1;
pVal = 'U';
break;
case 'Y':
axis = 1;//U
slice = 2;
direction = 1;
axis = 1;//E'
slice = 1;
direction = 1;
axis = 1;//D'
slice = 0;
direction = 1;
pVal = 'y';
break;
case 'y':
axis = 1;//U'
slice = 2;
direction = -1;
axis = 1;//E
slice = 1;
direction = -1;
axis = 1;//D
slice = 0;
direction = -1;
pVal = 'Y';
break;
case 'B':
axis = 2;
slice = 0;
direction = -1;
pVal = 'b';
break;
case 'b':
axis = 2;
slice = 0;
direction = 1;
pVal = 'B';
break;
case 'S':
axis = 2;
slice = 1;
direction = 1;
pVal = 's';
break;
case 's':
axis = 2;
slice = 1;
direction = -1;
pVal = 'S';
break;
case 'F':
axis = 2;
slice = 2;
direction = 1;
pVal = 'f';
break;
case 'f':
axis = 2;
slice = 2;
direction = -1;
pVal = 'F';
break;
case 'Z':
axis = 2;//F
slice = 2;
direction = 1;
axis = 2;//S
slice = 1;
direction = 1;
axis = 2;//B'
slice = 0;
direction = 1;
pVal = 'z';
break;
case 'z':
axis = 2;//F'
slice = 2;
direction = -1;
axis = 2;//S'
slice = 1;
direction = -1;
axis = 2;//B
slice = 0;
direction = -1;
pVal = 'Z';
break;
default:
return;
}
println(pVal);
//undoStr[undoStr.length] = pVal;//undoStr.push(pVal);
move(true);//autorise le mouvement
}
void undo() {//comme son nom l'indique
println("entré dans undo() pile="+stackOfMoves+" top="+stackOfMoves.top);
//if(!inMove){//pour éviter d'interrompre l'algorithme en cour
//println("pas en mouvement");
char p = stackOfMoves.pop();//retire le dernier mouvements enregistré dans le stack
println("undo par "+moveUndo(p)+" donné: "+stackOfMoves.data[0]+stackOfMoves.data[1]+" top="+stackOfMoves.top);
faceMove(moveUndo(p));//refait les mouvements sans le dernier
//moveStr = join(split(undoStr[undoStr.length-1]+"", ""), "");moveStrValid = true;// pour (char) pVal et (char[]) undoStr
//undoStr.pop(undoStr.length-1);
/*String poping = join(split(undoStr[undoStr.length-1]+"", ""), "").substring(0, undoStr.length-2);
String[] popedStr = split(poping, "");*/
//}else println("en mouvement");
}
void computeBoundaries() {//frontière du cube (pas important)
PVector pt[] = new PVector[8];//tableau à 8 vecteur pour les premiers points
PVector pt2[] = new PVector[8];//... les deuxièmes point
for (int i=0;i<8;i++) {//remplis les deux tableau en fonction de l'abscisse et de l'ordonné des points de frontière du cube apparaisant sur le plan 2D de l'écran à partir du plan 3D du cube
pt[i] = new PVector(screenX(rc.boundary.points[i].x, rc.boundary.points[i].y, rc.boundary.points[i].z),
screenY(rc.boundary.points[i].x, rc.boundary.points[i].y, rc.boundary.points[i].z), 0.0);
pt2[i] = new PVector(screenX(rc.boundary2.points[i].x, rc.boundary2.points[i].y, rc.boundary2.points[i].z),
screenY(rc.boundary2.points[i].x, rc.boundary2.points[i].y, rc.boundary2.points[i].z), 0.0);
}
//points visible sur la vue avant
//frontière du haut
topBound = new PVector[4];
topBound[0] = new PVector(pt[4].x, pt[4].y, pt[4].z);
topBound[1] = new PVector(pt[5].x, pt[5].y, pt[5].z);
topBound[2] = new PVector(pt[7].x, pt[7].y, pt[7].z);
topBound[3] = new PVector(pt[6].x, pt[6].y, pt[6].z);
//frontière du devant
frontBound = new PVector[4];
frontBound[0] = new PVector(pt[0].x, pt[0].y, pt[0].z);
frontBound[1] = new PVector(pt[1].x, pt[1].y, pt[1].z);
frontBound[2] = new PVector(pt[5].x, pt[5].y, pt[5].z);
frontBound[3] = new PVector(pt[4].x, pt[4].y, pt[4].z);
//frontière de droite
rightBound = new PVector[4];
rightBound[0] = new PVector(pt[0].x, pt[0].y, pt[0].z);
rightBound[1] = new PVector(pt[2].x, pt[2].y, pt[2].z);
rightBound[2] = new PVector(pt[6].x, pt[6].y, pt[6].z);
rightBound[3] = new PVector(pt[4].x, pt[4].y, pt[4].z);
//points visible sur la vue arrière
//frontière du bas
downBound = new PVector[4];
downBound[0] = new PVector(pt2[0].x, pt2[0].y, pt2[0].z);
downBound[1] = new PVector(pt2[2].x, pt2[2].y, pt2[2].z);
downBound[2] = new PVector(pt2[3].x, pt2[3].y, pt2[3].z);
downBound[3] = new PVector(pt2[1].x, pt2[1].y, pt2[1].z);
//frontière de l'arrière
backBound = new PVector[4];
backBound[0] = new PVector(pt2[2].x, pt2[2].y, pt2[2].z);
backBound[1] = new PVector(pt2[6].x, pt2[6].y, pt2[6].z);
backBound[2] = new PVector(pt2[7].x, pt2[7].y, pt2[7].z);
backBound[3] = new PVector(pt2[3].x, pt2[3].y, pt2[3].z);
//frontière de gauche
leftBound = new PVector[4];
leftBound[0] = new PVector(pt2[1].x, pt2[1].y, pt2[1].z);
leftBound[1] = new PVector(pt2[3].x, pt2[3].y, pt2[3].z);
leftBound[2] = new PVector(pt2[7].x, pt2[7].y, pt2[7].z);
leftBound[3] = new PVector(pt2[5].x, pt2[5].y, pt2[5].z);
}
void strOfMoves() {//algorithme
if (!inMove) {//si il n'y pas de risque de perturber l'algorithme en cour
if (strMoveCount < moveStr.length()) {//si le compte de mouvement est inférieur au nombre de mouvement
faceMove(moveStr.charAt(strMoveCount));//se place au dernier mouvement comptabilisé par strMoveCount
strMoveCount++;//incrémente strMoveCount de 1
nbOfMoves++;
}
else {
moveStrValid = false;//sinon l'ajout de mouvement est invalidé
strMoveCount = 0;//remise à zero
}
}
}
int ms=00, s=00, min=00;
void startTimer() {//chronomètre
if (!rc.isSolved()) {//si le cube est résolut ça n'a pas de sens
ms++;//augmentation des milliseconde
if (ms>=100) {
s++;
ms=00;
}//si t=100ms : t=1s
if (s>=60) {
min++;
s=00;
}//si t=60s : t=1min
}
else if (ms>=0 && s>0) {
println("["+millis()+"] "+min+":"+s+"."+ms);
min=00;
s=00;
ms=00;
}//affiche temps et le moment où la résolution c'est terminée et aussi pour éviter d'affiche le temps alors qu'il n'y a pas eu de résolution
}
char moveUndo(char move) {
String str = move+"";
if (str.equals(str.toLowerCase())) {
return str.toUpperCase().charAt(0);
}
else return str.toLowerCase().charAt(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment