From ad2d020b19f3706695c911fbe386e5a56925e9bc Mon Sep 17 00:00:00 2001 From: JeanMarieMineau Date: Sat, 13 May 2017 18:49:33 +0200 Subject: [PATCH] Add files via upload --- Action.py | 143 +++++++++ Bouton.py | 151 +++++++++ Commande.py | 104 +++++++ ConstanteTouche.py | 25 ++ Cube.py | 186 +++++++++++ Cube1x1.py | 510 ++++++++++++++++++++++++++++++ CubeGetteur.py | 207 +++++++++++++ InterfaceBoutons.py | 80 +++++ Solveur.py | 734 ++++++++++++++++++++++++++++++++++++++++++++ img/B1.png | Bin 0 -> 1961 bytes img/B2.png | Bin 0 -> 1956 bytes main.py | 53 ++++ 12 files changed, 2193 insertions(+) create mode 100644 Action.py create mode 100644 Bouton.py create mode 100644 Commande.py create mode 100644 ConstanteTouche.py create mode 100644 Cube.py create mode 100644 Cube1x1.py create mode 100644 CubeGetteur.py create mode 100644 InterfaceBoutons.py create mode 100644 Solveur.py create mode 100644 img/B1.png create mode 100644 img/B2.png create mode 100644 main.py diff --git a/Action.py b/Action.py new file mode 100644 index 0000000..cf4f6d8 --- /dev/null +++ b/Action.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +''' +Created on 16 mars 2017 + +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: +programme réalisé sur une base du programme dévellopé par Leonel Machava + http://codeNtronix.com +''' +from random import choice + +class Action: + """Object qui permet de tourner les faces.""" + + def __init__(self, parent): + """parent est le cube qui utilise l'instance.""" + self.parent = parent + self.angle = 0 # Angle restant à parcourir + self.actions = [] # Actions à effectuer + self.indexs = [] # Indices des cubies à tourner + self.anglesRotation = []# Angle en x, y, et z à tourner. + self.listeActionsPossible = ["H CWI", "H ACWI", + "B CWI", "B ACWI", + "D CWI", "D ACWI", + "G CWI", "G ACWI", + "AV CWI", "AV ACWI", + "AR CWI", "AR ACWI"] + + + def doAll(self): + """Execute toutes les actions de la liste.""" + while self.actions: + self.__call__() + + def __call__(self): + """Méthode principale.""" + if self.angle == 0 and not self.actions: + #self.actions.append("ALEA") + return #Si l'action est terminée et qu'il n'y en a pas d'autre, on passe. + elif self.angle == 0: + self.initNewAction() + + for i in self.indexs: + self.parent.cubies[i] = self.parent.cubies[i].rotationX(self.anglesRotation[0]) + self.parent.cubies[i] = self.parent.cubies[i].rotationY(self.anglesRotation[1]) + self.parent.cubies[i] = self.parent.cubies[i].rotationZ(self.anglesRotation[2]) + angle = 0 + for i in self.anglesRotation: + angle += i + if self.angle < 0: + print("deadlock") + self.angle -= abs(angle) + + def initNewAction(self, actions = None): + """Initialise une nouvelle action. Si une action est mise en argument, + elle est initialisée, sinon, l'action est prise dans la liste d'action.""" + if actions: + commande = actions + actions.split() + else: + commande = self.actions.pop(0) + actions = commande.split() + if actions[0] == "ALEA": + self.melange() + if self.actions: + actions = self.actions.pop(0).split() + if actions[0] == "SOLVE": + self.resoudre() + elif actions[1] == "CW": + self.angle = 90 + angle = 10 + elif actions[1] == "ACW": + self.angle = 90 + angle = -10 + elif actions[1] == "CWI": + self.angle = 90 + angle = 90 + elif actions[1] == "ACWI": + self.angle = 90 + angle = -90 + else: + raise ValueError("La valeur " + str(actions[1]) + " est inconnue.") + + x, y, z = 0, 0, 0 + if actions[0] == "H": + y = angle + self.indexs = self.parent.getH() + elif actions[0] == "B": + y = angle + self.indexs = self.parent.getB() + elif actions[0] == "G": + x = angle + self.indexs = self.parent.getG() + elif actions[0] == "D": + x = angle + self.indexs = self.parent.getD() + elif actions[0] == "AV": + z = angle + self.indexs = self.parent.getAv() + elif actions[0] == "AR": + z = angle + self.indexs = self.parent.getAr() + elif actions[0] == "X": + x = angle + self.indexs = [i for i in range(len(self.parent.cubies))] + elif actions[0] == "Y": + y = angle + self.indexs = [i for i in range(len(self.parent.cubies))] + elif actions[0] == "Z": + z = angle + self.indexs = [i for i in range(len(self.parent.cubies))] + elif actions[0] != "SOLVE" and actions[0] != "ALEA": + raise ValueError("La commande " + str(commande) + " est inconnue.") + self.anglesRotation = [x, y, z] + + def melange(self): + """Met des actions aléatoire dans la liste d'action.""" + liste = [] + while len(liste) < 21: + liste.append(choice(self.listeActionsPossible)) + self.actions = liste + self.actions + print(liste) + + def resoudre(self): + """Lance la resolution.""" + self.parent.resoudre() diff --git a/Bouton.py b/Bouton.py new file mode 100644 index 0000000..98bc21f --- /dev/null +++ b/Bouton.py @@ -0,0 +1,151 @@ +''' +Created on 5 mai 2017 + +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: + +Class pour les boutons. Une classe qui gère l'ensemble des +boutons, et une autre qui est le bouton. +''' + +import pygame + +class Boutons: + """Class gérant la création de boutons et le fait de clique dessus, + ainsi que l'affichage.""" + + def __init__(self): + """Cette class à pour attribut une liste de boutons.""" + + self.boutons = [] + + def nouveauBouton(self, pos, image=None, couleur=(255,0,255), + size=(60,60), callback=lambda *args: None, argsCallback=[]): + """Crée un nouveau bouton.""" + + bouton = Bouton(pos, self, image=image, couleur=couleur, + size=size, callback=callback, argsCallback=argsCallback) + self.boutons.append(bouton) + return bouton + + def update(self, events): + """Gère le click sur le bouton.""" + + for event in events: + if event.type == pygame.MOUSEBUTTONDOWN: + pos = event.pos + self.callbackClic(pos) + + def callbackClic(self, pos): + """Methode appellé lors d'un clic.""" + + for bouton in self.boutons: + if bouton.rect.collidepoint(*pos): + bouton.callback(*bouton.argsCallback) + return + + def display(self, screen): + """Affiche les Boutons.""" + + for bouton in self.boutons: + bouton.display(screen) + + +class Bouton: + """Un Bouton.""" + + def __init__(self, pos, parent, image=None, couleur=(255,0,255), + size=(60,60), callback=lambda *args: None, argsCallback=[]): + """Crée un bouton, si une image est donné, il la charge, sinon, + c'est un rectangle de taille size et de couleur couleur qui est affiché.""" + + self.parent = parent + self.pos = pos + if image is not None: + self.surface = pygame.image.load(image).convert_alpha() + self.rect = self.surface.get_rect() + else: + self.surface = pygame.Surface(size) + self.surface.fill(couleur) + self.rect = self.surface.get_rect() + self.rect = self.rect.move(self.pos) + self.callback = callback + self.argsCallback = argsCallback + + def suppr(self): + """Suprime le Bouton.""" + + self.parent.boutons.remove(self) + + def display(self, screen): + """Affiche le Bouton.""" + + screen.blit(self.surface, self.rect) + +def callbackTest(a, b, c, d, e, f, g): + """Test de callback.""" + print(a) + print(b) + print(c) + print(d) + print(e) + print(f) + print(g) + +def callback2(*args): + print("toto") + + +if __name__ == "__main__": + + boutons = Boutons() + + screen = pygame.display.set_mode((1000, 400)) + clock = pygame.time.Clock() + + pos = (50,50) + bouton = boutons.nouveauBouton(pos, callback=callbackTest, argsCallback=["A", + "B", + "C", + "D", + "E", + "F", + "G"]) + + pos2 = (50, 200) + bouton2 = boutons.nouveauBouton(pos2, callback=callback2, argsCallback=["H", + "I", + "J", + "K", + "L", + "M", + "N"]) + + while True: + screen.fill((150, 150, 150)) + + events = pygame.event.get() + for event in events: + if event.type == pygame.QUIT: + exit() + quit() + boutons.update(events) + boutons.display(screen) + pygame.display.update() + clock.tick(30) + \ No newline at end of file diff --git a/Commande.py b/Commande.py new file mode 100644 index 0000000..b4169e8 --- /dev/null +++ b/Commande.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +#coding: utf-8 + +""" +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: Maxime Keller +""" +import pygame +from pygame.locals import * +from ConstanteTouche import * +from Action import Action + +#pygame.init() + +class Commande: + """Objet permettant de gérer les touches du clavier""" + + def __init__(self, parent): + self.action = Action(parent) + self.KANOMI = (K_UP, K_UP, K_DOWN, K_DOWN, K_LEFT, K_RIGHT, K_LEFT, K_RIGHT, TOUCHE_b, TOUCHE_a) + self.iKonami = 0 + + def testKonamie(self, event): + """Fait avancer, ou remet a 0, le konami code.""" + if event.key == self.KANOMI[self.iKonami]: + self.iKonami += 1 + if event.key == TOUCHE_a: + event.key = None + else: + self.iKonami = 0 + if self.iKonami == 10: + self.iKonami = 0 + self.action.actions.append("SOLVE") + + def touches(self): + """Les touches sont reliés a un autre programme""" + + self.events = pygame.event.get() + for event in self.events: + if event.type == QUIT: + pygame.quit() + quit() + if event.type == KEYDOWN: + self.testKonamie(event) + if event.key == TOUCHE_a: + self.action.actions.append("ALEA") + print("alea") + elif event.key == TOUCHE_e : + self.action.actions.append("H CW") + elif event.key == TOUCHE_r : + self.action.actions.append("B CW") + elif event.key == TOUCHE_y : + self.action.actions.append("AV CW") + elif event.key == TOUCHE_u : + self.action.actions.append("AR CW") + elif event.key == TOUCHE_o : + self.action.actions.append("G CW") + elif event.key == TOUCHE_p : + self.action.actions.append("D CW") + elif event.key == TOUCHE_d : + self.action.actions.append("H ACW") + elif event.key == TOUCHE_f : + self.action.actions.append("B ACW") + elif event.key == TOUCHE_h : + self.action.actions.append("AV ACW") + elif event.key == TOUCHE_j : + self.action.actions.append("AR ACW") + elif event.key == TOUCHE_l : + self.action.actions.append("G ACW") + elif event.key == TOUCHE_m : + self.action.actions.append("D ACW") + elif event.key == TOUCHE_z : + print("stop") + # self.orientation[1] = -45 + #elif event.key == K_RIGHT : + # self.orientation[0] = -45 + # self.orientation[2] = -45 + #elif event.key == K_LEFT : + # self.orientation[2] = 135 + # self.orientation[0] = 135 + #elif event.type == KEYUP: + # if event.key == K_UP : + # self.orientation[1] = 45 + # elif event.key == K_RIGHT : + # self.orientation[0] = 45 + # self.orientation[2] = 45 + # elif event.key == K_LEFT : + # self.orientation[2] = 45 + # self.orientation[0] = 45 \ No newline at end of file diff --git a/ConstanteTouche.py b/ConstanteTouche.py new file mode 100644 index 0000000..6da0a5a --- /dev/null +++ b/ConstanteTouche.py @@ -0,0 +1,25 @@ +from pygame.locals import K_a +#TOUCHE_a = 113 +TOUCHE_a = K_a +TOUCHE_z = 119 +TOUCHE_e = 101 +TOUCHE_r = 114 +TOUCHE_t = 116 +TOUCHE_y = 121 +TOUCHE_u = 117 +TOUCHE_i = 105 +TOUCHE_o = 111 +TOUCHE_p = 112 + +TOUCHE_q = 97 +TOUCHE_s = 115 +TOUCHE_d = 100 +TOUCHE_f = 102 +TOUCHE_g = 103 +TOUCHE_h = 104 +TOUCHE_j = 106 +TOUCHE_k = 107 +TOUCHE_l = 108 +TOUCHE_m = 59 + +TOUCHE_b = 98 diff --git a/Cube.py b/Cube.py new file mode 100644 index 0000000..c60839e --- /dev/null +++ b/Cube.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +''' +Created on 16 mars 2017 + +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: , Maxime Keller +''' +import pygame +from operator import itemgetter +from random import choice +from Cube1x1 import Cubie3D +from Action import Action +from CubeGetteur import CubeGetteur +from Commande import Commande +from Solveur import Solver + +ROUGE, ORANGE, JAUNE, BLANC, VERT, BLEU = (255,0,0), (255,130,20), (255,255,20), (255,255,255), (0,255,0), (0,0,255) + +class Cube(Commande, CubeGetteur, Solver): + """Objet représentant le rubick's Cube.""" + + def __init__(self, screen, vide = False): + """Init. ''vide'' permet d'éviter une réinitialisation de tous le cube.""" + + Commande.__init__(self, parent = self) + if not vide: + #Place les cubies + lpos = [] + for x in [-4, 0, 4]: + for y in [-4, 0, 4]: + for z in [-4, 0, 4]: + lpos.append((x, y, z)) + self.cubies = [Cubie3D(pos) for pos in lpos if pos != (0,0,0)] + + self.orientation = [-30,30,0] + self.screen = screen + self.setColor() + + def creationRefletNonOriente(self): + """Crée un reflet du cube non orienté.""" + newCube = Cube(None, vide = True) + newCube.orientation = [0,0,0] + newCubies = [cubie.copie() for cubie in self.cubies] + newCube.cubies = newCubies + newCube.screen = self.screen + return newCube + + def creationReflet(self): + """Crée un reflet du cube orienté selon l'orientation du cube.""" + newCube = Cube(None, vide = True) + newCube.orientation = self.orientation + newCubies = [cubie.rotationX(self.orientation[0]) for cubie in self.cubies] + newCubies = [cubie.rotationY(self.orientation[1]) for cubie in newCubies] + newCubies = [cubie.rotationZ(self.orientation[2]) for cubie in newCubies] + newCube.cubies = newCubies + newCube.screen = self.screen + return newCube + + def affichage(self): + """Affiche le cube""" + + cubiesZ = [[cubie, cubie.minZ] for cubie in self.cubies] + + #self.nbImg = 0 + liste = sorted(cubiesZ, key=itemgetter(1), reverse=True) + + for tmp in liste: + cubie = tmp[0] + cubie.affichage(self.screen) + #pygame.display.flip() + #pygame.display.flip() + #pygame.image.save(screen,"./img/" + str(self.nbImg) + "Tri2.png") + #self.nbImg += 1 + #pygame.time.wait(100) + + def setColor(self): + """Attribut les couleurs aux faces.""" + liste = self.getH() + for i in liste: + self.cubies[i].couleurs[0] = ROUGE + self.cubies[i].couleursResolution.append(ROUGE) + liste = self.getB() + for i in liste: + self.cubies[i].couleurs[1] = ORANGE + self.cubies[i].couleursResolution.append(ORANGE) + liste = self.getG() + for i in liste: + self.cubies[i].couleurs[2] = JAUNE + self.cubies[i].couleursResolution.append(JAUNE) + liste = self.getD() + for i in liste: + self.cubies[i].couleurs[3] = BLANC + self.cubies[i].couleursResolution.append(BLANC) + liste = self.getAr() + for i in liste: + self.cubies[i].couleurs[4] = VERT + self.cubies[i].couleursResolution.append(VERT) + liste = self.getAv() + for i in liste: + self.cubies[i].couleurs[5] = BLEU + self.cubies[i].couleursResolution.append(BLEU) + + def run(self): + """Lance l'affichage.""" + self.touches() + self.action() + reflet = self.creationReflet() + reflet.affichage() + +if __name__ == "__main__": + + pygame.init() + + angle = 5 + screen = pygame.display.set_mode((500, 500)) + pygame.display.set_caption("Rubick's Cube") + + cubes = [Cube(screen)] + + prendreImage = False + + rot = False + #cubes[0].orientation = [-30,30,0] + #print(cubes[0].cubies[cubes[0].getCubieByPos((1,1,-1))].getOrientationFace(ROUGE)) + #cubes[0].action.actions.append("H CWI") + #cubes[0].action.actions.append("H CWI") + #cubes[0].action.actions.append("B CWI") + #cubes[0].action.actions.append("B CWI") + #cubes[0].action.actions.append("D CWI") + #cubes[0].action.actions.append("D CWI") + #cubes[0].action.actions.append("G CWI") + #cubes[0].action.actions.append("G CWI") + #cubes[0].action.actions.append("AV CWI") + #cubes[0].action.actions.append("AV CWI") + #cubes[0].action.actions.append("AR CWI") + #cubes[0].action.actions.append("AR CWI") + #cubes[0].action.doAll() + #cubes[0].action.actions.append("ALEA") + #cubes[0].resoudre() + while True: + #for event in pygame.event.get(): + # if event.type == pygame.QUIT: + # pygame.quit() + # sys.exit() + # if event.type == pygame.KEYDOWN: + # if event.key == pygame.K_SPACE: + # if rot: + # rot = False + # else: + # rot = True + # if event.key == pygame.K_LEFT: + # angle = 5 + # if event.key == pygame.K_RIGHT: + # angle = -5 + screen.fill((255,255,255)) + for cube in cubes: + cube.run() + pygame.display.flip() + #pygame.time.wait(25) + if rot: + for cube in cubes: + orientation = cube.orientation + cube.orientation = [alpha + angle for alpha in orientation] + + if prendreImage: + pygame.image.save(screen,"./img/pasDeTrie.png") + prendreImage = False + + \ No newline at end of file diff --git a/Cube1x1.py b/Cube1x1.py new file mode 100644 index 0000000..f5a9923 --- /dev/null +++ b/Cube1x1.py @@ -0,0 +1,510 @@ +''' +Created on 9 mars 2017 + +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: + +Programme inspiré d'un programme dévellopé par Leonel Machava + http://codeNtronix.com +''' + +import sys, math, pygame +from operator import itemgetter + +class Point3D: + def __init__(self, x = 0, y = 0, z = 0): + self.x, self.y, self.z = float(x), float(y), float(z) + + def rotationX(self, angle): + """ Fait pivoter le point autour de l'axe X d'une valeur donnée en degrés. """ + rad = angle * math.pi / 180 + cosa = math.cos(rad) + sina = math.sin(rad) + y = self.y * cosa - self.z * sina #Formules d'addition de trigo. + z = self.y * sina + self.z * cosa + return Point3D(self.x, y, z) + + def rotationY(self, angle): + """ Fait pivoter le point autour de l'axe Y d'une valeur donnée en degrés. """ + rad = angle * math.pi / 180 + cosa = math.cos(rad) + sina = math.sin(rad) + z = self.z * cosa - self.x * sina #Formules d'addition de trigo. + x = self.z * sina + self.x * cosa + return Point3D(x, self.y, z) + + def rotationZ(self, angle): + """ Fait pivoter le point autour de l'axe Z d'une valeur donnée en degrés. """ + rad = angle * math.pi / 180 + cosa = math.cos(rad) + sina = math.sin(rad) + x = self.x * cosa - self.y * sina #Formules d'addition de trigo. + y = self.x * sina + self.y * cosa + return Point3D(x, y, self.z) + + def projection(self, largeur_ecran, hauteur_ecran, zoom, distance): + """ Transforme ce point 3D en point 2D (avec la profondeur conservé). """ + factor = zoom / (distance + self.z) + x = self.x * factor + largeur_ecran / 2 + y = -self.y * factor + hauteur_ecran / 2 + return Point3D(x, y, self.z) + + def __eq__(self, point): + """opérateur d'égalité.""" + x = abs(self.x - point.x) < 0.01 + y = abs(self.y - point.y) < 0.01 + z = abs(self.z - point.z) < 0.01 + return x and y and z + + def dans(self, points): + """Test si un point est dans une liste de points avec une impressition.""" + for point in points: + + x = False + y = False + z = False + + if abs(self.x - point.x) < 0.01: + x = True + if abs(self.y - point.y) < 0.01: + y = True + if abs(self.z - point.z) < 0.01: + z = True + + if x and y and z: + return True + + return False + + def copie(self): + """Retourne une copie de l'objet.""" + return Point3D(self.x, self.y, self.z) + +class Peintre: + """Objet de tri pour les faces.""" + def __init__(self, LARGEUR_ECRAN = 500, HAUTEUR_ECRAN = 500, ZOOM = 254, DISTANCE = 50, ECART_INTER = 0.01): + self.LARGEUR_ECRAN = LARGEUR_ECRAN # DISTANCE 10 + self.HAUTEUR_ECRAN = HAUTEUR_ECRAN + self.ZOOM = ZOOM + self.DISTANCE = DISTANCE + self.ECART_INTER = ECART_INTER + self.coin = [] + self.faces = [] + + def intersection(self, A, B, C, D): + """Retourne l'intersection des secgments [point1 point2] et [point3 point4] en 2 dimentions""" + + # Segments paralleles + if A.x == B.x and C.x == D.x: return None + + elif (A.x == B.x): + c = (C.y - D.y)/(C.x - D.x) + d = C.y - (c*C.x) + x = A.x + y = c*x + d + + elif (C.x == D.x): + a = (A.y - B.y)/(A.x - B.x) + b = A.y - (a*A.x) + x = C.x + y = a*x + b + + else: + a = (A.y - B.y)/(A.x - B.x) + b = A.y - (a*A.x) + c = (C.y - D.y)/(C.x - D.x) + d = C.y - (c*C.x) + + if a == c: + return None # Segment parallèles + elif a == 0: + y = B.y + x = (y - d)/c + elif c == 0: + y = D.y + x = (y - b/a) + else: + x = (d - b)/(a - c) + y = a*x + b + + pointDansSegment1 = (min(A.x, B.x) + self.ECART_INTER < x < max(A.x, B.x) - self.ECART_INTER) \ + and (min(A.y, B.y) + self.ECART_INTER < y < max(A.y, B.y) - self.ECART_INTER) + pointDansSegment2 = (min(C.x, D.x) + self.ECART_INTER < x < max(C.x, D.x) - self.ECART_INTER) \ + and (min(C.y, D.y + self.ECART_INTER) < y < max(C.y, D.y) - self.ECART_INTER) + + if pointDansSegment1 and pointDansSegment2: + return Point3D(x, y, 0) + else: + return None + + def profondeurIntersection(self, A, B, I): + """Retourne le point avec sa profondeur""" + + x = I.x + y = I.y + + if A.z == B.z: + z = A.z + return Point3D(x, y, z) + + a = (A.x - B.x)/(A.z - B.z) + b = B.x - (a*B.z) + c = (A.y - B.y)/(A.z - B.z) + d = B.y - (c*B.z) + + # Attention grosse formule! + + denominateur = ((x - self.LARGEUR_ECRAN/2) - a*self.ZOOM) + if denominateur != 0: + z = (b*self.ZOOM - (x - self.LARGEUR_ECRAN/2)*self.DISTANCE)/denominateur + return Point3D(x, y, z) + + denominateur = (c*self.ZOOM + y - self.HAUTEUR_ECRAN/2) + if denominateur != 0: + z = (-d*self.ZOOM - (y - self.HAUTEUR_ECRAN/2)*self.DISTANCE)/denominateur + return Point3D(x, y, z) + + denominateur = (y - self.HAUTEUR_ECRAN/2 + (c+d)*self.ZOOM) + if denominateur != 0: + z = -(y - self.HAUTEUR_ECRAN/2)*self.DISTANCE/denominateur + return Point3D(x, y, z) + + ## Formules qui MARCHENT: + #z = - (y - HAUTEUR_ECRAN/2)*DISTANCE / (y - HAUTEUR_ECRAN/2 + (c+d)*ZOOM) + #z = (-d*ZOOM - (y - HAUTEUR_ECRAN/2)*DISTANCE)/(c*ZOOM + y - HAUTEUR_ECRAN/2) + #z = (b*ZOOM - (x - LARGEUR_ECRAN/2)*DISTANCE)/((x - LARGEUR_ECRAN/2) - a*ZOOM) + + ## Formules FAUSSENT z = ((y - HAUTEUR_ECRAN/2)*a/ZOOM + b) / (1- (a/ZOOM)*(y - HAUTEUR_ECRAN/2)) + ##z = ((x - LARGEUR_ECRAN/2)*DISTANCE - b*ZOOM)/(a*ZOOM - x - LARGEUR_ECRAN/2) + z = 0 + return Point3D(x, y, z) + + def tri(self, faces, intersections, segments, points3D, point2D): + """Trie les faces en fonction des points d'intersection""" + + # pointsSegments contient des listes avec en première valeur le point d'intersection + # en deuxième et troisième les extrèmitès du segment auquel il appartient. + # Ces listes vont normalement par paire. + while intersections: + # Selectionne deux points d'intersections de même position + intersection1 = intersections[0] + intersections = intersections[1:] + segment1 = segments[0] + segments = segments[1:] + for intersection2 in intersections: + if abs(intersection1.x - intersection2.x) < 0.01 and abs(intersection1.y - intersection2.y) < 0.01: + segment2 = segments[intersections.index(intersection2)] + segments.remove(segment2) + intersections.remove(intersection2) + break + + # verifie la validite du point + if intersection1.x - intersection2.x < 0.01 and intersection1.y - intersection2.y < 0.01: + + faces1 = [] + faces2 = [] + + for face in faces: + i = faces.index(face) + pointsFaces = [points3D[j] for j in face] + if segment1[0].dans(pointsFaces) and \ + segment1[1].dans(pointsFaces): + faces1.append(i) + if segment2[0].dans(pointsFaces) and \ + segment2[1].dans(pointsFaces): + faces2.append(i) + + # Nous avons associer deux points d'intersection de profondeur differentes + # aux faces qui contiennent le segment + # Il faut que les faces donc le segment est le plus proche soit avans les + # deux autres. + interTpm = [intersection1, intersection2] + facesTpm = [faces1, faces2] + # Le point le plus éloigné en premier, donc les premieres faces a afficher en premier + if interTpm[0].z > interTpm[1].z: + tmp = facesTpm[0] + facesTpm[0] = facesTpm[1] + facesTpm[1] = tmp + # les premieres faces doivent etre avant la limite + limite = min(facesTpm[0]) + indice1, indice2 = facesTpm[1] + + if indice1 > limite and indice2 > limite: + faces1Liste = faces[:limite] + faces2Liste = faces[limite:] + faces1Liste.append(faces[min(indice1, indice2)]) + faces1Liste.append(faces[max(indice1, indice2)]) + faces2Liste.remove(faces[indice1]) + faces2Liste.remove(faces[indice2]) + faces = faces1Liste + faces2Liste + + elif indice1 > limite: + faces1Liste = faces[:limite] + faces2Liste = faces[limite:] + faces1Liste.append(faces[indice1]) + faces2Liste.remove(faces[indice1]) + faces = faces1Liste + faces2Liste + + elif indice2 > limite: + faces1Liste = faces[:limite] + faces2Liste = faces[limite:] + faces1Liste.append(faces[indice2]) + faces2Liste.remove(faces[indice2]) + faces = faces1Liste + faces2Liste + + return faces + + def peintre1(self, faces, points2D): + """Algorithme du peintre traditionnel.""" + moy_z = [] + i = 0 + for f in faces: + z = (points2D[f[0]].z + points2D[f[1]].z + points2D[f[2]].z + points2D[f[3]].z) / 4.0 + moy_z.append([i,z]) + i = i + 1 + + nouvellesFaces = [] + # Trie lessurfaces en utlisant l'algorithme du peintre + # Les faces les plus éloignées sont tracées avant les plus proches. + for tmp in sorted(moy_z,key=itemgetter(1),reverse=True): + indiceFace = tmp[0] + nouvellesFaces.append(faces[indiceFace]) + + return nouvellesFaces + + def peintre2(self): + """Retourne une liste de surface clasés selon l'algorythme du peintre, ainsi que leur couleur""" + + points3D = self.coins + points2D = [i.projection(self.LARGEUR_ECRAN, self.HAUTEUR_ECRAN, self.ZOOM, self.DISTANCE) for i in self.coins] + faces = self.faces + intersections = [] + segments = [] + + for face1 in self.faces: + #Test l'intersection de tous les point de chaque face + for face2 in self.faces: + for j in [-1, 0, 1, 2]: + for i in [-1, 0, 1, 2]: + + A3D = points3D[face1[j]] + B3D = points3D[face1[j+1]] + C3D = points3D[face2[i]] + D3D = points3D[face2[i+1]] + + A = points2D[face1[j]] + B = points2D[face1[j+1]] + C = points2D[face2[i]] + D = points2D[face2[i+1]] + + inter = self.intersection(A, B, C, D) + if inter: + intersections.append(self.profondeurIntersection(A3D, B3D, inter)) + segments.append([A3D, B3D]) + intersections.append(self.profondeurIntersection(C3D, D3D, inter)) + segments.append([C3D, D3D]) + + faces = self.peintre1(faces, points2D) + faces = self.tri(faces, intersections, segments, points3D, points2D) + return faces + + +class Cubie3D(Peintre): + """Class définissant un cube en 3D""" + + def __init__(self, pos): + """Définit le cube de centre pos et de coté 2""" + + Peintre.__init__(self) + + self.centre = Point3D(pos[0], pos[1], pos[2]) + + self.coins = [ + Point3D(pos[0]-2,pos[1]+2,pos[2]+2), # droit haut arrière + Point3D(pos[0]+2,pos[1]+2,pos[2]+2), # gauche haut arrière + Point3D(pos[0]-2,pos[1]+2,pos[2]-2), # droit haut avant + Point3D(pos[0]+2,pos[1]+2,pos[2]-2), # gauche haut avant + Point3D(pos[0]-2,pos[1]-2,pos[2]+2), # droit bas arrière + Point3D(pos[0]+2,pos[1]-2,pos[2]+2), # gauche bas arrière + Point3D(pos[0]-2,pos[1]-2,pos[2]-2), # droit bas avant + Point3D(pos[0]+2,pos[1]-2,pos[2]-2)] # gauche bas avant + #Liste des faces, les valeurs sont les indices du point correspondant dans la liste coins. + self.faces = [ + (0,1,3,2), # Haut + (4,5,7,6), # Bas + (0,2,6,4), # Droite + (1,3,7,5), # Gauche + (0,1,5,4), # Arrière + (2,3,7,6)] # Avant + #Liste des couleurs, leur indices sont les mêmes que ceux de la faces associer. + self.couleurs = [ + (0,0,0), #(255,0,0), # Haut + (0,0,0), #(255,70,0), # Bas + (0,0,0), #(255,255,0), # Gauche + (0,0,0), #(255,255,255), # Droite + (0,0,0), #(0,255,0), # Arrière + (0,0,0)] #(0,0,255)] # Avant + + self.couleursResolution = [] + + def getMinZ(self): + """Retourne la profondeur du point le plus proche.""" + z = self.coins[0].z + for pt in self.coins: + if pt.z < z: + z = pt.z + return z + + minZ = property(fget = getMinZ) + + def rotationX(self, angle): + """ Fait pivoter le cube autour de l'axe X d'une valeur donnée en degrés. """ + newCoins = [] + for point in self.coins: + newCoins.append(point.rotationX(angle)) + newCube = self.copie() + newCube.coins = newCoins + newCube.centre = self.centre.rotationX(angle) + return newCube + + def rotationY(self, angle): + """ Fait pivoter le cube autour de l'axe Y d'une valeur donnée en degrés. """ + newCoins = [] + for point in self.coins: + newCoins.append(point.rotationY(angle)) + newCube = self.copie() + newCube.coins = newCoins + newCube.coins = newCoins + newCube.centre = self.centre.rotationY(angle) + return newCube + + def rotationZ(self, angle): + """ Fait pivoter le cube autour de l'axe Z d'une valeur donnée en degrés. """ + newCoins = [] + for point in self.coins: + newCoins.append(point.rotationZ(angle)) + newCube = self.copie() + newCube.coins = newCoins + newCube.centre = self.centre.rotationZ(angle) + return newCube + + def getOrientationFace(self, couleur): + """Retourn l'orientation de la face de la couleur donnée. + Elle est donné sous la forme x, y, z, seul l'axe perpendiculaire + à la face est différent ce 0, et le signe son de quel côté du cube il ce trouve.""" + indiceFace = self.couleurs.index(couleur) + pointsFace = [(self.coins[i].x, self.coins[i].y, self.coins[i].z) for i in self.faces[indiceFace]] + pointsFaceOposee = [(self.coins[i].x, self.coins[i].y, self.coins[i].z) + for i in range(len(self.coins)) if i not in self.faces[indiceFace]] + + orientation = [0, 0, 0] + + for i in [0, 1, 2]: + #on compart les coordonnées du premier point des deux faces, pour x, y puis z + if pointsFace[0][i] > pointsFaceOposee[0][i]: orientation[i] = 1 + elif pointsFace[0][i] < pointsFaceOposee[0][i]: orientation[i] = -1 + + for point in pointsFace: + for i in [0, 1, 2]: + if abs(point[i] - pointsFace[0][i]) > 0.01: + #if point[i] != pointsFace[0][i]: + orientation[i] = 0 + # pour donner l'axe de la face, + #on compart les coordonnées e x,y et z entre les points de la place. + if orientation == [0, 0, 0]: + raise Exception("Cette face ne semble pas avoir d'orientation, what?") + return tuple(orientation) + + + def affichage(self, screen): + """Affiche le cube sur l'écran screen en utilisant l'algorithme du peintre""" + + t = [] + for coin in self.coins: + p = coin.projection(self.LARGEUR_ECRAN, self.HAUTEUR_ECRAN, self.ZOOM, self.DISTANCE) + # Place le point dans une liste de coins transformés + t.append(p) + + # Trace la surface en utlisant l'algorithme du peintre + # Les faces les plus éloignées sont tracées avant les plus proches. + faces = self.peintre2() + for f in faces[-3:]: + pointlist = [(t[f[0]].x, t[f[0]].y), (t[f[1]].x, t[f[1]].y), + (t[f[1]].x, t[f[1]].y), (t[f[2]].x, t[f[2]].y), + (t[f[2]].x, t[f[2]].y), (t[f[3]].x, t[f[3]].y), + (t[f[3]].x, t[f[3]].y), (t[f[0]].x, t[f[0]].y)] + + pygame.draw.polygon(screen,(0,0,0),pointlist, 3) + #Pour que les faces caches les traits + #en arrière, ils faut les affichers après les arrètes + pygame.draw.polygon(screen,self.couleurs[self.faces.index(f)],pointlist) + #pygame.display.flip() + #pygame.time.wait(100) + + def copie(self): + """Retourne une copie de l'objet.""" + newCubie = Cubie3D((0,0,0)) + newCubie.centre = self.centre + newCubie.coins = self.coins + newCubie.faces = self.faces + newCubie.couleurs = self.couleurs + newCubie.couleursResolution = self.couleursResolution + return newCubie + + +if __name__ == "__main__": + + ###nb_image = 0 ####### + + pygame.init() + + cubes = [Cubie3D((-2,-2,-2))] + angle = 5 + screen = pygame.display.set_mode((500, 500)) + pygame.display.set_caption("Rubick's Cube") + + rot = True + + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_SPACE: + if rot: + rot = False + else: + rot = True + if event.key == pygame.K_LEFT: + angle = 5 + if event.key == pygame.K_RIGHT: + angle = -5 + screen.fill((255,255,255)) + for cube in cubes: + cube.affichage(screen) + #if nb_image == 40: + # pygame.quit() + # sys.exit() + #pygame.image.save(screen,"./img/" + str(nb_image)+".png") + #nb_image+=1################################################ + pygame.display.flip() + pygame.time.wait(100) + if rot: + for i, cube in enumerate(cubes): + cubes[i] = cube.rotationZ(angle)#.rotationY(angle).rotationX(angle) diff --git a/CubeGetteur.py b/CubeGetteur.py new file mode 100644 index 0000000..7d8afb2 --- /dev/null +++ b/CubeGetteur.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +#coding: utf-8 +''' +Created on 5 avr. 2017 + +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: +Sert à traiter des cubies. +''' + +from operator import itemgetter + +class CubeGetteur: + """Sert à traiter des cubies.""" + + def __init__(self): + pass + + def getH(self): + """Récupère les cubie du haut et retourne leur index.""" + cubiesH = [] + for cubie in self.cubies: + if len(cubiesH) < 9: + cubiesH.append([self.cubies.index(cubie), cubie.centre.y]) + else: + cubiesH = sorted(cubiesH,key=itemgetter(1)) + if cubiesH[0][1] < cubie.centre.y: + cubiesH = cubiesH[1:] + cubiesH.append([self.cubies.index(cubie), cubie.centre.y]) + cubiesH = [item[0] for item in cubiesH] + return cubiesH + + def getB(self): + """Récupère les cubie du bas et retourne leur index.""" + cubiesH = [] + for cubie in self.cubies: + if len(cubiesH) < 9: + cubiesH.append([self.cubies.index(cubie), cubie.centre.y]) + else: + cubiesH = sorted(cubiesH,key=itemgetter(1), reverse=True) + if cubiesH[0][1] > cubie.centre.y: + cubiesH = cubiesH[1:] + cubiesH.append([self.cubies.index(cubie), cubie.centre.y]) + cubiesH = [item[0] for item in cubiesH] + return cubiesH + + def getD(self): + """Récupère les cubie de droite et retourne leur index.""" + cubiesD = [] + for cubie in self.cubies: + if len(cubiesD) < 9: + cubiesD.append([self.cubies.index(cubie), cubie.centre.x]) + else: + cubiesD = sorted(cubiesD,key=itemgetter(1)) + if cubiesD[0][1] < cubie.centre.x: + cubiesD = cubiesD[1:] + cubiesD.append([self.cubies.index(cubie), cubie.centre.x]) + cubiesD = [item[0] for item in cubiesD] + return cubiesD + + def getG(self): + """Récupère les cubie de Gauche et retourne leur index.""" + cubiesG = [] + for cubie in self.cubies: + if len(cubiesG) < 9: + cubiesG.append([self.cubies.index(cubie), cubie.centre.x]) + else: + cubiesG = sorted(cubiesG,key=itemgetter(1), reverse=True) + if cubiesG[0][1] > cubie.centre.x: + cubiesG = cubiesG[1:] + cubiesG.append([self.cubies.index(cubie), cubie.centre.x]) + cubiesG = [item[0] for item in cubiesG] + return cubiesG + + def getAv(self): + """Récupère les cubie derrière et retourne leur index.""" + cubiesA = [] + for cubie in self.cubies: + if len(cubiesA) < 9: + cubiesA.append([self.cubies.index(cubie), cubie.centre.z]) + else: + cubiesH = sorted(cubiesA,key=itemgetter(1), reverse=True) + if cubiesH[0][1] > cubie.centre.z: + cubiesA = cubiesH[1:] + cubiesA.append([self.cubies.index(cubie), cubie.centre.z]) + cubiesA = [item[0] for item in cubiesA] + return cubiesA + + def getAr(self): + """Récupère les cubie de derrière et retourne leur index.""" + cubiesA = [] + for cubie in self.cubies: + if len(cubiesA) < 9: + cubiesA.append([self.cubies.index(cubie), cubie.centre.z]) + else: + cubiesA = sorted(cubiesA,key=itemgetter(1)) + if cubiesA[0][1] < cubie.centre.z: + cubiesA = cubiesA[1:] + cubiesA.append([self.cubies.index(cubie), cubie.centre.z]) + cubiesA = [item[0] for item in cubiesA] + return cubiesA + + def getCubieByPos(self, pos): + """Retourne l'indice du Cubie de pos donné. Cette pos est relative au cube, + elle est donné en x,y,z, l'origine étant le centre du cube, et l'unité + correspond à 1 cubie.""" + + if pos[0] == -1: + potentielsCubies = self.getG() + elif pos[0] == 1: + potentielsCubies = self.getD() + elif pos[0] == 0: + #Celui est est plus embetant, il n'y a pas de méthode pour les recuperer + #Donc c'est toutes les valeur sauf celle des deux autre tranchhes. + indicesFaux = self.getG() + self.getD() + potentielsCubies = [i for i in range(len(self.cubies)) if i not in indicesFaux] + else: + raise ValueError("Vous devez demander une ordonné de -1, 0 ou 1") + # Parce que la pep 20 est bien + + if pos[1] == -1: + potentielsCubies = [i for i in potentielsCubies if i in self.getB()] + elif pos[1] == 1: + potentielsCubies = [i for i in potentielsCubies if i in self.getH()] + elif pos[1] == 0: + indicesFaux = self.getB() + self.getH() + potentielsCubies = [i for i in potentielsCubies if i not in indicesFaux] + else: + raise ValueError("Vous devez demander une abscice de -1, 0 ou 1") + #Errors should never pass silently. + + if pos[2] == -1: + potentielsCubies = [i for i in potentielsCubies if i in self.getAv()] + elif pos[2] == 1: + potentielsCubies = [i for i in potentielsCubies if i in self.getAr()] + elif pos[2] == 0: + indicesFaux = self.getAv() + self.getAr() + potentielsCubies = [i for i in potentielsCubies if i not in indicesFaux] + else: + raise ValueError("Vous devez demander une profondeur de -1, 0 ou 1") + #Oui, aujourd'hui j'ai voulu faire propre. + + if len(potentielsCubies) > 1: + raise Exception("Maxime, je sait pas comment t'as fait, mais t'as réussit à trouver \ + Une coordonnée avec plusieurs Cubie!!!") + elif len(potentielsCubies) == 0: + raise Exception("Pas de Cubies ici, peut être avez vous demandé l'origine?") + else: + return potentielsCubies[0] + + def getCubieByColors(self, couleurs): + """Retourne l'indice du Cubie de couleurs données. Ces couleurs sont + une liste donnant les couleurs exact, mais pas obligatoirement dans l'ordre.""" + for cubie in self.cubies: + if len(couleurs) != len(cubie.couleursResolution): + continue + bonCubie = True + for couleur in couleurs: + if couleur not in cubie.couleursResolution: + bonCubie = False + break + if bonCubie: return self.cubies.index(cubie) + raise Exception("Pas de cubies correspondanr aux couleurs données.") + + def getPosRelative(self, cubie): + """Retourne la position relative du cubie (don on donne l'indice).""" + + if cubie not in range(len(self.cubies)): + raise ValueError("Vous devez rentrer un indice de l'argument 'cubies'.") + + if cubie in self.getG(): + x = -1 + elif cubie in self.getD(): + x = 1 + else: + x = 0 + + if cubie in self.getB(): + y = -1 + elif cubie in self.getH(): + y = 1 + else : + y = 0 + + if cubie in self.getAv(): + z = -1 + elif cubie in self.getAr(): + z = 1 + else: + z = 0 + + return x, y, z \ No newline at end of file diff --git a/InterfaceBoutons.py b/InterfaceBoutons.py new file mode 100644 index 0000000..b788d6d --- /dev/null +++ b/InterfaceBoutons.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +#coding: utf-8 + +""" +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: + +Interface de boutons fait à la va vite. +""" + +import pygame +from Bouton import Boutons + + +def addAction(cube, action): + """Ajoute une action à la liste.""" + + cube.action.actions.append(action) + +CARACTS_BOUTONS = [[(100,30), "img/B1.png", addAction, ["ALEA"]], + [(400,30), "img/B2.png", addAction, ["SOLVE"]], + [(100,600), None, addAction, ["B CW"]], + [(500,600), None, addAction, ["B ACW"]], + [(200,600), None, addAction, ["AV CW"]], + [(400,600), None, addAction, ["AV ACW"]], + [(100,150), None, addAction, ["H CW"]], + [(500,150), None, addAction, ["H ACW"]], + [(200,150), None, addAction, ["AR CW"]], + [(400,150), None, addAction, ["AR ACW"]], + [(100,350), None, addAction, ["G CW"]], + [(100,450), None, addAction, ["G ACW"]], + [(500,350), None, addAction, ["D CW"]], + [(500,450), None, addAction, ["D ACW"]]] + +class InterfaceBoutons: + """interface gérant l'ensemble des boutons.""" + + def __init__(self, screen, cube, caractsBoutons=None): + """caractsBoutons est une liste contenants des listes + de caracteristiques des boutons, dans l'ordre: + [position, img, callback, argsCallback]""" + + if caractsBoutons is None: + caractsBoutons = CARACTS_BOUTONS + + self.screen = screen + self.cube = cube + + self.boutons = Boutons() + + for caracts in caractsBoutons: + pos, img, callback, argsCallback = caracts + # Bon c'est pas propre mais faut bien mettre le Cube + # quelque part... + argsCallback = [self.cube] + argsCallback + self.boutons.nouveauBouton(pos, image=img, callback=callback, + argsCallback=argsCallback) + + def run(self): + """Affiche et check les envents pour une itération de la + boucle principale.""" + + events = self.cube.events + self.boutons.update(events) + self.boutons.display(self.screen) diff --git a/Solveur.py b/Solveur.py new file mode 100644 index 0000000..c71fbdc --- /dev/null +++ b/Solveur.py @@ -0,0 +1,734 @@ +#!/usr/bin/env python3 +# coding: utf-8 +''' +Created on 5 avr. 2017 + +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: +Solveur du cube à faire hériter à un Cube. +''' + +class Solver: + """Class pour résoudre le Cube, + Faire hériter. + Résoud un reflet avec des action instantanées + (en ajoutant un I a la direction, CWI ou ACWI)""" + + def __init__(self): + pass + + def resoudre(self): + """Méthode principale. + Pour une question de vitesse, elle résoud un cube virtuel (reflet) + et retourn la liste d'action à éfectuer sur le modèle d'origine.""" + ####/DEBUG\#### + self.action.doAll() + ####\DEBUG/#### + resolution = [] + reflet = self.creationRefletNonOriente() + resolution.extend(self.croixHaute(reflet)) + resolution.extend(self.coinsHaut(reflet)) + resolution.extend(self.arretesMilieu(reflet)) + actions = ["X CW", "X CW"] + resolution.extend(actions) + reflet.action.actions.extend([action + "I" for action in actions]) + reflet.action.doAll() + resolution.extend(self.croixBas(reflet)) + resolution.extend(self.croixBasArrete(reflet)) + resolution.extend(self.coinsBasPos(reflet)) + resolution.extend(self.orientationCoinsBas(reflet)) + ####/DEBUG\#### + #resolution = [i + "I" for i in resolution] + #self.action.actions.extend(resolution) + #self.action.doAll() + #resolution =[] + ####\DEBUG/#### + print(resolution) + self.action.actions.extend(resolution) + + def croixHaute(self, cube): + """Résoud la croix du haut.""" + resolution = [] + for i in range(4): + resolution.extend(self.croixHauteArreteDeFace(cube)) + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + return resolution + + def croixHauteArreteDeFace(self, cube): + """Place l'arrete de face.""" + resolution = [] + #Positionne l'arrete + centreAv = cube.getCubieByPos((0,0,-1)) + centreH = cube.getCubieByPos((0,1,0)) + couleurAv = cube.cubies[centreAv].couleursResolution[0] + couleurH = cube.cubies[centreH].couleursResolution[0] + couleursArrete = [couleurAv, couleurH] + arrete = cube.getCubieByColors(couleursArrete) + posArrete = cube.getPosRelative(arrete) + ####/DEBUG\#### + #savePos = posArrete + ####\DEBUG/#### + + # Le but de cette parti est de placer l'arrete en (1,-1,0), cad face de droite millieu bas. + if posArrete == (0,1,-1): #La position finale + orientation = cube.cubies[arrete].getOrientationFace(couleurH) + if orientation == (0,1,0): #Vers le Haut + return resolution + else: + actions = ["AV CW", "AV CW", "B ACW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + + elif posArrete[1] == 1: #Le cubie est sur la face du Haut + if posArrete[0] == -1: + actions = ["G CW", "G CW"] + elif posArrete[0] == 0: + actions = ["AR CW", "AR CW"] + elif posArrete[0] == 1: + actions = ["D CW", "D CW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + + elif posArrete[1] == 0: # Le cubie est sur la tranche du Millieu + if posArrete[0] == -1: # Cette action place place ce cubie sur la face du Bas, mais pas forcement + if posArrete[2] == -1: # à la bonne place + actions = ["G ACW", "B CW", "G CW"] + elif posArrete[2] == 1: + actions = ["G CW", "B CW", "G ACW"] + elif posArrete[0] == 1: + if posArrete[2] == -1: + actions = ["D ACW", "B CW", "D CW"] + elif posArrete[2] == 1: + actions = ["D CW", "B CW", "D ACW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + + #Normalement, arrivé ici, l'arrete est forcement sur la face du bas. + posArrete = cube.getPosRelative(arrete) + ####/DEBUG\#### + #if posArrete[1] != -1: + # print("error") + # print(savePos) + #nbIter = 0 + ####\DEBUG/#### + while not posArrete == (1,-1,0): + actions = ["B CW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + posArrete = cube.getPosRelative(arrete) + ####/DEBUG\#### + #nbIter += 1 + #if nbIter > 10: + # print("Error") + ####\DEBUG/#### + + #Maintenant, la face est en (1,-1,0), il reste une formule à appliquer, + #qu'il faut choisir en fonction de l'orientation du cubie. + orientation = cube.cubies[arrete].getOrientationFace(couleurH) + if orientation == (0,-1,0): + actions = ["B CW", "AV CW", "AV CW"] + else: + actions = ["D CW", "AV CW", "D ACW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + # Une arrete de finit! + return resolution + + def coinsHaut(self, cube): + """Résoud les coins de la face du haut.""" + resolution = [] + for i in range(4): + resolution.extend(self.coinHaut(cube)) + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + return resolution + + def coinHaut(self, cube): + """Résoud le coin en (1,1,-1), cad face avant, coin haut droit.""" + resolution = [] + #Position du coin + centreAv = cube.getCubieByPos((0,0,-1)) + centreH = cube.getCubieByPos((0,1,0)) + centreD = cube.getCubieByPos((1,0,0)) + couleurAv = cube.cubies[centreAv].couleursResolution[0] + couleurH = cube.cubies[centreH].couleursResolution[0] + couleurD = cube.cubies[centreD].couleursResolution[0] + couleursCoin = [couleurAv, couleurH, couleurD] + coin = cube.getCubieByColors(couleursCoin) + posCoin = cube.getPosRelative(coin) + ####/DEBUG\#### + #savePos = posArrete + ####\DEBUG/#### + + #Si le point est en Haut, le but est de le mettre sur la face du Bas + actions = None + if posCoin == (1,1,-1): #Pos final + orientation = cube.cubies[coin].getOrientationFace(couleurH) + if orientation == (0,1,0):#Bien placé + return resolution + else: + actions = ["D ACW", "B CW", "D CW"] + elif posCoin == (1,1,1): + actions = ["D CW", "B CW", "D ACW"] + elif posCoin == (-1,1,1): + actions = ["G CW", "B CW", "G ACW"] + elif posCoin == (-1,1,-1): + actions = ["G ACW", "B CW", "G CW"] + if actions is not None: + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + + #Positionne le coin en (1,-1,-1), cad face avant, coin bas droit. + posCoin = cube.getPosRelative(coin) + ####/DEBUG\#### + #if posCoin[1] != -1: + # print("Error") + ####\DEBUG/#### + while posCoin != (1,-1,-1): + actions = ["B CW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + posCoin = cube.getPosRelative(coin) + + #Positionne le coin en (1,1,-1) et l'oriente. + orientation = cube.cubies[coin].getOrientationFace(couleurH) + if orientation == (1,0,0): + actions = ["B ACW", "AV ACW", "B CW", "AV CW"] + elif orientation == (0,-1,0): + actions = ["D ACW", "B ACW", "D CW", "B CW", "AV ACW", "B CW", "AV CW"] + else: + actions = ["B CW", "D ACW", "B ACW", "D CW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + return resolution + + def arretesMilieu(self, cube): + """Résoud les arrêtes de la tranche du milieu.""" + resolution = [] + for i in range(4): + resolution.extend(self.arreteMillieuxDroit(cube)) + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + return resolution + + def arreteMillieuxDroit(self, cube): + """Résoud l'arrete du milieux droit de la face Avant (1,0,-1).""" + resolution = [] + #Positionne l'arrete + centreAv = cube.getCubieByPos((0,0,-1)) + centreD = cube.getCubieByPos((1,0,0)) + couleurAv = cube.cubies[centreAv].couleursResolution[0] + couleurD = cube.cubies[centreD].couleursResolution[0] + couleursArrete = [couleurAv, couleurD] + arrete = cube.getCubieByColors(couleursArrete) + posArrete = cube.getPosRelative(arrete) + + orientation = cube.cubies[arrete].getOrientationFace(couleurAv) + if posArrete == (1,0,-1) and orientation == (0,0,-1): + return resolution + + elif posArrete[1] == 0: + #Si l'arrete est sur la tranche du milieu. + while not posArrete == (1,0,-1): + #positionne le cube de facon à placer l'arrete en (1,0,-1) + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posArrete = cube.getPosRelative(arrete) + resolution.extend(self.belgeD(cube)) + + posCentreAv = cube.getPosRelative(centreAv) + while not posCentreAv == (0,0,-1): + #positionne le cube de facon à replacer la face de depart en face. + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posCentreAv = cube.getPosRelative(centreAv) + + posArrete = cube.getPosRelative(arrete) + while not posArrete == (0,-1,-1): + #Place l'arrete en bas de la face. + cube.action.actions.append("B CWI") + cube.action.doAll() + resolution.append("B CW") + posArrete = cube.getPosRelative(arrete) + + orientation = cube.cubies[arrete].getOrientationFace(couleurAv) + if orientation == (0,0,-1): + resolution.extend(self.belgeD(cube)) + else: + actions = ["Y CW", "B ACW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + resolution.extend(self.belgeG(cube)) + cube.action.actions.append("Y ACWI") + cube.action.doAll() + resolution.append("Y ACW") + posArrete = cube.getPosRelative(arrete) + + posArrete = cube.getPosRelative(arrete) + ####/DEBUG\#### + #if posArrete != (1,0,-1): + # print("error") + # print(resolution) + ####\DEBUG/#### + + return resolution + + def belgeD(self, cube): + """Applique l'algo du Belge à droite.""" + resolution = ["B CW", "D ACW", "B ACW", "D CW", "B ACW", "AV ACW", "B CW", "AV CW"] + cube.action.actions.extend([action + "I" for action in resolution]) + cube.action.doAll() + return resolution + + def belgeG(self, cube): + """Applique l'algo du Belge à gauche.""" + resolution = ["B ACW", "G ACW", "B CW", "G CW", "B CW", "AV CW", "B ACW", "AV ACW"] + cube.action.actions.extend([action + "I" for action in resolution]) + cube.action.doAll() + return resolution + + def croixBas(self, cube): + """Résoud la croix du bas (pour la resolution, le cube est retourné, + donc la croix du bas est en fait en haut).""" + resolution = [] + posAretes, boolOrientation, nbBienOriente = self.croixBasOrientation(cube) + iG = posAretes.index((-1,1,0)) + iD = posAretes.index((1,1,0)) + iAv = posAretes.index((0,1,-1)) + iAr = posAretes.index((0,1,1)) + + if nbBienOriente == 4: + return resolution + + elif nbBienOriente == 0: + resolution.extend(self.algoCroixBas(cube)) + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + resolution.extend(self.algoCroixBas(cube)) + resolution.extend(self.algoCroixBas(cube)) + return resolution + + elif nbBienOriente == 2: + aligne = (boolOrientation[iG] and boolOrientation[iD]) or \ + (boolOrientation[iAr] and boolOrientation[iAv]) + if aligne: + if not (boolOrientation[iAr] and boolOrientation[iAv]): + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + resolution.extend(self.algoCroixBas(cube)) + resolution.extend(self.algoCroixBas(cube)) + + else: + while not (boolOrientation[iAr] and boolOrientation[iG]): + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posAretes, boolOrientation, nbBienOriente = self.croixBasOrientation(cube) + resolution.extend(self.algoCroixBas(cube)) + + else: + print("Mais commenent quoi comment?") + return resolution + + def croixBasOrientation(self, cube): + """Retourne une liste associant position des arretes + et une bool indiquant si leur orientation est bonne + au pas et le nombre d'orientation correct.""" + #Résoud les orientation + centreH = cube.getCubieByPos((0,1,0)) + couleurH = cube.cubies[centreH].couleursResolution[0] + + retourBoolOrientation = [] + retourPos = [(-1,1,0), (0,1,-1), (1,1,0), (0,1,1)] + nbOrientationBonnes = 0 + for pos in retourPos: + cubie = cube.getCubieByPos(pos) + orientation = cube.cubies[cubie].getOrientationFace(couleurH) + if orientation == (0,1,0): + boolOrientation = True + nbOrientationBonnes += 1 + else: boolOrientation = False + retourBoolOrientation.append(boolOrientation) + + return retourPos, retourBoolOrientation, nbOrientationBonnes + + def algoCroixBas(self, cube): + """Applique l'algo pour la croix du bas.""" + resolution = ["D ACW", "H ACW", "AV CW", "H CW", "AV ACW", "D CW"] + cube.action.actions.extend([action + "I" for action in resolution]) + cube.action.doAll() + return resolution + + def croixBasArrete(self, cube): + """Resolution de la position des arretes de la face du bas.""" + resolution = [] + posAretes, posTheo, nbPosBonnes = self.testCroixBas(cube) + iG = posAretes.index((-1,1,0)) + iD = posAretes.index((1,1,0)) + iAv = posAretes.index((0,1,-1)) + iAr = posAretes.index((0,1,1)) + + if nbPosBonnes == 4: + return resolution + + while nbPosBonnes == 0: + cube.action.actions.append("H CWI") + cube.action.doAll() + resolution.append("H CW") + posAretes, posTheo, nbPosBonnes = self.testCroixBas(cube) + + if nbPosBonnes == 4: + return resolution + + if nbPosBonnes == 2: + aligne = (posAretes[iG] == posTheo[iG]) and (posAretes[iD] == posTheo[iD]) or \ + (posAretes[iAv] == posTheo[iAv]) and (posAretes[iAr] == posTheo[iAr]) + if aligne: + if posAretes[iD] == posTheo[iD]: + cube.action.actions.append("H CWI") + cube.action.doAll() + resolution.append("H CW") + resolution.extend(self.algoTCW(cube)) + posAretes, posTheo, nbPosBonnes = self.testCroixBas(cube) + while nbPosBonnes == 0: + cube.action.actions.append("H CWI") + cube.action.doAll() + resolution.append("H CW") + posAretes, posTheo, nbPosBonnes = self.testCroixBas(cube) + + if nbPosBonnes == 4: + return resolution + elif nbPosBonnes == 2: + print("Mais, cette configuration n'est pas sensé exister!") + return resolution + + else: + #nbBoucle = 0 + while nbPosBonnes != 1: + cube.action.actions.append("H CWI") + cube.action.doAll() + resolution.append("H CW") + posAretes, posTheo, nbPosBonnes = self.testCroixBas(cube) + ####/DEBUG\#### + #nbBoucle += 1 + #if nbBoucle > 10: + # print("Trop d'iteration") + ####\DEBUG/#### + + if nbPosBonnes == 1: + while posAretes[iG] != posTheo[iG]: + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posAretes, posTheo, nbPosBonnes = self.testCroixBas(cube) + if posAretes[iAv] == posTheo[iAr]: + resolution.extend(self.algoTCW(cube)) + elif posAretes[iAr] == posTheo[iAv]: + resolution.extend(self.algoTACW(cube)) + else: + print("wtf") + + return resolution + + def algoTACW(self, cube): + """Algo de resolution en T dans le sens anti horaire, + décale les cubies de la face du haut en forme de T + orienté le bas à droite, le haut à gauche. + Permet aussi de changer l'orientation des coins combinés à + algoTCW.""" + resolution = ["D ACW", "H ACW", "D CW", "H ACW", "D ACW", "H ACW", "H ACW", "D CW", "H ACW", "H ACW"] + cube.action.actions.extend([action + "I" for action in resolution]) + cube.action.doAll() + return resolution + + def algoTCW(self, cube): + """Algo de resolution en T dans le sens horaire, + décale les cubies de la face du haut en forme de T + orienté le bas à droite, le haut à gauche. + Permet aussi de changer l'orientation des coins combinés à + algoTACW.""" + resolution = ["D CW", "H CW", "D ACW", "H CW", "D CW", "H CW", "H CW", "D ACW", "H CW", "H CW"] + cube.action.actions.extend([action + "I" for action in resolution]) + cube.action.doAll() + return resolution + + def testCroixBas(self, cube): + """Test les arrete de la croix bas (donc en haut car le cube est retourné). + Retourne la position des cubies, la position final des cubies et le nombre + de cubies à la bonne place.""" + centreH = cube.getCubieByPos((0,1,0)) + couleurH = cube.cubies[centreH].couleursResolution[0] + + retourPosTheo = [None, None, None, None] + nbPosBonnes = 0 + + retourPos = [(-1,1,0), (0,1,-1), (1,1,0), (0,1,1)] + couleursFace = [] + for p in retourPos: + p = (p[0],0,p[2]) + c = cube.getCubieByPos(p) + couleursFace.append(cube.cubies[c].couleursResolution[0]) + + for p in retourPos: + c = cube.getCubieByPos(p) + couleur = [ i for i in cube.cubies[c].couleursResolution if i != couleurH][0] + i = couleursFace.index(couleur) + retourPosTheo[i] = p + if i == retourPos.index(p): + nbPosBonnes += 1 + + return retourPos, retourPosTheo, nbPosBonnes + + def coinsBasPos(self, cube): + """Place les coins du bas (du haut comme le cube est retourné) + à leur position.""" + resolution = [] + posCoins, posCoinsTheo, nbPosBonnes = self.testCoinsBas(cube) + + iAvG = posCoins.index((-1,1,-1)) + iAvD = posCoins.index((1,1,-1)) + iArD = posCoins.index((1,1,1)) + iArG = posCoins.index((-1,1,1)) + + if nbPosBonnes == 0: + resolution.extend(self.algoTriangleCW(cube)) + posCoins, posCoinsTheo, nbPosBonnes = self.testCoinsBas(cube) + + if nbPosBonnes == 4: + return resolution + elif nbPosBonnes != 1: + print("On vous a bien dit que le tournevis n'est pas la solution!") + return resolution + + while posCoins[iAvD] != posCoinsTheo[iAvD]: + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posCoins, posCoinsTheo, nbPosBonnes = self.testCoinsBas(cube) + + if posCoins[iArD] == posCoinsTheo[iAvG]: + resolution.extend(self.algoTriangleCW(cube)) + elif posCoins[iAvG] == posCoinsTheo[iArD]: + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + resolution.extend(self.algoTriangleACW(cube)) + else: + print("PAS DE TOURNEVIS!") + + return resolution + + def algoTriangleCW(self, cube): + """Algorithme en triangle dans le sens horaire, sur la face du haut, + décale les coins dans le sens hoaire, sauf le coin avant droit.""" + resolution = ["G CW", "H CW", "D CW", "H ACW", "G ACW", "H CW", "D ACW", "H ACW"] + cube.action.actions.extend([action + "I" for action in resolution]) + cube.action.doAll() + return resolution + + def algoTriangleACW(self, cube): + """Algorithme en triangle dans le sens anti horaire, sur la face du haut, + décale les coins dans le sens anti hoaire, sauf le coin avant gauche.""" + resolution = ["D CW", "H ACW", "G CW", "H CW", "D ACW", "H ACW", "G ACW", "H CW"] + cube.action.actions.extend([action + "I" for action in resolution]) + cube.action.doAll() + return resolution + + def testCoinsBas(self, cube): + """Test les coins du bas (donc du haut car le cube est retourné). + Retourne la position des cubies, la position final des cubies et le nombre + de cubies à la bonne place.""" + retourPosTheo = [None, None, None, None] + nbPosBonnes = 0 + + retourPos = [(-1,1,-1), (1,1,-1), (1,1,1), (-1,1,1)] + couleursCoins = [] + # Creer la liste couleursCoins qui contien la couleur des + # cubie qui devraient ce trouver à cette place. + for p in retourPos: + couleurs = [] + avancement = 0 + for i in p: + pos = [0,0,0] + pos[avancement] = i + avancement += 1 + c = cube.getCubieByPos(tuple(pos)) + couleurs.append(cube.cubies[c].couleursResolution[0]) + couleursCoins.append(couleurs) + + for couleurs in couleursCoins: + # Met la position théorique du coin dans la liste pos théorique + # à l'indice correspondant à sa position. + c = cube.getCubieByColors(couleurs) + posRelative = cube.getPosRelative(c) + retourPosTheo[couleursCoins.index(couleurs)] = posRelative + if posRelative == retourPos[couleursCoins.index(couleurs)]: + nbPosBonnes += 1 + + return retourPos, retourPosTheo, nbPosBonnes + + def orientationCoinsBas(self, cube): + """Oriente les coins de la face du bas (qui est en haut + car le cube est retourné).""" + resolution = [] + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + + #Indices des pos + iAvG = posCoins.index((-1,1,-1)) + iAvD = posCoins.index((1,1,-1)) + iArD = posCoins.index((1,1,1)) + iArG = posCoins.index((-1,1,1)) + + ####/DEBUG\#### + #print((orientationCoin, nbBonneOrientation)) + ####\DEBUG/#### + + if nbBonneOrientation == 4: + return resolution + + if nbBonneOrientation == 0: + while not self.testOrientationCoinsBasHomogene(cube): + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + resolution.extend(self.resolutionOrientationCoinsBasHomogene(cube)) + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + + if nbBonneOrientation == 1: + while not orientationCoin[iAvD] == (0,1,0): + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + resolution.extend(self.algoTCW(cube)) + resolution.extend(self.algoTACW(cube)) + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + + if nbBonneOrientation == 2: + diagonal = (orientationCoin[iAvD] == (0,1,0) and orientationCoin[iArG] == (0,1,0)) or \ + (orientationCoin[iArD] == (0,1,0) and orientationCoin[iAvG] == (0,1,0)) + + if diagonal: + while not orientationCoin[iAvD] == (1,0,0): + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + actions = ["AV CW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + resolution.extend(self.algoTCW(cube)) + resolution.extend(self.algoTACW(cube)) + actions = ["AV ACW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + + else: + while not self.testOrientationCoinsBasHomogene(cube): + cube.action.actions.append("Y CWI") + cube.action.doAll() + resolution.append("Y CW") + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + resolution.extend(self.resolutionOrientationCoinsBasHomogene(cube)) + + return resolution + + def testOrientationCoinsBasHomogene(self, cube): + """Test si les coins de droite orienté de façon "Homogene", cad + orientable par la fonction associer. Utilisé dans le cadre de + l'orentation des coin de la face du bas. (en haut parce que le cube + est retourné)""" + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + #Indices des pos + iAvG = posCoins.index((-1,1,-1)) + iArG = posCoins.index((-1,1,1)) + + facesHautesVersLaDoite = (orientationCoin[iAvG] == (-1,0,0) and orientationCoin[iArG] == (-1,0,0)) + facesHautesVersAvAr = (orientationCoin[iAvG] == (0,0,-1) and orientationCoin[iArG] == (0,0,1)) + + return (facesHautesVersLaDoite or facesHautesVersAvAr) + + def resolutionOrientationCoinsBasHomogene(self, cube): + """Oriente correctement les coins en (-1,1,-1) et (-1,1,1) si ils sont + "Homogene". S'utilise dans le cadre de l'orientation des coins du bas. + (qui sont en haut car on a retournés le cube)""" + if not self.testOrientationCoinsBasHomogene(cube): + raise Exception("Les coins ne sont pas Homogènes.") + resolution = [] + posCoins, orientationCoin, nbBonneOrientation = self.testOrientationCoinsBas(cube) + + #Indices des pos + iAvG = posCoins.index((-1,1,-1)) + iArG = posCoins.index((-1,1,1)) + + if (orientationCoin[iAvG] == (0,0,-1) and orientationCoin[iArG] == (0,0,1)): + resolution.extend(self.algoTCW(cube)) + resolution.extend(self.algoTACW(cube)) + elif (orientationCoin[iAvG] == (-1,0,0) and orientationCoin[iArG] == (-1,0,0)): + actions = ["Z ACW", "Y CW", "Y CW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + resolution.extend(self.algoTCW(cube)) + resolution.extend(self.algoTACW(cube)) + actions = ["Y CW", "Y CW", "Z CW"] + resolution.extend(actions) + cube.action.actions.extend([action + "I" for action in actions]) + cube.action.doAll() + + return resolution + + def testOrientationCoinsBas(self, cube): + """Test l'orientation des coins de la face du bas + (qui eest en haut parce que le cube est retourné. + Retourn la liste de la position des coins, celle de leur + orientation et le nombre de coin bien orientés.""" + retourOrientation = [] + nbBonneOrientation = 0 + + retourPos = [(-1,1,-1), (1,1,-1), (1,1,1), (-1,1,1)] + cubieCentreHaut = cube.getCubieByPos((0,1,0)) + couleurH = cube.cubies[cubieCentreHaut].couleursResolution[0] + + for pos in retourPos: + cubie = cube.getCubieByPos(pos) + orientation = cube.cubies[cubie].getOrientationFace(couleurH) + retourOrientation.append(orientation) + if orientation == (0,1,0): + nbBonneOrientation += 1 + + return retourPos, retourOrientation, nbBonneOrientation \ No newline at end of file diff --git a/img/B1.png b/img/B1.png new file mode 100644 index 0000000000000000000000000000000000000000..be9b2e73c9044b920c424a7e792b9812e2e7c71f GIT binary patch literal 1961 zcmeAS@N?(olHy`uVBq!ia0y~yU^v0Rz>vbh#K6Gd^W;Sg0|NtNage(c!@6@aFBupZ zSkfJR9T^xl_H+M9WMyDr;4JWnEM{QfI}E~%$MaXDFfg#&dAc};RK&fVd(m6QUFP`p zKg_iprY&qoyk|KXJ~-&OV1Wo9%jNbxQ|2@-Xi;!d@fXx-vU8ch_A9K$LWQL{fv5Im zv#1KY<3a%eo)DJk$BGhO>%J`f@0ebmUS7A^dh@v|^Wv<1@_iP4+xO2sUGv@i{?E6I zWarJRUmx>Wmmws^=GODIB2kHsp$7z(?zwWPX_bTCrCUOWeqH=MXFG$1rhUc#mR|0M z;tWyTEv#!6L~uRMVQWy_SmCz5M(mgFyp0d8Ij-i|dc;`lciO`j`!~p@=~PrEI63L+ zByX&by!6Rf?2>4Pu1}!B`vY78VjB*zu4z8NCcAONrnA)YWut?2X5_I;oHBEC`{o9u=v?qq&`DtB z;ErHC#1z)Bp+OYNJy<2nz4YhhlN&#`T8UZoBAE%ZBHbmT{p#}g(!SR9Go{|=%zJh? zO6tbV=W94#uV=pZi7T9Y&ryZXze+Zqvp##V)pYq!j^|Upac8Hi?Rxb1dcf9$^(Sst z{%zWB^S=9C-o2#1=a)UMw5^ag_l2sRTE?Y4<;r>OKhX!jeKDClJtFdz{QoDq=e1+s zJ<{FzpM}#|{Mwvn%5$P`sjj&-DJW>l)UC4jtbSC_-fr{$wRh6LUHY-lb=O-|*Q?|o zx&X0$+BS~vm9O5Z<^L90b}#I^)Ss<(r;YtTo<6ssaL0ee90RRp+dWS&e^#-2^;DvM zv*oW1%Pw$y%y4X|PQ04!91^(G=)~I6cg@TKVn?@b zkm`QC{>Z0?Uv>Y4g{@Jz`*eD5_1{0za~<<^?sfhBcZu!&k|o<&zvQYVUcSxYWz|%2 z>`d}ftJZ&il@eZ9n5!-Net(rL+xref?iW1A53O2zKKG;A;!QHE-TSZY{J&AG-AnR$ z0@EeAyke7eUKxk-e0(MgcCA_2;3o0z?WgCVR*Uzu&g{rhQVcoJaN~SYlWuoSYe(>g zIj_2NLOdcTC@-+S!sHWcb+>fH{1J(NJ`GW zv&bMGnj?>@ukW~>c8(W z%03E+_#*V(Yia(`Dd`I~-+1-!@K@c9HmRZ8mK@&ub$OC$;^gIb{8c7y(mx$~&}X?~ z^6GWRu20R;yLrpEXRblbCCy9Br{yoG>a|YY@z3XiRe5UtF%rP-`oG)Z%h>E|2q{jy~P99~K|5^lVMKoBsR>|BGp5oTqbEFDiTS ztIqkw!9+V6vqiTSzW&WUy)(M%YY&I{3g7fAcdXszgfGg^-crK+Vd1&CKWvP)y?y!o zQCj>ZV94|8{5AHczf0yUo{8=@}X6#{%eS9sswAc5T|IrKue$yvK@9c^m zc?vw`?|tv5vQ~$CM(XNLg_Pj?mPY<_=5TF3FELqvLGhf~<^NaCa4U7pIg<2Ctmd&M zSFL*CyQkIFy^)iuEv7zw6&JGL(yzSCrPK0{o=D+QIKA_Ce@@5pvnox#GHbadEWg_@ z&TWq8`qbGJ&&703ML=u=dLjF78s7wmos4s`pyei7G0IqfRBnZ%EcYBY1)YS}wCPSC zRyHmb*dmNs;DR(7tW9QI;1Yd&SH%|jZ44QT5nPSn@;E*sg4Z!SvFjB_^qE8IXRH~X zshqJE7K`VaxWxfddoX`xyJ-N^*!kAn^2@zqStB s7S>HCHPli#wOKwfd2R9vnfKjZ<%P9@>t&BA3=9kmp00i_>zopr0PAv_NdN!< literal 0 HcmV?d00001 diff --git a/img/B2.png b/img/B2.png new file mode 100644 index 0000000000000000000000000000000000000000..6ff9b30d302ffb9247d1aeefd4ebfc4c374f70ec GIT binary patch literal 1956 zcmeAS@N?(olHy`uVBq!ia0y~yU^v0Rz>vbh#K6Gd^W;Sg0|NtNage(c!@6@aFBupZ zSkfJR9T^xl_H+M9WMyDr;4JWnEM{QfI}E~%$MaXDFfg!Ndb&7o!|&K38R4oV8EB&!TVp{<)`XzMJ3w`F4@)ym^OC z%U5zUh;CgNzGteFw$2JJiA7VUM@0mPo^Xwdc8ZYxUw1H?Va}A<^ZyE)>9xc&WatWr zZD>l;?Aa{I(01g}%s*duU&#O1!@8X>%5{b6yp1=^`}h6V&1sk9j+2`)A%OdI#GCkO zTmJO4mas-~2aBAs-oVttx~72fwZaBRcbzpkpXFv5F(l1x(Ku_(?5zlvWJqMxc8EB@ zB_IapnnWL;_3s&He&gq(kL2w?PUCqMv0&~No#dA+eCNcD`)L@7WvC-5N79|Jx3Bfm zpO;T={B)A#p5vyVlfcTs9l>~rDXd{bgQ$W|LaVXTn#HFcU7!2-VfMOH*0Y!Y+%S!; z?3<5|0~SqUBC5vM`oAa8q2fp7VEbJM=V?-xAt>BYxU;MK5obw_W_`pVIg}hoa*O|Gmq&*Rqg1 z+wZgBIo(`AvEZw3W$%5{JhNx5jM+?$8G^XIi+3_O4Q z=j^i=+f7^DI^4OL?r^V;lU=q->+Y>ApUWwiP3>xbd<(nydbfP$(MgVZYm4P&8a8OG zYrPsVvqEti|N8u&)9a3MUAp^!PvLW`|8D6Mq_xeH_V3?y)>`vrE@SO%&l_LNnkMxr z%r*QRvC?m0{eBJ!t8;HxzW8gt%Ke~q(itYJ!y7lOiss(E=cLB^qpvP5E`7H@|JcHy zW0o@-wzS`t>|QN2Ys2l}%_%KP*;)$(-Kuu~shoQ5MZV)QrP&@Fq6P}zHqS{otrV~L zNJMA(FC}T=Ah9PLFZi|}x^-`g*um<*duFztQ<(hk(S=YY9s8#m>Iw>8eycY4^gTJS zFLk16=mkcZoF&h%KEJv9$evBXEAF4XDY-aGW%-Rc6V?kGJ(FR)`!FP;5 zTyu{u`y0Mbr1I&CPg9o2?9!c~v$=2Ly#Vc(8-9kUy5G3{b@?_6dwstnKLeJh-hQQJ z$tpSfy{2-9*h$T-YL&~zKk8l2HSiT`u6bo$bav*i)VW8)zF%K``O&A;{})T51#%SY zpI!2iITt2u==`tl(GSyEE*~`4AGN-G?)1_J*^~ZSZp=msE2dsB?) znGz|ZZKvF7VoyGswD#_i8=QyL=PKE5_`?|cC@7-y^?8=H#o^z~@9hz()X4tt&;Pwl ztZ22CpyI=i((gqldmVqa_WrWl=DT7Q_#CZV6Vo&eH~e9o`{>#pZl2dy*v`d%@&0|} zOpgb@zG#c=&AYY973#CyI%=fbxtuJ`boY4*&GffAdN$>ot)s*8E#47nOLWURcEvA0 zB=Y6MqYHO`EAKh$q@yGGcHt6^&RN!yZ9fEMqi0P#dBD=MHR*2p^C$c-rj>D?&RM;v z?8UD-=NAuij_$E@{2m~!)_-3`=|dWW?#c^qvwXHxs;tlOb~OCo)5duBQ9#6X_TLq! z)1Pnsv~kBPt(BV3i{A;c%L}(=I=w2$-@>fI0zZ*-IN;mMAzB}@K;>NdU z?R<}|4{yJjzPvnhlb_%O^99*+^jdAdFH#qGI`n+qBJuS)-A2348NdAX^bzk}4bQEa zPapneHh$*mZO*_Z+ESLYwKY2_=ilq7g8x^Z7$1AW`$B5F-0JKat2!+==E@a{(~G}P z6!ve@sL!Y=SISI{JfIWp%c3~@*SU+TkDi3gs695LhH>uWJ9~cnA2rcBoun*wdHWk_ z|G*d4oyG6>7vGqnzOy|zZncaXtJd20)55ZThV}RyW7srj_x}@Z6}NnpZXCI|t6^gC zdEpC|Kb9)Y*Z8qi;eihmW&!&k|0M(G6zKyi*$^coazXha{7s|Om5m27Sq?FUWt3^M z%$JJbaED=`MIEv0L2U0A(=)q(q95XTx&q5k90p)8Il?%)vj8%bhz ziT4-{)D!Q`-0&l)V+kvy_K;Yi-|(z+y7i25a53$u(>i5`41WV>X8$@dG3HxLZ5riu xQHh2NbSL<_87g{5C{7R$m=wC`)1Lo~KVm~RDdkS(U|?Wi@O1TaS?83{1OR2JmW}`b literal 0 HcmV?d00001 diff --git a/main.py b/main.py new file mode 100644 index 0000000..0467fb0 --- /dev/null +++ b/main.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +#coding: utf-8 + +''' +Created on 5 mai 2017 + +Copyright 2017 Jean-Marie Mineau, Maxime Keller +This file is part of "ISN's Cube". + + "ISN's Cube" is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + "ISN's Cube" is distributed in the hope that it will be useful and + recreative, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with "ISN's Cube". If not, see . + +@author: , Maxime Keller + + https://www.python.org/ (c'est le language, donc important) + Module principale du programme de Rubicks cube. +''' + +import this # (c'est important aussi) +import pygame +from Cube import Cube +from InterfaceBoutons import InterfaceBoutons + +if __name__ == "__main__": + + pygame.init() + + surfaceCube = pygame.Surface((500, 500)) + screen = pygame.display.set_mode((700, 700)) + pygame.display.set_caption("ISN's Cube") + + cube = Cube(surfaceCube) + interfaceBoutons = InterfaceBoutons(screen, cube) + + while True: + surfaceCube.fill((255,255,255)) + screen.fill((255,255,255)) + cube.run() + screen.blit(surfaceCube, (100,100)) + interfaceBoutons.run() + pygame.display.flip() + pygame.time.wait(25) +