Programmer un robot (2)

Série d'exercices

Cet exercice fait partie d'une série :

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 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 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.

###(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

.128013bq,9vi3o_x;G}lpwf( g0]6-)2As1+8ené4[m5tRPhk:c.a=ryDSIu/7{d050*0G0N0V0g0o0C0t0T0o0V0C0C0W010N0g0p010406050C0$0L0L0V0X0Y040!0i0o0$0~0i0H0t020V0L0p0l0t0O0G180X0c0$0G0C050%1517191b130p04051G1z1J0%1G130*0g0f0?0^0`0|0Q0g0u0Q0o1X0Q0N11050.0b0o0G1S0_0{011W1Y1!1Y0N1*1,1(0N0X1H0N0Q0?1e0C0p0V0H0|0A011.1U010r0:0G0H1m0G1(24262b1:2e1,2h0L2j040a0t0P0X0i0p0i0C0g1h1j0,220X0X0G0T2E1z2l0H1H0%202Q1}1 1~1)0*2n0|1!0H2g2B1(1P1R0@1/2!0g2$0H0i2*1(0p2J1H2O2Q2{14251j2,2c2;0X180o110t0D2N2 122~2m311:3335370A3a263c2O2Z013h0V36040t0h3l2P133o3f0|3r3t0t0J3x3n2 3p3D370M3H3z3J3B3q0i343s370x3O3d301T3g3T3i3u0(3Y3A3#3C3%3V3u0F3+3Q3-3S3U3E0e3?3e3^3L040D0v3}3!2-3_3(0D391A3b3P3~46400D3k4b3m4d45323/3t0D3w4j3y3Z3K4o110D3G4s3I4e4n3`4x3N4A4l4v4E413X4H4u3R4g3*4N3,4f4w413=4S3@4U4K0D3|4Y4C3$4K0A434A1K2_1z2*2T0*1 2Y3R0T2=2t0+1Q1H2^0G2`3b3H054{0,534)0|0R110,0r554T2c0q375g4Z320r110G0k2K1g0G5l5a0110040s5v4m3g112y0b0i0N5B3p5y0d3H0t4O3 112/0C1}0$2L0g1i1y4.5h1:5y0z0S3O0t5+5O5#3C5R5N5P460i110W5;5.010L0g114-2{065,5-5m1:5c040q1W1,5`655/040g6c5w5@04020o0N0l6h5C3C0b112q5J3R5y5A5!6d3q5R0H5T0X5V2E5Z2}5{5%5)4H63635=2c0T0D11030t1,2L0$2J0t2G0o02030h0e0l5S5U5W1i5*6O5+6Q5D6f6D5U6p3p6j5_4A645w0H6C6E6G5X6D6v3^5y0K794f5:6z5w5y0w6;6=6@5b5R5f707m6B6f6|3R6j6l6n7u3 6s046u7g6q5x116y6J6A736_756/787E5K110z7z5?110y7U2c5}4x7Y1:0i5j04260*7$6r5R2C6`260T5u7Q6w7H7d32746.6H7{5$117c7^5Q7t7q5{6j0E7-5|5~41800|7i5M877K7B2/5I84466L7k6=5,7r7L0H8b6~8b7L6-6F7O6I546K828f7s6g8j6i118a8L7F7!8e8o2c7i8r8s717F8A8x8N6 2{8Y3p8R4i618X7r670G0;7@7J7h116M8-8X8t5{8v8#048%3b8)3R8R4a8{8|94858K8(7r89923m9a46963O628s7r6S6U0t5q0I0T1g776#1-6%6)6+8B766:6N9n5{670r3T8z110j8x7)2/8z7B0X260u8?8F6A6x8I8 8T81045(8W9G6A670g7p9d8~7}6F900W9g2P9i2c0C296k1v5H0l0Z02a07y9$8g8_9*998u5E0i5G8n8@7F6j0U9!5d2y2D9W3m7r6x7T9Fab9H5p1!9/93ac7M6{8P6}5^9_3u7r9}11a40$a10maMa18I5y8`4c997l9;045F5H8Iajal040u0V5V0Qap2Par7Hat988|8/ax0Ca.597FaTaaa@aYa!ag9X8M04aka77s0V0f7=a{a:5za=4c1z57524/bk0%4=1z0N4@bp2W2R0V1+bm4=1Fa|3p2J0L0j0r0V0R0G0j0Q0h111r1t1v1x0taUaq1M3c1G0#0o9r5T0ta+0`0g0t2Ab%bw6#000$0H9.0g0G0X6WbQ9J3U0N0=0,0=9C8D9r1j2^0i0T0I0*260Nb#5Sb(6M1N4~2+3^1=1Z1#1%bA3R2p2g2i112v0!0T0X0 ca0P0Y209E2}51a|2|54bjcn3^675e8I7)5Ob80H5o045q5s0Nbd8G5za(b2aS118i9:7K9=9D7Pah7R9(bS3y6PaY250g2J8E9h9e5^8b7b7jau9{6^9caA88c~aE958d60aV8}9,11692f9L86c)b57x6od97A6t2gc$cZcQc+8Ddt9)d2c?c*aC9?dp7V91djc07 b87ba(d5aqcYd1a?dB5w9-azc|aYdO9`c}6k6mdodl8Z7B7Dc.7_dud-9b6`8CdKd:8p7S907XdF7Z8d97d66A7(117+9R7/6-bcdt7Ib48Zdwd@ecc/83d^7|dke1b58Od)8*d dt0wc(emd*6Cb3dP9Y7Sc;12aWd36ec^c`a$11b7ej6^0V0p0p2g7,dL7Hebez72c+dteudXdCdJ77c{a/cYeieg4P7fep7v8N8b9keT040w0zbg4keEaBdZaId7040EaHeF8c118,ddaXdfcT8=dteC9mavdCeH1xeJb6a(eOeQ0HeSeMa85zeVe*e$d=0$eZe?ere^e{a dTedele#enf57re@61fhf67L9J0H7O9@c 110)8IaK9 aN6n0BaQa6ft7G04eCfRadaffmeLe-85bae9e^e!d!5{f#f*a2g1ffdjc#b8a%dv040*ancWfBd}1:g0a50laPgig4gf6eg6f,g8f,7La*a,cXeA040n8ra^04b`dj0VfVf|dj8wgn01e36_djfke)cJd_f.a gBgDgK7L9NgKgM9QgX9S9Ugw8^d/f^7e04gJf,8qdA9+eXgC1igGg;8Hg9gF2Me^0w0se|4t0%cIbl2Qby4;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

.128013bq,9vi3o_x;G}lpwf( g0]6-)2As1+8ené4[m5tRPhk:c.a=ryDSIu/7{d050*0G0N0V0g0o0C0t0T0o0V0C0C0W010N0g0p010406050C0$0L0L0V0X0Y040!0i0o0$0~0i0H0t020V0L0p0l0t0O0G180X0c0$0G0C050%1517191b130p04051G1z1J0%1G130*0g0f0?0^0`0|0Q0g0u0Q0o1X0Q0N11050.0b0o0G1S0_0{011W1Y1!1Y0N1*1,1(0N0X1H0N0Q0?1e0C0p0V0H0|0A011.1U010r0:0G0H1m0G1(24262b1:2e1,2h0L2j040a0t0P0X0i0p0i0C0g1h1j0,220X0X0G0T2E1z2l0H1H0%202Q1}1 1~1)0*2n0|1!0H2g2B1(1P1R0@1/2!0g2$0H0i2*1(0p2J1H2O2Q2{14251j2,2c2;0X180o110t0D2N2 122~2m311:3335370A3a263c2O2Z013h0V36040t0h3l2P133o3f0|3r3t0t0J3x3n2 3p3D370M3H3z3J3B3q0i343s370x3O3d301T3g3T3i3u0(3Y3A3#3C3%3V3u0F3+3Q3-3S3U3E0e3?3e3^3L040D0v3}3!2-3_3(0D391A3b3P3~46400D3k4b3m4d45323/3t0D3w4j3y3Z3K4o110D3G4s3I4e4n3`4x3N4A4l4v4E413X4H4u3R4g3*4N3,4f4w413=4S3@4U4K0D3|4Y4C3$4K0A434A1K2_1z2*2T0*1 2Y3R0T2=2t0+1Q1H2^0G2`3b3H054{0,534)0|0R110,0r554T2c0q375g4Z320r110G0k2K1g0G5l5a0110040s5v4m3g112y0b0i0N5B3p5y0d3H0t4O3 112/0C1}0$2L0g1i1y4.5h1:5y0z0S3O0t5+5O5#3C5R5N5P460i110W5;5.010L0g114-2{065,5-5m1:5c040q1W1,5`655/040g6c5w5@04020o0N0l6h5C3C0b112q5J3R5y5A5!6d3q5R0H5T0X5V2E5Z2}5{5%5)4H63635=2c0T0D11030t1,2L0$2J0t2G0o02030h0e0l5S5U5W1i5*6O5+6Q5D6f6D5U6p3p6j5_4A645w0H6C6E6G5X6D6v3^5y0K794f5:6z5w5y0w6;6=6@5b5R5f707m6B6f6|3R6j6l6n7u3 6s046u7g6q5x116y6J6A736_756/787E5K110z7z5?110y7U2c5}4x7Y1:0i5j04260*7$6r5R2C6`260T5u7Q6w7H7d32746.6H7{5$117c7^5Q7t7q5{6j0E7-5|5~41800|7i5M877K7B2/5I84466L7k6=5,7r7L0H8b6~8b7L6-6F7O6I546K828f7s6g8j6i118a8L7F7!8e8o2c7i8r8s717F8A8x8N6 2{8Y3p8R4i618X7r670G0;7@7J7h116M8-8X8t5{8v8#048%3b8)3R8R4a8{8|94858K8(7r89923m9a46963O628s7r6S6U0t5q0I0T1g776#1-6%6)6+8B766:6N9n5{670r3T8z110j8x7)2/8z7B0X260u8?8F6A6x8I8 8T81045(8W9G6A670g7p9d8~7}6F900W9g2P9i2c0C296k1v5H0l0Z02a07y9$8g8_9*998u5E0i5G8n8@7F6j0U9!5d2y2D9W3m7r6x7T9Fab9H5p1!9/93ac7M6{8P6}5^9_3u7r9}11a40$a10maMa18I5y8`4c997l9;045F5H8Iajal040u0V5V0Qap2Par7Hat988|8/ax0Ca.597FaTaaa@aYa!ag9X8M04aka77s0V0f7=a{a:5za=4c1z57524/bk0%4=1z0N4@bp2W2R0V1+bm4=1Fa|3p2J0L0j0r0V0R0G0j0Q0h111r1t1v1x0taUaq1M3c1G0#0o9r5T0ta+0`0g0t2Ab%bw6#000$0H9.0g0G0X6WbQ9J3U0N0=0,0=9C8D9r1j2^0i0T0I0*260Nb#5Sb(6M1N4~2+3^1=1Z1#1%bA3R2p2g2i112v0!0T0X0 ca0P0Y209E2}51a|2|54bjcn3^675e8I7)5Ob80H5o045q5s0Nbd8G5za(b2aS118i9:7K9=9D7Pah7R9(bS3y6PaY250g2J8E9h9e5^8b7b7jau9{6^9caA88c~aE958d60aV8}9,11692f9L86c)b57x6od97A6t2gc$cZcQc+8Ddt9)d2c?c*aC9?dp7V91djc07 b87ba(d5aqcYd1a?dB5w9-azc|aYdO9`c}6k6mdodl8Z7B7Dc.7_dud-9b6`8CdKd:8p7S907XdF7Z8d97d66A7(117+9R7/6-bcdt7Ib48Zdwd@ecc/83d^7|dke1b58Od)8*d dt0wc(emd*6Cb3dP9Y7Sc;12aWd36ec^c`a$11b7ej6^0V0p0p2g7,dL7Hebez72c+dteudXdCdJ77c{a/cYeieg4P7fep7v8N8b9keT040w0zbg4keEaBdZaId7040EaHeF8c118,ddaXdfcT8=dteC9mavdCeH1xeJb6a(eOeQ0HeSeMa85zeVe*e$d=0$eZe?ere^e{a dTedele#enf57re@61fhf67L9J0H7O9@c 110)8IaK9 aN6n0BaQa6ft7G04eCfRadaffmeLe-85bae9e^e!d!5{f#f*a2g1ffdjc#b8a%dv040*ancWfBd}1:g0a50laPgig4gf6eg6f,g8f,7La*a,cXeA040n8ra^04b`dj0VfVf|dj8wgn01e36_djfke)cJd_f.a gBgDgK7L9NgKgM9QgX9S9Ugw8^d/f^7e04gJf,8qdA9+eXgC1igGg;8Hg9gF2Me^0w0se|4t0%cIbl2Qby4;0-0/0;04.