================================================================================ EXERCICE 1 ================================================================================ A : Structure de données La solution la plus simple consiste à encoder chaque pièce par une chaîne à deux caractères (en reprenant un principe similaire à celui utilisé pour encoder les cartes dans la classe 'deck' vu en TD). Le premier caractère est une lettre (de A à I) qui permet d'identifier chacune des pièces différente du jeu, et le second caractère est un chiffre (de 1 à 4) qui va identifier les zones de chaque pièce, en fonction du nombre de cases qu'elle occupe sur la grille : * Le prisonnier a donc 4 symboles correspondant à ses 4 cases: A1, A2, A3, A4 * Le gardien horizontal a 2 symboles: B1, B2 * Les gardiens verticaux ont 2 symboles chacun: C1, C2, D1, D2, E1, E2, F1, F2 * Les petits gardiens n'ont qu'un symbole chacun: G1, H1, I1, J1 * Les cases vides sont représentées par une chaîne vide: '' ------------------------------------------------------------------------------- B : Modélisation UML Il faut juste donner un diagramme de classe qui va lister les attributs et les méthodes de la classe Klotski. On peut s'inspirer de la classe 'Grid' (exercice D8) mais ce n'est pas obligatoire de mettre toutes les méthodes de 'Grid' car dans Klotski, la taille de la grille est fixe (5x4) * Attributs : - grid : matrice de 5 lignes et 4 colonnes, implémentée avec une liste de listes (chaque élément de la matrice est une chaîne de 2 caractères) - moves : dictionnaire permettant d'associé un déplacement (drow, dcol) à chaque direction 'E'/'N'/'W'/'S' * Méthodes : - __init__() : none # initialisation de l'attributs - reset() : none # (ré)initialisation de la grille dans la configuration de départ - move(pos:tuple, dir:str) : none # déplace la pièce localisé en pos=(x,y) dans la direction dir=E/N/W/S - victory() : bool # vérifie la configuration de victoire et renvoit le booléen correspondant -------------------------------------------------------------------------------- C : Code de la méthode 'reset' L'idée est de définir la configuration de départ comme une grande chaîne de caractère qui représente les symboles de chaque case, mis bout à bout. On utilisera le caractère '/' pour séparer les différentes lignes de la grille, et le caractère ':' pour séparer les cases. Ainsi, il suffit d'utiliser la méthode 'split' pour séparer cette grande chaîne en une série de lignes et colonnes, et créer automatiquement une structure de liste de listes, que l'on va stocker dans l'attribut 'self.grid' de la classe noyau : def reset(self): """reset the grid to its initial configuration""" grid = 'C1:A1:A2:D1/C2:A3:A4:D2/E1:B1:B2:F1/E2:G1:H1:F2/I1:::J1' self.grid = [line.split(':') for line in grid.split('/')] -------------------------------------------------------------------------------- D : Code de la méthode 'move' Pour le déplacement des pièces, on va s'inspirer des exemples fournis durant le semestre, notamment les fichiers 'E6D_event' et 'E6_slide', en les adaptant à la configuration spécifique de Klotski (pièces de taille variable). Le code complet de la méthode est assez long si l'on veut traiter tous les cas particuliers, mais globalement l'intégralité des points de la question était données dès que le squelette de base du code était fourni par l'étudiant. def move(self, pos, dir): """try to move piece at position 'pos' in direction 'dir'""" grid = self.grid; row, col = pos # symbol = grid[row][col] # get symbol for piece stored as 'pos' moves = dict(E=(0,1), W=(0,-1), S=(1,0), N=(-1,0)) # translation vectors drow, dcol = moves[dir] # get translation vector for provided direction if grid[row+drow][col+dcol] != '': return False # destination is not empty etc... (on teste les autres cas de figure pour le déplacement) -------------------------------------------------------------------------------- E : Modélisation UML Le sujet parle d'utiliser la widget "Canvas' pour le tracé de la grille de jeu, donc on se retrouve avec une l'interface très proche de celle de la classe 'PixelArt' (exercice E9). Mais oralement durant l'épreuve, j'avais indiqué qu'on pouvait également imaginer une interface utilisant une grille de Brick, en supposant qu'on possède une grille d'images permettant d'afficher les différentes parties de chaque pièce. L'une ou l'autre des modélisations était acceptée de manière équivalente. Comme on est dans un cadre de séparation noyau/interface, la classe 'PegGUI' (exemple D8) est l'exemple à suivre * Attributs : board : Frame avec fold=4, contenant les 20 Brick multi-état images : tuple d'images (utilisés pour les états des Brick) names : liste de chaîne de caractères identifiant les noms des pièces * Méthodes : - __init__() : none # création de la fenêtre et des widgets - __on_click(widget, code, mods) : none # gestion des clics de souris - reset() : none # (ré)initialisation de la grille dans la configuration de départ - show() : none # affichage de la nouvelle configuration de la grille après un déplacement - victory() : none # création d'une animation de victoire -------------------------------------------------------------------------------- F : On suppose que les images des pièces du jeu sont dans une grille d'images, appelée 'klotski.png'. Ainsi, la fenêtre graphique consiste simplement à créer une grille de widgets Brick qui vont afficher l'une des images des pièces. Comme dans les exemples vus en TD, on va utiliser des widgets multi-états pour simplifier la mise en oeuvre : def __init__(self): """create main window and pack the widgets""" Win.__init__(self, title='KLOTSKI', op=5, fold=4) self.grid = Klotski() # create instance for kernel and set initial config self.names = 'A1 A2 A3 A4 B1 B2 C1 C2 D1 D2 E1 E2 F1 F2 G1 H1 I1 J1'.split() self.images = ImageGrid('klotski.png') # load image grid for loop in range(20): # loop over all grid cells row, col = loop // 4, loop % 4 # get grid coords for current cell n = self.grid[row][col] # get name for piece stored at current cell state = self.names.index(n) # get state for current cell Brick(self, image=self.images, state=state) # create multi-state Brick self.loop() ================================================================================ EXERCICE 2 ================================================================================ L'astuce est de générer une liste (ou un tuple) qui contient les numéros des faces du dé, en les répétant autant de fois que la valeur du poids associé. Ainsi un 'udice(1,3)' va donner une liste [1,2,2,2] et un 'udice(1,1,1,1,1,5)' va donner une liste [1,2,3,4,5,6,6,6,6,6]. Ensuite pour lancer le dé, il suffira de faire un 'choice' (importé du module 'random') sur cette liste. def __init__(self, *weights): """initialize 'self' with a set of weights, one for each side""" self.weights = weights if weights else (1,1,1,1,1,1) self.size, self.total = len(self.wrights), sum(self.weights) # transform each side into a set of virtual sides according to its weight self.sides = [s for n,w in enumerate(self.weights) for s in w*[n+1]] def __repr__(self): """return object representation for 'self'""" return "%s%r" % (type(self).__name__, self.weights) def roll(self, n=None): """return result of a single dice roll or a tuple of 'n' dice rolls""" if n is None: return choice(self.sides) return tuple(self.roll() for p in range(n)) ================================================================================