Méthodologie pour résoudre un challenge

On prépare le code pas à pas en analysant l’énoncé, les données, puis en faisant un plan.

→ Corrigé du Challenge : CodeMind #3 : Coach sportif

Attention, corrigé un peu spécial ! Ce challenge était proposé dans le cadre de l’évènement Top Code 2024, c’était le 3ème challenge d’une série de 6. Il paraissait compliqué mais en y regardant de plus près, il ne l’était pas tant que ça…

On va voir dans ce corrigé comment bien analyser un problème avant de se jeter dans le code. C’est d’ailleurs souvent une mauvaise habitude quand on débute, on a tellement envie de coder qu’on démarre sans prendre le temps de bien analyser le problème. Alors qu’en prenant un peu de temps pour comprendre le problème, le décortiquer, et faire un petit plan, on gagne beaucoup de temps lors de la phase de code.

Dans l’ordre :

Ce corrigé est codé en PHP mais les premières rubriques sont complètement agnostiques du langage.

Le corrigé en vidéo

L’ensemble de cet article est disponible en vidéo, tu peux donc la regarder tranquillement et retrouver l’ensemble des éléments et bouts de code dans le reste de la page !

Analyse de l’énoncé

Dans ce challenge, on a en entrée les horaires de cours d’un étudiant sur une journée, par exemple : ‘L1 8-10 13-16’ ce qui signifie que le Lundi 1er, cet étudiant a cours de 8h à 10h et de 13h à 16h. Et on a 31 éléments, un par jour du mois.

Pour résoudre le challenge, il faut trouver les créneaux durant lesquels l’étudiant peut faire du sport. Pour cela, il faut qu’il ait un créneau de libre de 3h d’affilée. Comme ça il peut faire du sport au milieu du créneau.

Il y a ensuite quelques règles à respecter :

  • Un créneau maximum par jour
  • Si je fais du sport le jour J, je ne ferais pas de sport le jour J+1
  • On ne cherche pas à optimiser, on part du premier créneau possible puis on applique les règles ci-dessus

Tu suis ? Si c’est compliqué, saches que pour chaque challenge Tainix, tu as une infinité (oui une infinité) de jeux de données d’exemple ! Mais où les trouver ??

  • Sur chaque page d’un challenge, il y a un lien « Données et logique algorithmique »
  • Dans la sandbox, à la fin de l’énoncé, il y a un lien « Données et logique algorithmique »

Allons voir ça de plus près…

Analyse des jeux de données

En complément de ces jeux de données, vous avez la réponse attendue et (surtout) un déroulé étape par étape qui présente une logique de résolution. Cette logique est plus ou moins précise et détaillée selon les challenges mais elle donne des indications très intéressantes. Et en fait, quand tu dois produire du code pour résoudre un problème, répondre à une problématique client ou autre, tu as souvent aussi des exemples de données : une idée de comment les choses vont s’orchestrer, une vision sur le format de sortie avant même de commencer à coder, etc. C’est pour coller à ce cas de figure que Tainix propose ces jeux de données et indications.

Prenons 3 jeux de données du challenge :

Jeu de données #1

Jeu de données #2

Jeu de données #3

Et qu’est ce qu’on remarque si on regarde de plus près ? Le nombre de créneaux possibles est en réalité assez limité :

  • Le matin (en bleu), il n’y a qu’un seul créneau possible, entre 8 et 9
  • Le soir (en violet), il n’y a qu’un seul créneau possible, entre 17 et 18
  • Le midi (en orange), il y a par contre 3 créneaux possibles : 11 à 12, 12 à 13 ou 13 à 14

D’un problème qui paraissait très compliqué, sans coder, on l’a déjà simplifié. Les cas matin et soir devraient se gérer rapidement. Pour le midi, il y aura un peu plus de code à écrire.

Organisons le code maintenant…

Plan du code

Reprenons les étapes après avoir analyser correctement l’énoncé les données :

  1. Tout d’abord il va falloir faire un peu de parsing pour extraire les données correctement d’une chaine type « L1 8-10 13-16 ». Si tu es familier des challenges Tainix, ça commence souvent par un peu de parsing. On a des tutos dédiés au parsing si besoin 😉
  2. On va ensuite chercher un créneau disponible en découpant le code en 3 sections :
    • Y a t-il un créneau le matin ? Si oui, c’est forcément 8-9
    • Y a t-il un créneau le midi ? Si oui, il faut le définir
    • Y a t-il un créneau le soir ? Si oui, c’est forcément 17-18
  3. Application des règles métier complémentaires

Et on peut faire tout ça sous forme de code, et je t’invite à le faire ! Ecris ce plan dans ton code sous forme de commentaires, puis code autour ensuite :

// 0. Initialisation de la réponse
$slots = [];

foreach ($days as $day) {
    
    // 1. Parsing
    
    // 2. Recherche d'un créneau
    
        // 2.1 Matin
        
        // 2.2 Midi
        
        // 2.3 Soir
        
    // 3. Règles métier
}

// Production de la réponse
echo implode(' ', $slots);

Il y a de grandes chances que les choses bougent un peu, notamment les règles métier, mais ça te donne une bonne base.

Production du code, avec un peu d’IA ?

Le parsing

