Lycée Carnot MP* P ROJET B ACKTRACKING 3 : PARCOURS D ’ ÉCHIQUIER PAR UN CAVALIER ET PROBLÈME DES n DAMES Parcours d’un échiquier par un cavalier But Le jeu d’échec se joue sur un échiquier, c’est-à-dire un plateau de 8×8 cases. Ces cases sont référencées de a1 à h8 (cf. figure). Un pièce, appelée le cavalier, se déplace suivant un « L » imaginaire d’une longueur de deux cases puis d’une largeur d’une case. Exemple : un cavalier situé en d4 atteint, en un seul déplacement, une des huit cases b5, c6, e6, f5, f3, e2, c2 et b3 (voir figure ci-contre). On appelle case accessible toute case que le cavalier peut atteindre en un déplacement à partir de sa position. Le but est déterminer un parcours de l’ensemble de l’échiquier par un cavalier en ne passant sur chaque case qu’une et une seule fois. Heuristique de Warnsdorff Une heuristique est un algorithme permettant d’obtenir rapidement une solution approchée à un problème d’optimisation. 0Z0Z0Z0Z Z0Z0Z0Z0 6 0Z0Z0Z0Z 5 Z0Z0Z0Z0 4 0Z0m0Z0Z 3 Z0Z0Z0Z0 2 0Z0Z0Z0Z 1 Z0Z0Z0Z0 8 7 a b c d e f g On va utiliser ici une heuristique proposée par le mathématicien allemand Warnsdorff en 1823 qui a le défaut de ne pas toujours fournir de solution même s’il en existe. Le principe de cette heuristique est le suivant : • à chaque case c de l’échiquier, on affecte un poids égal au nombre de cases accessibles à partir de c , • lorsque l’on parcourt l’échiquier, on choisit les cases de poids minimal. L’idée est donc de parcourir l’échiquier en partant de la périphérie et en se rapprochant du centre. On pourra définir une variable globale deplacement permettant d’obtenir les 8 déplacements possibles d’un cavalier sur l’échiquier (structure de données libre). La variable parcours désigne une structure contenant des entiers égaux à 0 si on n’a pas visité une case (i , j ) et n si on l’a visitée à l’étape n . 1. Écrire une fonction init_poids() renvoyant les poids de chaque case (structure libre). 2. Écrire une fonction Warnsdorff(position, parcours, poids) qui applique l’heuristique à la position, c’est à-à-dire renvoie une case de poids minimal non encore visitée. Prévoir un retour lorsque toutes les cases voisines sont déjà visitées. 3. Écrire une fonction parcourir(position, parcours, poids) qui réalise le parcours : à chaque étape, on utilise l’heuristique pour trouver la position suivante. On s’arrête lorsque l’on a visité toutes les cases et on renvoie le parcours, ou lorsqu’il n’y a plus de case à visiter. 4. Écrire une fonction calcul() renvoyant un parcours de l’échiquier. On pourra tester différentes positions initiales jusqu’à obtenir un résultat. Backtracking Une première idée est de faire parcourir toutes les cases possibles à un cavalier en listant à chaque déplacement les cases parcourues. Lorsque celui-ci ne peut plus avancer on consulte le nombre de cases parcourues. • Si ce nombre est égal à 64 = 8 × 8, alors le problème est résolu. • Sinon, il faut revenir en arrière et tester d’autres chemins. Projet Backtracking 3 : parcours d’échiquier par un cavalier et problème des n dames - page 1 h 1. Écrire une fonction init_casesAccessibles() qui renvoie une structure donnant, pour chaque position, les cases accessibles à partir de cette position. Au cours de la recherche, lorsqu’on déplace le cavalier vers une case, celle-ci doit être retirée des cases accessibles à partir de toute case accessible depuis de la position de la case de départ. 2. Écrire une fonction OccupePosition(position, deplacements, casesAccessibles) qui prend comme argument une position, l’ajoute aux deplacements déjà effectués, puis enlève la position de la liste des case accessible de toutes cases accessibles depuis cette position, et enfin renvoie True si l’une de ces liste est vide (situation critique) et False sinon. 3. Écrire une fonction LiberePosition(deplacements, casesAccessibles) qui récupère le dernier déplacement (c’est-à-dire la position p de la dernière case occupée), l’enlève de la structure deplacements, et rajoute p à toutes les listes des cases accessibles des cases accessibles depuis p . À la fin de cette fonction, deplacements, casesAccessibles seront donc dans le même état qu’avant l’exécution de la fonction OccupePosition à la position p . 4. Écrire une fonction récursive TestePosition(position, deplacements, casesAccessibles) qui • occupe la position, • vérifie si la situation est critique. Si c’est est le cas, ? la fonction vérifiera si 63 cases sont occupées et dans ce cas renverra True pour indiquer que la recherche est terminée. ? Si 63 cases ne sont pas occupées, la fonction libérera la position et renverra False. Dans le cas contraire, ? la fonction testera (appel récursif) toutes les positions des cases accessibles après la position de départ. ? La fonction retournera True dès que l’un des appels à TestePosition retourne True ou li- bérera la position et retournera False si tous les appels à TestePosition(k) retournent False. 5. Écrire une fonction renvoyant une solution au problème initial à partir d’une position initiale donnée. Amélioration Proposer une amélioration en visitant d’abord les cases pour lesquelles le nombre de cases accessibles est minimal. Toutes les solutions Proposer une version renvoyant toutes les solutions (pour une/toutes les positions initiales). Généralisation Généraliser à un échiquier de taille N × M . Visualisation Proposer une interface graphique permettant de visualiser un parcours. Projet Backtracking 3 : parcours d’échiquier par un cavalier et problème des n dames - page 2 Problème des 8 dames Le problème des 8 dames consiste à placer 8 dames sur un échiquier de façon à ce qu’aucune n’en attaque une autre. Voir http://www.hbmeyer.de/backtrack/achtdamen/eight.htm Proposer, par backtracking, une résolution du problème des 8 dames. Projet Backtracking 3 : parcours d’échiquier par un cavalier et problème des n dames - page 3