Approfondissement
Le problème du cavalier est un problème de logique dans lequel on souhaite faire se déplacer un cavalier sur un échiquier avec la contrainte suivante : le cavalier est initialement positionné sur une case de l'échiquier, et il doit effectuer une suite de déplacements de telle sorte qu'il visite chaque case et ne visite jamais une case deux fois.
On représente un échiquier de taille \(n \in \mathbb{N}^{*}\) par un tableau de tableaux, rectangulaire de taille \(n \times n\). On repère la position d'un cavalier par le couple (abscisse, ordonnee)
. Si \(n\) est la taille de l'échiquier, alors les positions possibles pour le cavalier sont les couples \((i, j)\), avec \(0 \leq i < n\) et \(0 \leq j < n\).
Par exemple, pour \(n = 8\), le cavalier de la figure ci-dessous se trouve à la position \((2, 3)\). On rappelle que le cavalier peut se déplacer "en L" aux cases indiquées.
On considérera que la position de départ du cavalier est toujours \((0, 0)\). Un parcours du cavalier sera représenté par une pile, constituée des différentes positions du cavalier au cours du parcours (la dernière position du cavalier se trouvant au sommet de la pile).
-
Soit \(n = 3\). Faire le schéma d'un échiquier de taille \(3 \times 3\) puis numéroter les cases de l'échiquier dans l'ordre dans lequel elles sont visitées par le cavalier lors du parcours correspondant à la pile
pile_parcours
:[Sommet] (1, 2) (2, 0) (0, 1) (2, 2) (1, 0) (0, 2) (2, 1) (0, 0)
-
Le problème du cavalier possède-t-il une solution lorsque \(n = 3\) ? Expliquer.
On utilisera dans la suite des questions les variables globales suivantes. Les fonctions pourront directement modifier la variable plateau
.
1 2 3 4 5 |
|
-
Écrire une fonction
est_dans
qui étant donné une positionpos
détermine si celle-ci est une position compatible avec la taille de l'échiquier.1 2 3 4
def est_dans(pos): """ (int, int) -> bool Détermine si la position pos = (x, y) appartient au plateau """ pass
-
On souhaite obtenir la liste de tous les déplacements à tester à partir d'une position donnée. Il s'agit de toutes les cases accessibles par le cavalier, sans celles par lesquelles le cavalier est déjà passé. On décide d'affecter la valeur
1
à la case d'indice(i ,j)
deplateau
si le cavalier est passé par cette case lors de son parcours. Sinon, cette case stocke-1
.-
Écrire une fonction
est_visitee
qui renvoieTrue
si et seulement si la casepos
a été visitée par le cavalier.1 2 3 4
def est_visitee(pos): """ (int, int) -> bool Détermine si la position pos = (x, y) a déjà été visitée """ pass
-
En déduire une fonction
a_partir_de
qui renvoie une pile constituée des positions accessibles par le cavalier depuis la positionpos
(dans l'état courant de la variableplateau
). On calculera pour cela toutes les cases atteignables par le cavalier à l'aide de la variabledeplacements
et on empilera uniquement celles qui appartiennent au plateau et qui n'ont pas encore été visitées par le cavalier.1 2 3 4 5
def a_partir_de(pos): """ (int, int) -> Pile Renvoie une pile constituée des éléments accessibles depuis pos avec l'état courant du plateau. '""" pass
-
La structure de pile peut être utilisée pour "revenir en arrière". Par exemple, si pile_parcours
est la pile représentant le chemin du cavalier jusqu'à l'étape actuelle, on peut "revenir en arrière" à l'aide de l'instruction pile_parcours.depiler()
: après exécution de cette instruction pile_parcours
représente alors le chemin du cavalier sans la dernière étape.
L'idée de l'algorithme permettant de déterminer un parcours possible du cavalier est la suivante :
- on maintient deux piles :
pile_parcours
: la pile correspondant au parcours du cavalier ;pile_essais
: qui représente la liste des positions à explorer par le cavalier. Sipos
est la valeur au sommet depile_parcours
(la position actuelle du cavalier), alors la valeur au sommet depile_essais
est une pile constituée des positions restantes à explorer à partir depos
, dans l'état actuel de l'échiquier.
- tant que la longueur du chemin du cavalier n'est pas \(n^2\) (le cavalier est alors passé par toutes les cases) :
- soient
pos
etaccessibles
les valeurs présentes au sommet des pilespile_parcours
etpile_accessibles
:- si aucune position n'est accessible depuis
pos
(le chemin suivi par le cavalier abouti à une impasse) : alors il faut revenir en arrière. On indique que la positionpos
est à nouveau accessible, puis on dépilepile_parcours
etpile_accessibles
. On décrémente également la longueur du chemin. - sinon : la nouvelle position est la valeur qui se trouve au sommet de la pile
accessibles
: on la dépile afin de la supprimer de la pile des positions à explorer. Puis on indique que la nouvelle position ne sera plus accessible par la suite (car on s'y rend). On met à jour la longueur du chemin. On empile dans la pilepile_parcours
la nouvelle position ainsi que la liste des positions à explorer à partir de cette nouvelle position selon l'état actuel du plateau.
- si aucune position n'est accessible depuis
- si à un moment
pile_parcours
est vide, c'est que l'on a échoué à trouver une solution en testant toutes les possibilités : il n'y a donc pas de solution au problème.
- soient
Répondre aux questions suivantes dans le cas \(n = 3\).
- Initialement
pile_parcours
est constituée d'un seul élément,(0,0)
.- Décrire la constitution de la
pile_essais
correspondante. - Décrire la constitution du
plateau
correspondant.
- Décrire la constitution de la
- Le cavalier se rend à la position au sommet de la pile au sommet de
pile_essais
. On supprime cette valeur de la pile correspondante en la dépilant.- Quelle est la position du cavalier ?
- Donner la composition de
pile_parcours
,pile_essais
, etplateau
correspondants à cette situation.
-
- Suivre l'exécution de cet algorithme jusqu'au premier dépilement de
pile_parcours
. - Quelle est la prochaine valeur empilée sur
pile_parcours
?
- Suivre l'exécution de cet algorithme jusqu'au premier dépilement de
-
Compléter le code ci-dessous afin d'implémenter l'algorithme décrit dans l'énoncé. Vérifier que le chemin trouvé pour \(n = 5\) est une solution au problème du cavalier.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
def cavalier(): """ () -> Pile Renvoie une pile constituée des mouvements à effectuer pour obtenir un chemin de cavalier passant par toutes les cases du plateau. """ pos = (0,0) # le sommet de la pile parcours contient # la position courante pile_parcours = Pile() pile_parcours.empiler(pos) # initialement le chemin est de longueur 1 longueur_chemin = 1 # le sommet de la pile essais contient # les positions à explorer à partir de la position courante pile_essais = Pile() pile_essais.empiler(...) # on indique la position courante comme visitée plateau[...][...] = 1 while longueur_chemin < n**2: # on récupère l'état courant du cavalier : # sa position se trouve au sommet de la pile parcours # et ses mouvements à explorer au sommet de la pile essais pos = pile_parcours.sommet.valeur accessibles = pile_essais.sommet.valeur # Dans le cas où il est possible de visiter une # case à partir de la position courante if ...: # on choisit la première position à explorer # il ne sera plus nécessaire de l'explorer # par la suite, on la supprime de accessibles pos = ... # on indique que la case pos a été visitée plateau[...][...] = ... longueur_chemin = ... # on met à jour les piles de parcours et d'essais pile_parcours.empiler(...) pile_essais.empiler(...) else: # la position courante est un cul de sac # on met à jour le plateau (la case courante # redevient accessible) ... # on revient à la dernière configuration connue qui # ne menait pas à un cul de sac en dépilant les piles # parcours et essais ... ... # on met à jour la longueur du chemin ... # si jamais la pile du parcours est vide # c'est qu'il n'y a pas de solution au problème if ...: raise IndexError("Pas de solution trouvée au problème") return pile_parcours