Un des objectifs de Tainix, c’est de pratiquer le code. On pourrait se dire qu’utiliser de l’IA est donc complètement antinomique avec Tainix ? Mais le vrai objectif de Tainix c’est que tu progresses ! Et il y a des moments, dans les phases de code, où l’utilisation de l’IA est intéressante. Pour ce challenge, si tu lui passes l’énoncé tel quel, tu vas avoir un retour un code beaucoup trop compliqué, notamment parce que l’IA n’aura pas fait l’analyse des jeux de données, et va donc produire un code qui fonctionne « à tous les coups ». En réalité, ici, l’IA va te retourner un truc très complexe qui ne fonctionne pas.

Le but est donc de définir quand utiliser l’IA, pour quelle portion du problème, et quand ne pas l’utiliser pour ne pas récupérer une solution trop compliquée, parfois difficile à comprendre ou à débuguer. Ici le bon usage, c’est de s’en servir pour le parsing ! Qui aime faire du parsing de toute façon ?

Voici un exemple de prompt : (réalisé sans trucage avec GPT4-o)

On aurait pu lui demander de le faire avec « explode » ou avec une expression régulière.

Trouver le créneau

Cette fois-ci on va le coder manuellement.

Pour un créneau le matin, de 8 à 9, il faut que l’étudiant commence à 10h :

// 2.1 Matin
if ($morningStart === 10) {
    $slot = '8-9';
}

Pour un créneau le soir, de 17 à 18, il faut que l’étudiant finisse à 16h :

// 2.3 Soir
if ($afternoonEnd === 16) {
    $slot = '17-18';
}

Pour un créneau le midi, il faut un trou d’au moins 3h entre la fin des cours le matin et le début des cours l’après midi. On prendra alors le créneau 1h après la fin du cours du matin :

// 2.2 Midi
if ($afternoonStart - $morningEnd >= 3) {
    $start = $morningEnd + 1;
    $end = $morningEnd + 2;
    
    $slot = "$start-$end";
}

Et quand on a trouvé un créneau, on le met dans le tableau principal, avec le nom du jour :

$slots[] = "$dayName:$slot";

Et voilà !

Règles métier

Pour ne pouvoir faire qu’un seul créneau par jour, comme on se trouve dans une boucle, on va pouvoir utiliser l’instruction « continue ». Comme ça dès qu’on a trouvé un créneau, on passe au jour suivant.

On aura donc (pour chaque recherche de créneau) :

$slots[] = "$dayName:$slot";
continue;

Pour gérer le fait qu’on ne pouvait pas faire du sport 2 jours consécutifs, il faut travailler avec un booléen.

On est ici sur une notion de « flag » ou drapeau, qui se lève et qui descend, un coup à true, un coup à false et un comportement qui change régulièrement.

Ce booléen, qu’on peut appeler « hasDoneSportYesterday » (est ce que j’ai fait du sport hier ? Important de bien nommer les choses) démarre donc à false, avant le début de la boucle principale, puis :

  • Dès que je trouve un créneau, avant le continue, je le passe à true.
  • Au début de chaque itération, je le contrôle. Si j’ai fait du sport hier, je repasse la valeur à false, puis je passe tout de suite au jour suivant, en utilisant à nouveau l’instruction « continue » comme pour la première règle métier.

De cette façon, le comportement change presque à chaque itération.

Et voici donc le code final :

// 0. Initialisation de la réponse
$slots = [];

// 0. Initialisation du Flag
$hasDoneSportYesterday = false;

foreach ($days as $day) {
    
    // Règle métier #2
    if ($hasDoneSportYesterday) {
        $hasDoneSportYesterday = false;
        continue;
    }
    
    // 1. Parsing
    sscanf($day, "%s %d-%d %d-%d", $dayName, $morningStart, $morningEnd, $afternoonStart, $afternoonEnd);
    
    // 2. Recherche d'un créneau
    
        // 2.1 Matin
        if ($morningStart === 10) {
            $slot = '8-9';
            $slots[] = "$dayName:$slot";
            $hasDoneSportYesterday = true; // Règle métier #2
            continue; // Règle métier #1
        }
        
        // 2.2 Midi
        if ($afternoonStart - $morningEnd >= 3) {
            $start = $morningEnd + 1;
            $end = $morningEnd + 2;
            
            $slot = "$start-$end";
            $slots[] = "$dayName:$slot";
            $hasDoneSportYesterday = true; // Règle métier #2
            continue; // Règle métier #1
        }
        
        // 2.3 Soir
        if ($afternoonEnd === 16) {
            $slot = '17-18';
            $slots[] = "$dayName:$slot";
            $hasDoneSportYesterday = true; // Règle métier #2
            continue; // Règle métier #1
        }
}

// Production de la réponse
echo implode(' ', $slots);

Optimisation du code

Ce code peut être optimisé à bien des égards :

  • Les règles métier se répètent
  • Aucune fonction, donc du code difficilement testable unitairement
  • On pourrait créer une objet Day qui s’occuperait de trouver les créneaux et un objet Program charger de gérer les règles métier

A toi de jouer 😉

Et n’hésite pas à choisir un autre challenge de code pour mettre en application cette méthodologie d’analyse et de préparation du code.


Qui a codé ce superbe contenu ?

Keep learning

Autres contenus à découvrir


Ta newsletter chaque mois

Corrigés, challenges, actualités, veille technique... aucun spam.