Programmer un robot (1)

Série d'exercices

Cet exercice fait partie d'une série :

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 type Robot Ă©voluant dans une grille de 4 cases de haut et 5 de large. Cet objet est affectĂ© Ă  la variable robot ;

  • 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 d'ores et dĂ©jĂ  ignorĂ© ;

  • robot.droite() : fait tourner le robot d'un quart de tour vers la droite ;
  • 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 cherche à programmer 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.

Écrire la fonction execute qui prend en paramùtres un objet de type Robot ainsi qu'une liste d'instructions et fait effectuer chacune de celles-ci par le robot.

On garantit que toutes les instructions sont valides ("A", "D" ou "G").

Exemple
>>> robot = Robot(4, 3)
>>> instructions = ["A", "A", "D", "A", "A", "A", "G", "A"]
>>> execute(robot, instructions)
>>> robot.grille
[['*', '*', '*'], [' ', ' ', '*'], [' ', ' ', '*'], [' ', ' ', '*']]
>>> robot.dessine_parcours()
┌───┐
│***│
│  *│
│  *│
│  >│
└───┘
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.

###(Dés-)Active le code aprÚs la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier
Évaluations restantes : 10/10

.128013AeDv1wSrGc7sq )_ĂčiRa:tĂ 4f3,o6[/hpmngP8y.Ă©{xk5=ul;dI2]}b(050Y0c0w0u0s0W0m0o0k0W0u0m0m0U010w0s0H010406050m0V0I0I0u0i0N040h0C0W0V0|0C0J0o020u0I0H0X0o0t0c160i0n0V0c0m050F13151719110H041x1E051H0F1H1J1E110Y0s0e0;0?0^0`0G0s0K0G0W1X0G0w0 050,0%0W0c1S0@0_011W1Y1!1Y0w1*1,1(0w0%0C0Y191)0i1F0w0G0;1c0m0H0u0J0`0!011.1U010z0.0c0J1k0c1(292b2g1:2j1,2m0I2o040a0o0L0i0C0H0C0m0s1f1h0*270i0i0c0k2J1x2q0J1F0F252V0w2322240Y2s0`1!0J2l2G1(1P1R0=1/2)0s2+0J1 1Q1(0H2O1F2T2V30122a1h2;2h2_0i160W0 0f2S3410332r361:383a0 0!3e2b3g2T2(013l0u3b040A3p2U113s3j0`3v3x0y3A3r343t3G0 0T3J3C3L3E3u0C393w0 0D3Q3h351T3k3V3m040l3!3D3%3F3)3X040M3J1G2~1x2/2Y0Y2$3t0k1 2y0)1Q1F2}0c2 3f3^410*493i3/010S0 0*0z3^3.2=010g0 0o4m3S4g0J0z0 0c0R2P1e0c4t4f4o0~040(4E3$4o0J0 2D1~0w4K3t4H0B3J4s4n370 2@0m2Z0V2Q0s1g1w1y4a4Y1:4H0p0v3Q0o4^4X4u4o4i040z3V4W3#3M0 0u4)1g514/0`0C4q042@584{4Z5d0J4$0i4(2J4,3259014H4?4-3q064_5x4`4F2h4}0s4l5u2U5z4L5h552R5F045H3t0C0 0U0U5f5A1:0m2e04010d014S3T5s4@5y5+523T4N044P0C4R5M5-4g5Q040O5%4v4j2D2I4D5@5q4H0(0p5*5,5q4}0c1!5E305O5.54560J5U5I1:5`5S6l3t5X0 010j5$635g4:0 5t305w5+5y5^4M4O0C4Q5}4o5`5|6x5V3F0 0K0u4(0G625p6y0`65675M6D696Z4h4y0/6X4.6*5)6%6E6F5q5/5;5?6Y6Q016N6L5J0e2b0k6.3q6G2h6#3!0F4c483_7d0F3|1x0w3~7i2!2W1~202Y0u1+7f3|1D4e6m0`2O0I0q0z0u0S0c0q0G0A0 1p1r1t1v0o6B4a1K3g1E0Z0W0o0m0c0i0-0w0o6U0^0s0o2F7(7s0o0Y001e0.0s7X0i0o0V2K0s6j2m0s2O7$0^0C0k0s2b7#0x0;0G0u7M0o4#4%6j0o0?0o0I0P25427W0H0P842j8c0Y0V0o6`7O1G3g2/3t1=1Z1#1%7w3t2u2l2n0 2A0h0k0i0}7#0L0N8m6k5@477w314a7c8H3T4}4k701:5c4s6P7x3u4x044z4B0w752U776z4I8+6R5:6J5=8 5r0 4V5M6g5~5i5k5m4*5j944;7P5v5x8|904 0J6j6q3T6o9q4g4H0Q946s5Z0b6w6|8:6;6f9l3u6I6K8/5P0 6O9C53040u729o8`8%9u969t4o9y5!9B6/6}9E3f996H919J9O9r9M945/0Y608_9g9X989G9!6v9`049i5G9G6_926{9%8:6 9K6h046T6V9U9G4H0$689+5B0 4 0i9Y5J9p9|5q5b4!8W9F6^aw9c6j5oa84T6Aak9kaz4~1gas9/9W040E9=6i5LaN4G0 0#667a8$7e2V7u1I040d2b0:1,0;0@0o0C0r8h005K9e0o1v7#057c8C1#1@1$2p6*9!9A3^7b42040B4s8$9n6j0Ea^1g0#1x8$a`4$0o0P6T7V88a}b96`0O9R7362bl0+7.aLbcb9be2Jbg6jaX6$bA0R8q4CbE4dbvbx9TaYb84d6O1N3{0+0-0/04.

###(Dés-)Active le code aprÚs la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activĂ©, le texte copiĂ© dans le terminal est joint sur une seule ligne avant d'ĂȘtre copiĂ© dans le presse-papier
Évaluations restantes : 10/10

.128013AeDv1wSrGc7sq )_ĂčiRa:tĂ 4f3,o6[/hpmngP8y.Ă©{xk5=ul;dI2]}b(050Y0c0w0u0s0W0m0o0k0W0u0m0m0U010w0s0H010406050m0V0I0I0u0i0N040h0C0W0V0|0C0J0o020u0I0H0X0o0t0c160i0n0V0c0m050F13151719110H041x1E051H0F1H1J1E110Y0s0e0;0?0^0`0G0s0K0G0W1X0G0w0 050,0%0W0c1S0@0_011W1Y1!1Y0w1*1,1(0w0%0C0Y191)0i1F0w0G0;1c0m0H0u0J0`0!011.1U010z0.0c0J1k0c1(292b2g1:2j1,2m0I2o040a0o0L0i0C0H0C0m0s1f1h0*270i0i0c0k2J1x2q0J1F0F252V0w2322240Y2s0`1!0J2l2G1(1P1R0=1/2)0s2+0J1 1Q1(0H2O1F2T2V30122a1h2;2h2_0i160W0 0f2S3410332r361:383a0 0!3e2b3g2T2(013l0u3b040A3p2U113s3j0`3v3x0y3A3r343t3G0 0T3J3C3L3E3u0C393w0 0D3Q3h351T3k3V3m040l3!3D3%3F3)3X040M3J1G2~1x2/2Y0Y2$3t0k1 2y0)1Q1F2}0c2 3f3^410*493i3/010S0 0*0z3^3.2=010g0 0o4m3S4g0J0z0 0c0R2P1e0c4t4f4o0~040(4E3$4o0J0 2D1~0w4K3t4H0B3J4s4n370 2@0m2Z0V2Q0s1g1w1y4a4Y1:4H0p0v3Q0o4^4X4u4o4i040z3V4W3#3M0 0u4)1g514/0`0C4q042@584{4Z5d0J4$0i4(2J4,3259014H4?4-3q064_5x4`4F2h4}0s4l5u2U5z4L5h552R5F045H3t0C0 0U0U5f5A1:0m2e04010d014S3T5s4@5y5+523T4N044P0C4R5M5-4g5Q040O5%4v4j2D2I4D5@5q4H0(0p5*5,5q4}0c1!5E305O5.54560J5U5I1:5`5S6l3t5X0 010j5$635g4:0 5t305w5+5y5^4M4O0C4Q5}4o5`5|6x5V3F0 0K0u4(0G625p6y0`65675M6D696Z4h4y0/6X4.6*5)6%6E6F5q5/5;5?6Y6Q016N6L5J0e2b0k6.3q6G2h6#3!0F4c483_7d0F3|1x0w3~7i2!2W1~202Y0u1+7f3|1D4e6m0`2O0I0q0z0u0S0c0q0G0A0 1p1r1t1v0o6B4a1K3g1E0Z0W0o0m0c0i0-0w0o6U0^0s0o2F7(7s0o0Y001e0.0s7X0i0o0V2K0s6j2m0s2O7$0^0C0k0s2b7#0x0;0G0u7M0o4#4%6j0o0?0o0I0P25427W0H0P842j8c0Y0V0o6`7O1G3g2/3t1=1Z1#1%7w3t2u2l2n0 2A0h0k0i0}7#0L0N8m6k5@477w314a7c8H3T4}4k701:5c4s6P7x3u4x044z4B0w752U776z4I8+6R5:6J5=8 5r0 4V5M6g5~5i5k5m4*5j944;7P5v5x8|904 0J6j6q3T6o9q4g4H0Q946s5Z0b6w6|8:6;6f9l3u6I6K8/5P0 6O9C53040u729o8`8%9u969t4o9y5!9B6/6}9E3f996H919J9O9r9M945/0Y608_9g9X989G9!6v9`049i5G9G6_926{9%8:6 9K6h046T6V9U9G4H0$689+5B0 4 0i9Y5J9p9|5q5b4!8W9F6^aw9c6j5oa84T6Aak9kaz4~1gas9/9W040E9=6i5LaN4G0 0#667a8$7e2V7u1I040d2b0:1,0;0@0o0C0r8h005K9e0o1v7#057c8C1#1@1$2p6*9!9A3^7b42040B4s8$9n6j0Ea^1g0#1x8$a`4$0o0P6T7V88a}b96`0O9R7362bl0+7.aLbcb9be2Jbg6jaX6$bA0R8q4CbE4dbvbx9TaYb84d6O1N3{0+0-0/04.