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

.128013aGu ;x5DS}o3_[q,)yv4Ă :Ăčdfmg=Awprsh68kbc(it]R{e7/PĂ©nl.I12050y0U0Q0b0P0!0H0e0N0!0b0H0H0C010Q0P0F010406050H0d0A0A0b0G0s040j0l0!0d0|0l0Z0e020b0A0F0f0e0S0U160G0p0d0U0H050W13151719110F04051E1x1H0W1E110y0P0t0;0?0^0`0I0P0B0I0!1V0I0Q0 050,0M0!0U1Q0@0_011U1W1Y1W0Q1(1*1$0Q0G1F0Q0I0;1c0H0F0b0Z0`0(011,1S010z0.0U0Z1k0U1$2224291.2c1*2f0A2h040a0e0X0G0l0F0l0H0P1f1h0*200G0G0U0N2C1x2j0Z1F0W1~2O1{1}1|1%0y2l0`1Y0Z2e2z1$1N1P0=1-2Y0P2!0Z0l2(1$0F2H1F2M2O2_12231h2*2a2/0G160!0 0%2L2}102|2k2 1.31330 0(3724392M2X013e0b34040m3i2N113l3c0`3o3q0u3t3k2}3m3z0 0h3C3v3E3x3n0l323p0 0J3J3a2~1R3d3O3f040V3T3w3W3y3Y3Q040K3C1I2@1x2(2R0y1}2W3M0N2:2r0)1O1F2?0U2^383.3{0*433b3(010L0 0*0z3.3%2+010E0 0e4g3L4a0Z0z0 0U0g2I1e0U4n494i0~040O4y3V4i0Z0 2w0M0l0Q4E3m4B0q3C4m4h300 2-0H1{0d2J0P1g1w1y444T1.4B0r0w3J0e4:4S4o4i4c040z3O4R3U3F0 0b4!1g4|4*0`0l4k042-534?4U580Z4X0G4Z2C4%2{54014B4.4(3j064;5s4=4z2a4^0P4f5p2N5u4F5c502K5A045C3m0l0 0C0C5a5v1.0H2704021t4L0f0i5V0d5X4N3M5n4/5t5+4}3M4H044J4L5%4a5L040#5?4G4d2w2B4x5H5-4a4B0O0r5*5,5l4^0U1Y5z2_5J5.4 510Z5P5D1.5^5N6k3m5S0 5!5X0c6t0Q0f5{2a5)5H5r5+5t625|5:0l4K4M615l5^5`6M5b3d0 0B0b4Z0I605k6R0`64666C6E6f4a6a0/6Y4)6!5m0 5o2_6D6)4:6G5c5;6L6Z5Q550 6P6 6l3y4 0t240N6.3j6{4+0 653T0W46423/7k0W3=1x0Q3@7p2U2P0b1)7m3=1D4875012H0A0n0z0b0L0U0n0I0m0 1p1r1t1v0e6?441K391E0$0!0e0H0U0G0-0Q0e6V0^0P0e2y7,7w0e0y001e0.0P7#0G0e0d2D0P6i2f0P2H7*0^0l0N0P247)0v0;0I0b7Q0e4W4Y6i0e0?0e0A0Y1~3|7!0F0Y882c8g0y0d0e6}7S1I392(3m1:1X1Z1#7A3m2n2e2g0 2t0j0N0G0}7)0X0s8q6j61417A2`447j8L3M4^4e6z1.574m6Q703n4r044t4v0Q7b2N7d6#7f8/766I6K936;044Q5H6*6H8i5g6i5j6/8@4,7T5q5s903n0 4`0Z6i6p3M6n9u630 0T976r5U5W6x0D6w6y8?7B6B6e9o5/6}976O975/0b789s8~8+9y999x4i9C9H5Z9E9I744O6=9#6|6J5=9J5K729S5}0l5 974P9/5R5T9H6v9*9}9.9b9N4I9;6~9i7B9R9?6g046U6W9X9o4B0k679c5w9q4{a75l9T9tat6:564V8!9MauaA5f5h4#5ea5049l3u6)a84_1gaw9,5(0 0o9_045FaHaJ0R7g6C1x8*7l2O7y1G040i240:1*0;0@0e0l0x8l00aY1g0e1v7)057j8G1Z1=1!2i6:9%9*9Ga4617i3|994m8*9r6i0oa}0Z0Ra(bga 4X0e0Y6U7Z8cb2bg6}0#9U79608*a 7)0yaQbibgbk2Cbm6ia#6%bG4t8u4wbL47bBbD9Wa$8*6P1L3;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

.128013aGu ;x5DS}o3_[q,)yv4Ă :Ăčdfmg=Awprsh68kbc(it]R{e7/PĂ©nl.I12050y0U0Q0b0P0!0H0e0N0!0b0H0H0C010Q0P0F010406050H0d0A0A0b0G0s040j0l0!0d0|0l0Z0e020b0A0F0f0e0S0U160G0p0d0U0H050W13151719110F04051E1x1H0W1E110y0P0t0;0?0^0`0I0P0B0I0!1V0I0Q0 050,0M0!0U1Q0@0_011U1W1Y1W0Q1(1*1$0Q0G1F0Q0I0;1c0H0F0b0Z0`0(011,1S010z0.0U0Z1k0U1$2224291.2c1*2f0A2h040a0e0X0G0l0F0l0H0P1f1h0*200G0G0U0N2C1x2j0Z1F0W1~2O1{1}1|1%0y2l0`1Y0Z2e2z1$1N1P0=1-2Y0P2!0Z0l2(1$0F2H1F2M2O2_12231h2*2a2/0G160!0 0%2L2}102|2k2 1.31330 0(3724392M2X013e0b34040m3i2N113l3c0`3o3q0u3t3k2}3m3z0 0h3C3v3E3x3n0l323p0 0J3J3a2~1R3d3O3f040V3T3w3W3y3Y3Q040K3C1I2@1x2(2R0y1}2W3M0N2:2r0)1O1F2?0U2^383.3{0*433b3(010L0 0*0z3.3%2+010E0 0e4g3L4a0Z0z0 0U0g2I1e0U4n494i0~040O4y3V4i0Z0 2w0M0l0Q4E3m4B0q3C4m4h300 2-0H1{0d2J0P1g1w1y444T1.4B0r0w3J0e4:4S4o4i4c040z3O4R3U3F0 0b4!1g4|4*0`0l4k042-534?4U580Z4X0G4Z2C4%2{54014B4.4(3j064;5s4=4z2a4^0P4f5p2N5u4F5c502K5A045C3m0l0 0C0C5a5v1.0H2704021t4L0f0i5V0d5X4N3M5n4/5t5+4}3M4H044J4L5%4a5L040#5?4G4d2w2B4x5H5-4a4B0O0r5*5,5l4^0U1Y5z2_5J5.4 510Z5P5D1.5^5N6k3m5S0 5!5X0c6t0Q0f5{2a5)5H5r5+5t625|5:0l4K4M615l5^5`6M5b3d0 0B0b4Z0I605k6R0`64666C6E6f4a6a0/6Y4)6!5m0 5o2_6D6)4:6G5c5;6L6Z5Q550 6P6 6l3y4 0t240N6.3j6{4+0 653T0W46423/7k0W3=1x0Q3@7p2U2P0b1)7m3=1D4875012H0A0n0z0b0L0U0n0I0m0 1p1r1t1v0e6?441K391E0$0!0e0H0U0G0-0Q0e6V0^0P0e2y7,7w0e0y001e0.0P7#0G0e0d2D0P6i2f0P2H7*0^0l0N0P247)0v0;0I0b7Q0e4W4Y6i0e0?0e0A0Y1~3|7!0F0Y882c8g0y0d0e6}7S1I392(3m1:1X1Z1#7A3m2n2e2g0 2t0j0N0G0}7)0X0s8q6j61417A2`447j8L3M4^4e6z1.574m6Q703n4r044t4v0Q7b2N7d6#7f8/766I6K936;044Q5H6*6H8i5g6i5j6/8@4,7T5q5s903n0 4`0Z6i6p3M6n9u630 0T976r5U5W6x0D6w6y8?7B6B6e9o5/6}976O975/0b789s8~8+9y999x4i9C9H5Z9E9I744O6=9#6|6J5=9J5K729S5}0l5 974P9/5R5T9H6v9*9}9.9b9N4I9;6~9i7B9R9?6g046U6W9X9o4B0k679c5w9q4{a75l9T9tat6:564V8!9MauaA5f5h4#5ea5049l3u6)a84_1gaw9,5(0 0o9_045FaHaJ0R7g6C1x8*7l2O7y1G040i240:1*0;0@0e0l0x8l00aY1g0e1v7)057j8G1Z1=1!2i6:9%9*9Ga4617i3|994m8*9r6i0oa}0Z0Ra(bga 4X0e0Y6U7Z8cb2bg6}0#9U79608*a 7)0yaQbibgbk2Cbm6ia#6%bG4t8u4wbL47bBbD9Wa$8*6P1L3;0+0-0/04.