Programmer un robot (2)
Série d'exercices
Cet exercice fait partie d'une série :
-
« Programmer un robot (0) »,
-
« Programmer un robot (1) »,
-
« Programmer un robot (2) »,
-
« Programmer un robot (3) »,
-
« Programmer un robot (4) ».
Rappels sur le fonctionnement du Robot
On considÚre dans cet exercice un robot se déplaçant sur une grille de dimensions finies. Initialement, il se trouve sur la case en haut à gauche de la grille et est dirigé vers la droite.
Ce robot est représenté en Python par un objet de la classe Robot
. L'interface de la classe Robot
est la suivante :
-
robot = Robot(4, 5)
: instancie un objet de typeRobot
évoluant dans une grille de 4 cases de haut et 5 de large. Cet objet est affecté à la variablerobot
; -
robot.avance()
: fait avancer le robot d'une case dans la direction actuelle. Un déplacement qui ferait sortir le robot de la grille est ignoré ; robot.droite()
: fait tourner le robot d'un quart de tour vers la gauche ;robot.gauche()
: fait tourner le robot d'un quart de tour vers la gauche ;robot.dessine_parcours()
: affiche la grille, les cases déjà parcourues et la position actuelle du robot dans la console.
Un objet de type Robot
contient aussi un attribut grille
. Il s'agit d'une liste de listes gardant la trace des cases visitées (marquées par "*"
) ou non (laissées vides " "
).
Exemple d'utilisation d'un Robot
>>> robot = Robot(4, 3) # la grille fait 4 case de haut sur 3 de large
>>> robot.grille # le robot est en haut Ă gauche
[['*', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
>>> robot.dessine_parcours() # le robot pointe vers la droite
âââââ
â> â
â â
â â
â â
âââââ
>>> robot.avance()
>>> robot.avance()
>>> robot.droite()
>>> robot.avance()
>>> robot.avance()
>>> robot.avance()
>>> robot.dessine_parcours()
âââââ
â***â
â *â
â *â
â vâ
âââââ
>>> robot.gauche()
>>> robot.avance()
>>> robot.dessine_parcours()
âââââ
â***â
â *â
â *â
â >â
âââââ
>>> robot.grille
[['*', '*', '*'], [' ', ' ', '*'], [' ', ' ', '*'], [' ', ' ', '*']]
La classe Robot
MOUVEMENTS = ((0, 1), (1, 0), (0, -1), (-1, 0))
class Robot:
def __init__(self, hauteur, largeur):
self.hauteur = hauteur
self.largeur = largeur
self.grille = [[" " for _ in range(largeur)] for _ in range(hauteur)]
self.i = 0
self.j = 0
self.grille[self.i][self.j] = "*"
self.direction = 0
def avance(self):
"""Fait avancer le robot d'une case (seulement si possible)"""
di, dj = MOUVEMENTS[self.direction]
if 0 <= self.i + di < self.hauteur and 0 <= self.j + dj < self.largeur:
self.i += di
self.j += dj
self.grille[self.i][self.j] = "*"
def droite(self):
"""Fait tourner le robot d'un quart de tour vers la droite"""
self.direction = (self.direction + 1) % 4
def gauche(self):
"""Fait tourner le robot d'un quart de tour vers la gauche"""
self.direction = (self.direction - 1) % 4
def dessine_parcours(self):
"""Affiche les cases parcourues et la position actuelle du robot"""
affichage = [
["" for _ in range(self.largeur + 2)] for _ in range(self.hauteur + 2)
]
for j in range(1, self.largeur + 1):
affichage[0][j] = "â"
affichage[-1][j] = "â"
for i in range(1, self.hauteur + 1):
affichage[i][0] = "â"
affichage[i][-1] = "â"
affichage[0][0] = "â"
affichage[-1][0] = "â"
affichage[0][-1] = "â"
affichage[-1][-1] = "â"
for i in range(self.hauteur):
for j in range(self.largeur):
affichage[1 + i][1 + j] = self.grille[i][j]
affichage[self.i + 1][self.j + 1] = [">", "v", "<", "^"][self.direction]
print("\n".join("".join(ligne) for ligne in affichage))
La classe Robot
est déjà chargée dans l'éditeur, vous pouvez l'utiliser sans l'importer.
On programme ce robot en lui faisant effectuer différentes actions :
- le code
"A"
fait avancer le robot ; - le code
"D"
le fait tourner vers la droite ; - le code
"G"
le fait tourner vers la gauche.
Fonction decoupe
On fournit une fonction decoupe
rudimentaire permettant de transformer une chaĂźne de caractĂšres formant une suite d'instructions en une liste.
def decoupe(instructions):
return [int(c) if c.isnumeric() else c for c in instructions]
decoupe("A3GD5") # renvoie ['A', 3, 'G', 'D', 5]
Cette fonction est déjà importée dans l'éditeur. Vous pouvez l'utiliser pour écrire des tests personnels.
Cette fonction ne gÚre que les entiers positifs strictement inférieurs à 10 et ne filtre pas les instructions incorrectes. Ainsi decoupe("Z12")
renvoie ['Z', 1, 2]
.
Le premier exercice de la série a pour objectif d'écrire une version valide de cette fonction.
Le fonctionnement détaillé ci-dessus et utilisé dans le premier exercice nécessite d'écrire de longues séries d'instructions. Afin de les alléger, on autorise aussi des suites d'instructions du type [action, n]
dans lesquelles :
action
est une des trois actions décrites ci-dessus ;n
est un nombre entier positif ou nul.
Ainsi, les instructions ["A", 8, "D", 41, "A"]
ont pour effet :
- de faire avancer le robot de 8 cases,
- de faire tourner le robot sur la droite 41 fois,
- de faire enfin avancer le robot d'une seule case.
Lors d'une répétition, on exécute un maximum d'actions valides. Si par exemple, le robot ne peut avancer que de 5 cases, les instructions ["A", 8]
le feront avancer de 5 cases, seules les trois derniÚres actions seront ignorées.
Notez-bien que les instructions simples (non suivies d'un nombre entier) restent valides.
Ăcrire la fonction execute
qui prend en paramĂštres un objet de type Robot
et une liste d'instructions et fait effectuer chacune de ces instructions par le robot.
On garantit que toutes les instructions sont valides ("A"
, "D"
ou "G"
ou un entier positif ou nul) et que la suite d'instructions est bien formée (chaque entier suit une action valide).
Exemple
>>> robot = Robot(4, 5)
>>> instructions = ["A", 8, "D", 41, "A"]
>>> execute(robot, instructions)
>>> robot.dessine_parcours()
âââââââ
â*****â
â vâ
â â
â â
âââââââ
Aide
On pourra construire le code autour d'une boucle qui lit les instructions tant que l'on n'a pas atteint la fin de la liste.
La premiĂšre instruction est toujours une action ("A"
, "D"
ou "G"
).
Si l'instruction suivante est un entier, il faut répéter cette action autant de fois que nécessaire. Dans le cas contraire, on effectue l'action une seule fois.
On rappelle que si la variable n
a pour valeur un nombre entier, l'instruction isinstance(n, int)
est évaluée à True
.
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
# Tests
(insensible Ă la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)