Rapport sur le problème détective Pikaptcha épisode 1 et 2







Mathieu Chedas

26/01/2023



TABLE DES MATIERES




  1. Episode 1

  2. Episode 2






Episode 1



Présentation du problème

Le problème codingame Detective Pikaptcha est un problème de labyrinthe, inspiré de l'univers de pokemon. Comme son nom l'indique, il fait réference au célèbre pokemon Pikachu. Le but du premier épisode de ce problème est de faire l'analyse d'un labyrinthe. Le principe est de retourner un labyrinthe similaire à celui que l'on nous fourni, mais en modifiant certaines cases et en inscrivant sur ces dernières le nombre de passages qui leurs sont adjacent. On considère qu'un passage peut avoir un maximum de 4 cases adjacentes (on ne considère pas les cases en diagonales commes adjacentes). Pour résoudre ce problème, le site codingame nous fourni un certain nombre d'entrée.

Méthode de résolution


Description de la stratégie

La stratégie avec laquelle j'ai abordé ce problème se décompose en plusieurs parties. Tout d'abord, j'ai récupéré les entrées données par le site Codingame, j'ai donc voulu stocker le labyrinthe dans un tableau à 2 dimentions afin de pouvoir le manipuler de façon simple par la suite. J'ai également récupéré les valeurs de hauteur et longueur du labyrinthe.
Une fois cela fais, je me suis occupé de la boucle principale du programme. Elle consiste en parcourir toutes les cases du tableau et à vérifier si elles sont adjacentes à d'autres passages. J'ai donc éliminé de cette boucle les cases étant des murs (puisqu'il ne fallait pas changer leur valeur). Ensuite j'ai regardé de chaque coté de chaque cases pour vérifier les élements qui les entouraient. A la fin de chaque vérification, j'ai modifié la valeur de départ de la case traité, en fonction des résltats de la boucle.
Enfin, lorsque toutes les cases étaient traitées, il ne me restait plus qu'à afficher le résultat sous la forme d'un tableau en deux dimensions, avec les valeurs modifiées à l'intérieur.

Détail algorithmique de la méthode

Penchons nous désormais plus en détail sur le code qui m'a permis de résoudre ce problème. J'ai écris ce code en C, en utilisant uniquement la fonction main car il était assez court et ne nécessitait pas de séparation du code en plusieurs parties. J'ai donc commencé par initialiser mes variables, qui sont toutes des entiers, sauf le tableau à deux dimensions qui utilisera le type caractère char. Puis j'ai créé une boucle afin de récupérer les données du labyrinthe. En effet, comme le site Codingame nous fourni le labyrinthe ligne par ligne, il est important de stocker les lignes une à une dans le tableau à deux dimensions.
Par la suite, on crée une double boucle for qui permettra de parcourir toutes les cases du labyrinthe sans en oublier. En effet, si on définit les coordonnées d'une case dans le labyrinthe sous la forme (i,k), alors on parcour grace à cette boucle tout les couples (i,k) possibles. On considère ici que la valeur maximale de i est la hauteur du labyrinthe, et la valeur maximale de k, est la longueur du labyrinthe.
Alors pour chaque case du labyrinthe on va vérifier plusieurs conditons à l'aide de l'instruction if. Tout d'abord on vérifie que la case que l'on traite est un passage. Puis, si c'est un passage, pour ne pas avoir d'erreurs liées aux index des cases que l'on regarde, on va vérifier si la case n'est pas sur un bord. Si tel est le cas, alors on ne regardera pas le coté qui correspond au bord.

Exemple:

Si on observe la case rouge, on se rend compte qu'elle se situt sur un bord, alors pour ne pas qu'il y ai d'erreur, le programme va d'abord détecter sur quel bord elle se trouve, et donc n'observer que les cases grisées. Alors que la case bleue elle, n'est pas sur un bord. Le programme va donc regarder toutes les cases grisées autour d'elle afin de détecter si elles correspondent à un passage, ou à un mur.

Enfin on incrémente de 1 la case que l'on observe, si le test est passé, et ce pour chacun des 4 tests. A la fin du programme on réalise une double boucle for afin d'afficher la solution de la manière attendu. On va parcourir chaque ligne du tableau, et afficher chaque élement un à un. Une fois une ligne affiché, on effectue un retour à la ligne, puis on réitère l'opération pour la ligne suivante.

Si dessous, voici une version en pseudo code de la boucle principale de mon programme, afin de mieux comprendre son fonctionnement.

Pour i allant de 0 à la valeur de la hauteur du labyrinthe
    Pour k allant de 0 à la valeur de la longueur du labyrinthe

        Si la case de coordonnées (i,k) est un 0

            Si la case (i,k) n'est pas sur le bord gauche
            Et que la case à sa gauche n'est pas un mur

                Alors on ajoute 1 à la valeur de (i,k)

            Si la case (i,k) n'est pas sur le bord droit
            Et que la case à sa droite n'est pas un mur

              Alors on ajoute 1 à la valeur de (i,k)

            Si la case (i,k) n'est pas sur la dernière ligne
            Et que la case du dessous n'est pas un mur

              Alors on ajoute 1 à la valeur de (i,k)

            Si la case (i,k) n'est pas sur la première ligne
            Et que la case du dessus n'est pas un mur

              Alors on ajoute 1 à la valeur de (i,k)

Code commenté ayant résolu le problème

Voici ci dessous ma proposition de code commenté pour résoudre le problème Pikaptcha épisode 1.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

// Fonction principale

int main()
{

// Initialisation des variables

    int width;                                 // Longueur du labyrinthe
    int height;                             // Hauteur du labyrinthe
    int i;
    int k;
    scanf("%d%d", &width, &height);    // Récupération des données correspondantes
    char table[256][256];              // Création d'un tableau à deux dimentions


// Récupération du labyrinthe dans un tableau à deux dimentions

    for (i = 0; i < height; i++) {
        char line[256];
        scanf("%s", line);
        for (k = 0; k < width; k++) {
            table[i][k] = line[k];
        }
    }

// Boucle principale

    for (int i = 0; i < height; i++) {  // i l'indice de l'ordonnée dans le tableau de la case traitée dans
        for (int k = 0; k < width; k++){   // k l'indice de l'abscisse dans le tableau de la case traitée dans
            if(table[i][k] == '0'){       // test si la case que l'on regarde est un passage

                if((i != 0)&&(table[i-1][k] != '#')){  // test si la case du dessus est un passage
                    table[i][k] ++;
                }

                if((i != height-1)&&(table[i+1][k] != '#')){ // test si la case du dessous est un passage
                    table[i][k] ++;
                }

                if((k != 0)&&(table[i][k-1] != '#')){   // test si la case de gauche est un passage
                    table[i][k] ++;
                }

                if ((k != width-1)&&(table[i][k+1] != '#')){   // test si la case de droite est un passage
                    table[i][k] ++;
                }
            }
            }
        }

// Affichage de la solution

for (i = 0; i < height; i++) {
        for (k = 0; k < width; k++){
            printf("%c", table[i][k]);
        }
        printf("\n");
    }

    return 0;
}


Présentation des tests

Afin de valider notre réponse, le site Codingame effectu différents test. Ils peuvent également nous aider à résoudre le problème. Alors voici ci dessous les entrées et sorties de différents test effectués, accompagnés d'une remarque si le test a aidé à la réalisation d'une partie spécifique du programme.

Entrée standard

> 0000#
> #0#00
> 00#0#
Sortie standard

> 1322#
> #2#31
> 12#1#

Ce test est le plus important, c'est celui d'origine. Il permet de comprendre la base du problème et, si le problème ne comporte que peu de cas particuliers, il peut permettre de le résoudre dans son intégralité.

Entrée standard

> 0#0
> #0#
> 0#0
Sortie standard

> 0#0
> #0#
> 0#0

Ce test permet de coder le mécanisme lié au bord de façon plus précise, et il permet également de constater que l'entrée et la sortie peuvent être la même.

Entrée standard

> #00###000
> 000000000
> 000##0000
Sortie standard

> #22###232
> 244223443
> 232##2332

Ce test permet de vérifier si le programme fonctionne à plus grande échelle.




Description d'une autre solution au problème

Beaucoup d'autres utilisateurs de la plateforme Codingame ont essayé de résoudre le problème Détective Pikaptcha ep1. Parmi eux, l'utilisateur "lhm". J'ai décidé de présenter son code même si il utilise le même principe que moi, car il est beaucoup plus compact. En effet, même si il perd légerement en lisibilité, il a su utiliser la syntaxe du C afin d'optimiser au maximum le nombre de lignes de son code. Je trouve donc cette solution intéressante car elle permet de montrer que, malgré la syntaxe plutot exigente de ce langage, il est possible d'être assez rapide lors de l'écriture de la solution. Son code est donc pratique lorsque l'on fait des test ou lorsque l'on résout des problèmes personnels, mais je le déconseillerais lorsqu'il s'agit de gros projets, ou de travaux de groupe à présenter. En effet, le manque d'aération dans le code et le regroupement d'un certain nombre d'instruction par ligne le rend plus difficile à comprendre. De plus, dans la version originale, le code n'est pas commenté.

Quand au fonctionnement de son programme, il est similaire au mien. L'utilisateur va d'abord récupérer les informations données par le site, la longueur et hauteur du labyrinthe, ainsi que les lignes du labyrinthe. Puis, il va parcourir le labyrinthe à l'aide d'une double boucle, et tester si la case qu'il traite est un passage. Si tel est le cas, les coordonnées de chaque passage passent différents test afin de savoir si la case se situt sur un bord, et si elle est entouré par des passages ou des murs, il modifi alors les cases en fonction des résultats des tests. Ensuite il affiche le labyrinthe modifié ligne par ligne. Il fait cela à l'intérieur de sa double boucle principale, ce qui lui évite de recréer une boucle pour afficher le résultat, et son programme gagne donc en efficacité par rapport à ma proposition.

Voici ci dessous le code commenté de l'utilisateur "lhm".


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

main()
{
    int  w, h;  scanf("%d%d", &w, &h); // Récupération des dimentions du labyrinthe
    char line[101][101];  // Creation d'un tableau

    for (int i = 0; i < h; i++) {scanf("%s", line[i]); } // Récupération des lignes du labyrinthe
    for (int i = 0; i < h; i++) { // Création d'une double boucle pour parcourir le labyrinthe
        for (int j = 0; j < w; j++) {
            if (line[i][j] != '#')  {
                if( i > 0   && line[i-1][j  ] != '#' ) {  line[i][j]++; }  // vérifi si la case du dessus est un passage
                if( i < h-1 && line[i+1][j  ] != '#' ) { line[i][j]++; }  // vérifi si la case du dessous est un passage
                if( j > 0   && line[i  ][j-1] != '#' ) { line[i][j]++; }  // vérifi si la case de gauche est un passage
                if( j < w-1 && line[i  ][j+1] != '#' ) { line[i][j]++; }  // vérifi si la case de droite est un passage
            }
        }
        printf("%s\n", line[i]);  // Affichage du résultat
    }
}



Bilan de ce travail

Ce travail m'a tout d'abord permis d'approfondir mes connaissances en matière de gestion de coordonnées. En effet, le mécanisme selon lequel il faut regarder uniquement certaines cases, en fonction de si elles se situent ou non sur les bords était très intéressant. De plus j'ai pu apprendre à manipuler un nouvel élement en C, les tableaux. Enfin, la rédaction de ce rapport en langage HTML et CSS m'a permis de réviser les bases de ce langages que j'avais eu l'occasion d'étudier précedement, ainsi que d'approfondir mes connaissances, notamment en ce qui concerne la gestion des espaces et du style CSS.





Episode 2



Présentation du problème

Le problème codingame Detective Pikaptcha épisode 2 est un problème de labyrinthe. C'est la suite du problème précedent. Mais cette fois ci, la situation va se compliquer. Le but de cet épisode va être de faire longer un mur à Pikachu, jusqu'à ce qu'il revienne à son point de départ. Chaque fois qu'il passera sur une case, il faudra ajouter 1 à cette case. Le but du problème sera donc de renvoyer un labyrinthe modifié, avec sur les cases "passages", le nombre de fois que Pikatchu les a parcouru. Pour cela, le site codingame nous donnes un certain nombre d'éléments en entrée.

Afin de mieux comprendre l'énoncé du problème, voici ci dessous un exemple de chemin que Pikachu doit suivre si on souhaite qu'il longe le labyrinthe par la gauche. Les cases jaunes représentent les murs, est les blanches les passages.

Version imagé du premier test de ce problème Src: www.codingame.com


Méthode de résolution


Description de la stratégie


Mon programme se divise en plusieurs parties. Tout d'abord, la récupération des différentes entrées données par Codingame, tel que les dimensions du labyrinthe, le coté que Pikatchu doit longer, ou encore les lignes du labyrinthe. Puis on initialise plusieurs variables qui vont nous aider à résoudre le problème. En effet on aura besoin d'une copie du labyrinthe donné en entrée, afin d'avoir un labyrinthe intact, et l'autre modifiable. Il nous faudra également connaitre le point de départ de Pikachu, afin de connaitre les conditions qui feront s'arréter le programme. Ensuite, après avoir remarqué que Pikachu regardait les cases une à une, selon un cycle, on défini une fonction qui nous permettra de connaitre ce cycle, en fonction du mur que Pikachu doit longer (gauche ou droite). A partir de ce cycle, il nous est possible de créer une fonction qui va permettre à Pikachu de se déplacer selon un ordre précis.
Alors, on peut créer le programme principal, qui utilisera les fonctions créées précedement et qui tournera tant que Pikachu ne sera pas retourné au point de départ. Enfin, lorsque la boucle principale se sera arrété, on retournera le résultat à l'aide d'une boucle qui affichera le tableau obtenu ligne par ligne.

Détail algorithmique de la méthode

Ce programme use de mécanismes assez particuliers qu'il faut comprendre afin de pouvoir résoudre le problème.

Tout d'abord, le mécanisme du cycle. En effet Pikachu va regarder chaque case qui l'entoure selon un cycle précis qui prendra en compte l'orientation de Pikachu, ainsi que le mur qu'il devra longer. Avant de rentrer plus en détail dans le code du cycle, regardons comment cette mécanique fonctionne à l'aide de deux tableaux excel.



Tableau excel représentant l'ordre dans lequel Pikachu doit regarder chaque case


Afin de coder les résultats de ces observations, j'ai utilisé une liste. On retrouve la méthode que je vais détailler dans la fonction cycle de mon programme. Il fallait distinguer plusieurs cas dans cette fonction. Tout d'abord, il fallait distinguer le cas coté gauche et coté droit. Alors en fonction du coté, le programme affecte à la variable direction une liste représentant le cycle à suivre.
Alors, grace aux tableaux excel on peut se rendre compte de la chose suivante. Si l'orientation de Pikachu correspond au premier élement de la liste, lors de son prochain mouvement il regardera d'abord dans toutes les autres directions avant de regarder dans celle qu'il pointe. Et si l'orientation de Pikachu correspond à un autre élement de la liste, le principe est le même, c'est seulement la commande pour réorganiser la liste qui change, et ce à cause d'un soucis d'index.

Prenons un exemple, si Pikachu regarde à gauche (<), et qu'il doit suivre le mur droit. Alors la case à sa droite, correspond dans le réferentiel d'un observateur à la case au dessus de lui. Il regardera donc d'abord en haut, puis à droite puis en bas, et enfin à gauche (voir deuxième ligne du tableau de droite).

Alors, avec l'obtention de la liste réarrangé des directions, il était aisé de coder la fonction deplacement. Cette fonction regarde pour chaque élement de la liste dans l'ordre, si l'endroit où Pikachu veut se diriger est un mur. Si c'est un mur alors elle continue son processus, mais dès qu'une case où Pikachu peut aller est détectée, on modifie les coordonnées auquelles Pikachu se situt. C'est ce changement qui simule le déplacement de Pikachu. On utilise alors l'outils break afin d'arréter la fonction.

Maintenant intéressons nous à la boucle principale du programme. Pour comprendre son fonctionnement et l'utilisation des fonctions décrites précedement, on peut la représenter sous la forme d'un schéma.



Organigramme décrivant la boucle principale de mon programme


Code commenté ayant résolu le problème

Voici ci dessous ma proposition de code commenté pour résoudre le problème Pikaptcha épisode 2.


import sys
import math

table = []

width, height = [int(i) for i in input().split()] # Recuperation de la taille du labyrinthe

end = False

depart = tuple()

table2 = []

# Creation d'un tableau à deux dimentions rempli de 0

for i in range(height):
    line2 = []
    for j in range(width):
        line2.append(0)
    table2.append(line2)



for i in range(height):    # On récupère le labyrinthe donné par l'énoncé
    line = input()
    table.append(line)

    for directions in ['<','^','>','v']:   # On cherche le point de départ de Pikatchu
        for j in range (len(line)):
            if directions == line[j]:
                depart = (i,j,directions,False)

    for j in range(len(line)):      # Ajout des murs au tableau à deux dimentions rempli de 0
        if table[i][j] == "#":
            table2[i][j] = "#"

side = input() # On récupère le coté par lequel Pikatchu doit longer le mur

# On défini l'ordre dans lequel Pikatchu doit regarder chaque coté

def cycle(direction_p,side):
    if side == 'R':
        direction = ['v','>','^','<']   # Cycle si Pikatchu doit longer le mur de droite
    else:
        direction = ['<','^','>','v']   # Cycle si Pikatchu doit longer le mur de gauche

    index = direction.index(direction_p)   # Récupération de l'index dans le cycle de la position initiale de Pikatchu

    if index == 0:                  # Si l'index vaut 0 on reconstruit l'ordre des directions de la fin vers le début
        direction = direction[3:]+direction[0:3]

    else:  # Sinon on reconstruit l'ordre en renvoyant la direction correspondant à l'index 0, à la fin de la liste des directions
        direction = direction[index-1:len(direction)]+direction[0:index-1]

    return direction


# Fonction permettant à Pikachu de regarder dans chaque direction dans un certain ordre

def deplacement(table, depart, side, height, width):

    i = depart[0]
    j = depart[1]
    direction_p = depart[2]
    direction = cycle(direction_p,side)  # Fourni l'ordre des directions que Pikatchu doit regarder

    for k in direction:
        if k == ">" and j < width-1 and (table[i][j+1] == "0" or table[i][j+1] in direction): # Si on est pas sur le bord droit du labyrinthe on regarde à droite
                                                                                              # et si ce n'est pas un mur on s'y déplace
            depart=(i, j+1,">", True)
            break
        if k == "v" and i < height-1 and (table[i+1][j] == "0" or table[i+1][j] in direction): # Si on est pas sur la derniere ligne du labyrinthe on regarde en bas
                                                                                                # et si ce n'est pas un mur on s'y déplace
            depart=(i+1, j,"v", True)
            break
        if k == "<" and j > 0 and (table[i][j-1] == "0" or table[i][j-1] in direction): # Si on est pas sur le bord gauche du labyrinthe on regarde                                                                                             # à gauche
et si ce n'est pas un mur on s'y déplace
            depart=(i, j-1,"<", True)
            break
        if k == "^" and i > 0 and (table[i-1][j] == "0" or table[i-1][j] in direction): # Si on est pas sur la première ligne du labyrinthe on regarde                                                                                               # en haut
et si ce n'est pas un mur on s'y déplace
            depart=(i-1, j,"^", True)
            break

    return depart

# Boucle principale, qui s'execute tant que Pikatchu n'est pas revenue sur sa case de départ

while(end == False):
    depart = deplacement(table, depart, side, height, width)  # Initialisation de la position de Pikatchu à chaque tour de boucle
    i = depart[0]
    j = depart[1]

    if depart[3] == True: # On vérifi que pikachu se soit déplacé au moins une fois
        table2[i][j] += 1

    if table[i][j] in ('<','^','>','v'): # On vérifi si il ne retourne pas sur le point de départ
        end = True


# Boucle qui affiche le résultat

for i in range(height):
    for j in range(width):

        print(table2[i][j], end="")
    print("")   # Retour à la ligne uniquement lorsque l'on a affiché une ligne entière du labyrinthe



Présentation des tests

Afin de valider notre réponse, le site Codingame effectue différents tests, voici ci dessous les tests les plus intéressant, qui ont permis de trouver la solution à ce problème.

Entrée standard

> >000#
> #0#00
> 00#0#
side: L
Sortie standard

> 1322#
> #2#31
> 12#1#

Ce test est le premier que l'on doit passer. Il est important mais peut également être trompeur. En effet, ici Pikachu commence le labyrinthe en haut à gauche, mais ce n'est pas forcément le cas pour tout les tests. De plus, il passe sur toutes les cases, ce qui n'est pas non plus vrai pour tout les tests.

Entrée standard

> #00###000
> 0000<0000
> 000##0000
side: R
Sortie standard

> #11###000
> 112210000
> 111##0000

Ce test montre qu'il est possible que Pikachu ne commence pas en haut à gauche, il permet également de tester le fait que Pikachu soit orienté différement. Enfin il montre que toutes les cases ne sont pas obligatoirement parcourues.

Entrée standard

> 0#0
> #v#
> 0#0
Sortie standard

> 0#0
> #0#
> 0#0

Ce test permet de coder deux choses très importantes. Tout d'abord il permet de se rendre compte que Pikachu peut ne pas bouger. Donc, il nous permet de rajouter une condition dans le code pour vérifier si Pikachu bouge avant de remplacer sa case de départ par un 1. De plus, il montre qu'il faut initialiser toutes les cases du labyrinthe à 0, et donc qu'il est plus simple de travailler en conservant le labyrinthe d'origine, et en le copiant dans une variable que l'on pourra modifier.

Difficultés rencontrées

Lors de la résolution de ce problème, j'ai rencontré un certain nombre de difficultés. En effet, après avoir longtemps cherché, j'avais réussi à établir un ordre dans lequel Pikachu devait regarder chaque case, mais je ne savais pas comment l'implémenter. J'ai donc eu besoin d'aide afin de coder le système d'ordre par concaténation de liste. De plus, après avoir codé la boucle permettant d'incrémenter de 1 chaque case lorsque Pikachu se déplace dessus, mon programme ne fonctionnait toujours pas. J'avais ommis un point assez important, je ne prenais pas en compte qu'après chaque déplacement, l'orientation de Pikachu pouvait changer. Alors j'ai de nouveau eu besoin d'aide afin de trouver le système de tuple permettant d'initialiser la position de Pikachu à chaque tour de boucle. La condition de retour au point de départ que j'avais codé précedement s'inscrivait dans la méthode des tuple. Alors mon programme fonctionnait enfin.
Afin de m'aider dans la réalisation du programme, j'ai visionné la vidéo de la chaine youtube Foxxpy-Mathématique et algorithmie. A la fin, j'ai séparé les différentes parties de mon code, qui était assez brouillon, en différentes fonction. Car, après avoir visionné cette vidéo, je me suis rendu compte que mon code manquait réellement de clarté, et n'était pas bien mis en forme afin d'être présenté.

Video qui m'a aidé à résoudre le problème Chaine youtube: Foxxpy-Mathématique et algorithmie

Description d'une autre solution au problème

J'ai choisi de présenter la solution de l'utilisateur nicola car elle est bien plus compacte que celle que je propose, et utilise un mécanisme bien différent.

Il va d'abord récupérer les variables données par le site. Lorsqu'il récupère le labyrinthe en entrée, il le modifi en remplaçant la case indiquant la position de Pikachu par un 0, et il extrait la position de Pikachu dans une variable à part. Puis, grace à un dictionnaire, il va associer l'orientation de Pikachu à un tuple. Ensuite, il associe un coté (gauche ou droite) à un autre dictionnaire composé de tuples du même type que ceux évoqués précedement.

Alors il crée la boucle principale qui récupère l'ordre exact dans lequel Pikachu doit regarder les cases adjacentes. La clé de l'ordre exacte situé dans le dictionnaire correspond au tuple récupéré au début du programme grace à l'orientation de Pikachu. Cet ordre exact est un tuple composé de tuple qui nous servirons dans l'étape suivante. En effet, on additionne aux coordonnées de Pikachu les différents tuples présents dans l'ordre exact, ce qui va nous donner des coordonnées potentielles pour le prochain déplacement de Pikachu. On va alors vérifier que ces coordonnées correspondent bien aux futures coordonnées de Pikachu. Elles vont donc passer une série de test. Il faut qu'elles soit comprises dans le labyrinthe (que ce ne soit pas des coordonnées qui dépassent les bords), et qu'elles correspondent à un passage (donc pas à un mur). Dès qu'une condition n'est pas respectée, on crée de nouvelles coordonnées avec les autres tuple du tuple "ordre exact". Si au contraire, on trouve les bonnes coordonnées, on met à jour les coordonnées de Pikachu, ainsi que son orientation (donné par un tuple). Puis on incrémente de 1 la case sur laquelle Pikachu se déplace, et la boucle recommence. Il se peut qu'aucune des propositions de coordonnées ne correspondent à un déplacement viable, alors la boucle s'arrête et on affiche le résultat. Il se peut aussi que Pikachu retourne sur le point de départ (cela doit forcément arriver à un moment donné), alors on arrête la boucle et on affiche le résultat.

Voici ci dessous le code commenté de l'utilisateur "nicola".


w,h=map(int,input().split())
p=[]
for i in range(h):
    p.append(input())  # Ajoute les lignes du labyrinthe dans une liste
    D=set(p[-1])&{"<",">","v","^"}
    if D:
        d=D.pop()    # Récupère l'orientation initiale de Pikachu
        P=(i,p[-1].index(d))
        p[-1]=p[-1].replace(d,"0") # Remplace le symbole de l'orientation de Pikachu par un 0
c=input()  # Récupère le coté que Pikachu doit longer
init=P
d={">":(0,1),"<":(0,-1),"v":(1,0),"^":(-1,0)}[d]  # Récupère le code correspondant à l'orientation de Pikachu
mouv={"R":{(0,1): ((1,0), (0,1), (-1,0),(0,-1)),
           (0,-1):((-1,0),(0,-1),(1,0), (0,1)),
           (1,0): ((0,-1),(1,0), (0,1), (-1,0)),
           (-1,0):((0,1), (-1,0),(0,-1),(1,0))},
      "L":{(0,1): ((-1,0),(0,1), (1,0), (0,-1)),
           (0,-1):((1,0), (0,-1),(-1,0),(0,1)),
           (1,0): ((0,1), (1,0), (0,-1),(-1,0)),
           (-1,0):((0,-1),(-1,0),(0,1), (1,0))}}[c]  # Récupère les ordres de déplacement possibles de Pikachu en fonction du coté qu'il doit longer

while True:
    for m in mouv[d]:   # Récupère l'ordre exact dans lequel Pikachu doit regarder les cases qui l'entoure
        x,y=P[0]+m[0],P[1]+m[1]  # Récupère les potentielles futures coordonnées de Pikachu
        if not 0<=x<h:
            continue
        if not 0<=y<w:
            continue
        if p[x][y]=="#":
            continue
        d=m   # Met à jour l'orientation de Pikachu
        P=(x,y)   # Met à jour les coordonnées de Pikachu
        p[x]=p[x][:y]+str(int(p[x][y])+1)+p[x][y+1:]   # Stock le résultat obtenu
        break
    else:
        break
    if P==init:   # Test au cas où Pikachu n'ai pas bougé
        break

print("\n".join(p))  # Affiche le résultat




Bilan de ce travail

Ce travail m'a appris à coder un programme avec de nombreuses fonctions et à ne pas se baser uniquement sur un test pour coder un résultat. En effet, lorsque j'ai voulu résoudre ce problème je me suis uniquement basé sur le premier test ce qui m'a fait défaut par la suite, et m'a obligé à recommencer. Il m'a aussi permi d'approfondir d'avantage l'aspect logique, puisqu'il fallait trouver les cycles correspondant aux différents cas de figure. J'ai également trouver intéressant le fait de devoir simuler le déplacement d'un personnage de manière un minimum élaborée (prise en compte de la direction dans laquelle il regarde), car j'avais peu traité ce genre de problèmes auparavent. Enfin, la solution de nicola avec les dictionnaires m'a permi de revoir l'utilisation des dictionnaires ainsi que leurs possibilités dans un cadre un peu plus concret qu'un simple exercice d'affectation de valeurs.