Guide du barème de correction automatique

Ce guide s'adresse aux enseignants et administrateurs qui configurent la correction automatique d'un exercice ou d'un défi. Il explique comment fonctionne un barème et détaille chacune des vérifications disponibles dans le constructeur, avec un exemple pour chacune.

Accéder au constructeur de barème

Sur la page d'un exercice (parcours d'apprentissage) ou d'un défi, un bouton « Configurer le barème » (ou « Modifier le barème » s'il en existe déjà un) ouvre le constructeur. Tant qu'aucun barème n'est défini, la correction reste la comparaison simple habituelle : la sortie du programme doit correspondre exactement à la sortie attendue.

Principe général

Un barème est une liste de vérifications. Chaque vérification :

  • a un poids : les poids des vérifications réussies sont additionnés et divisés par le poids total pour obtenir le score final, en pourcentage ;
  • peut être marquée bloquante : si une vérification bloquante échoue, le score est forcé à 0, quel que soit le résultat des autres vérifications (utile par exemple pour « aucune erreur d'exécution ») ;
  • renvoie un message explicite, affiché à l'élève après sa soumission — le barème sert donc aussi d'outil pédagogique, pas seulement de note.

Un seuil de réussite (par défaut 100 %) détermine à partir de quel score l'exercice est considéré comme réussi. Un seuil inférieur à 100 % permet d'accorder du crédit partiel.

Les vérifications se répartissent en deux familles :

  • les vérifications statiques analysent le code source sans l'exécuter (présence d'une structure, variables inutilisées, taille du code…) ;
  • les vérifications dynamiques exécutent réellement le programme (comparaison de la sortie, cas de test, détection d'erreur…).

Combiner les deux est ce qui permet de vérifier qu'un exercice a été résolu « de la bonne manière », et pas seulement que la sortie est correcte — un énoncé qui demande une boucle Pour peut ainsi rejeter une solution qui produit le bon résultat avec une TantQue, ou avec la réponse écrite en dur.


Vérifications statiques

Doit utiliser une structure (requires_construct)

Vérifie qu'une structure du langage (Pour, TantQue, Si, Selon, Fonction ou Classe) apparaît au moins une fois dans le programme.

Champ à renseigner : Structure.

Exemple d'usage : un exercice « Calculez la somme de 1 à 10 avec une boucle Pour » — on ajoute une vérification « Doit utiliser Pour » pour que la solution soit rejetée si l'élève utilise une TantQue à la place, même si le résultat affiché est correct.

Variable somme: Entier
Variable i: Entier

Debut
    somme = 0
    Pour i De 1 A 10 Faire
        somme = somme + i
    FinPour
    Ecrire(somme)
Fin

Ne doit pas utiliser une structure (forbids_construct)

L'inverse de la précédente : échoue si la structure choisie apparaît dans le programme.

Champ à renseigner : Structure.

Exemple d'usage : un exercice de recherche qui doit s'arrêter dès que l'élément est trouvé — on ajoute « Ne doit pas utiliser Pour » pour empêcher un simple parcours complet du tableau qui ignorerait la contrainte d'arrêt anticipé, en complément d'une vérification « Doit utiliser TantQue ».

Doit combiner plusieurs structures (requires_all_constructs)

Vérifie que toutes les structures sélectionnées apparaissent dans le programme (une seule vérification pour plusieurs structures requises).

Champ à renseigner : Structures requises (sélection multiple).

Exemple d'usage : un exercice de tri qui doit combiner une boucle et une condition — on sélectionne Pour et Si, ce qui rejette une solution composée uniquement d'une longue suite d'échanges écrits à la main sans aucune structure de contrôle.

Variable i: Entier
Variable j: Entier
Variable temp: Entier
Tableau notes[5]: Entier

Debut
    Pour i De 0 A 3 Faire
        Pour j De 0 A 3 - i Faire
            Si notes[j] > notes[j + 1] Alors
                temp = notes[j]
                notes[j] = notes[j + 1]
                notes[j + 1] = temp
            FinSi
        FinPour
    FinPour
Fin

Interdit une structure à l'intérieur d'une boucle (forbids_construct_in_loop)

Vérifie qu'une structure donnée (aujourd'hui : Ecrire) n'apparaît nulle part à l'intérieur du corps d'une boucle Pour/TantQue (ou des deux, avec l'option « any »).

Champs à renseigner : Structure interdite, Boucle concernée (any/Pour/TantQue).

Exemple d'usage : le classique bug « le message s'affiche à chaque tour de boucle au lieu de s'afficher une seule fois à la fin ». On ajoute « Ne doit pas utiliser Ecrire à l'intérieur d'une boucle (any) ».

# Rejeté : Ecrire est à l'intérieur de la boucle
Pour i De 1 A 3 Faire
    Ecrire("Bonjour")
FinPour
# Accepté : Ecrire est après la boucle
Pour i De 1 A 3 Faire
    i = i
FinPour
Ecrire("Bonjour")

Doit déclarer un identifiant (requires_identifier)

Vérifie qu'un nom précis (variable, tableau, constante ou fonction) apparaît dans le programme, que ce soit à la déclaration ou à l'usage.

Champ à renseigner : Nom de la variable/fonction.

Exemple d'usage : imposer le nom d'une variable donné dans l'énoncé (par exemple pour que la correction automatique puisse s'y référer sans ambiguïté) — on ajoute « Doit déclarer/utiliser « moyenne » ».

Profondeur d'imbrication maximale (max_nesting_depth)

Compte la profondeur maximale d'imbrication des structures de contrôle (Si/TantQue/Pour/Selon) et échoue si elle dépasse la limite fixée.

Champ à renseigner : Profondeur maximale.

Exemple d'usage : encourager une solution simple pour un exercice qui ne devrait nécessiter qu'une seule boucle — une profondeur maximale de 1 rejette une solution qui imbrique une boucle dans une autre alors que l'énoncé ne le demande pas.

Taille maximale du code (max_lines)

Compte le nombre de lignes non vides du programme et échoue au-delà de la limite fixée.

Champ à renseigner : Nombre de lignes maximum.

Exemple d'usage : éviter qu'un élève ne contourne l'exercice en écrivant une longue suite d'instructions répétées à la main plutôt que d'utiliser une boucle — utile en complément d'une vérification « Doit utiliser Pour ».

Motif interdit (expression régulière) (forbidden_source_pattern)

Recherche un motif (texte ou expression régulière, insensible à la casse) dans le code source brut, et échoue s'il est trouvé.

Champs à renseigner : Expression régulière, Description (affichée à l'élève).

Exemple d'usage : interdire l'appel direct à une fonction native que l'exercice demande justement de réimplémenter — motif Trier\(, description « La fonction Trier() intégrée ne doit pas être utilisée pour cet exercice ».

Aucune variable inutilisée (no_unused_variables)

Vérifie que chaque variable, tableau ou constante déclaré est effectivement utilisé quelque part dans le programme (y compris comme compteur d'une boucle Pour). Ne prend aucun paramètre.

Exemple d'usage : repérer du code de démarrage recopié tel quel, avec des variables déclarées « au cas où » mais jamais réellement utilisées — signe fréquent d'un exercice pas totalement terminé.

Variable somme: Entier
Variable inutile: Entier

Debut
    somme = 0
    Ecrire(somme)
Fin

Cette solution échoue la vérification : inutile est déclarée mais n'apparaît nulle part ailleurs dans le programme.


Vérifications dynamiques

Toutes exécutent réellement le code soumis (avec les mêmes entrées simulées et la même limite d'exécution que la correction classique), et détectent donc aussi automatiquement une boucle infinie (« Délai dépassé ») ou une erreur d'exécution, en plus de leur propre logique.

Sortie exacte (output_equals)

Exécute le programme avec les entrées fournies et compare la sortie, au caractère près (espaces de début/fin ignorés), à la sortie attendue. C'est l'équivalent, sous forme de vérification pondérée, de la correction simple habituelle.

Champs à renseigner : Entrées (une valeur par ligne, une par appel à Lire()), Sortie attendue.

Exemple d'usage : un exercice qui lit un âge et affiche un message.

Variable age: Entier

Debut
    Lire(age)
    Si age >= 18 Alors
        Ecrire("Majeur")
    Sinon
        Ecrire("Mineur")
    FinSi
Fin

Avec l'entrée 20, la sortie attendue est Majeur.

Sortie contient (output_contains)

Comme « Sortie exacte », mais réussit dès que la sortie contient le texte attendu, sans devoir correspondre exactement.

Champs à renseigner : Entrées, Sous-chaîne attendue.

Exemple d'usage : un exercice où la mise en forme exacte du message importe peu, tant que l'information essentielle apparaît — sous-chaîne attendue « 55 » plutôt qu'une correspondance exacte avec « La somme vaut 55 ».

Résultat numérique approché (numeric_tolerance)

Exécute le programme, extrait le premier nombre trouvé dans la sortie, et réussit s'il est proche de la valeur attendue à la tolérance près.

Champs à renseigner : Entrées, Valeur attendue, Tolérance.

Exemple d'usage : un calcul faisant intervenir des nombres décimaux (racine carrée, trigonométrie…) où de petites différences d'arrondi entre solutions sont normales — valeur attendue 78.54, tolérance 0.01 pour l'aire d'un cercle de rayon 5.

Plusieurs cas de test (test_cases)

Exécute le programme séparément pour chaque cas de test fourni (entrées + sortie attendue), et ne réussit que si tous les cas passent. C'est le principal outil pour éviter qu'une réponse écrite en dur ne soit acceptée par erreur.

Champ à renseigner : Cas de test (autant de paires entrées/sortie attendue que nécessaire).

Exemple d'usage : un exercice qui double un nombre lu au clavier — sans plusieurs cas, une solution comme Ecrire(4) passerait la vérification « Sortie exacte » pour l'entrée 2. Avec plusieurs cas couvrant aussi zéro et un nombre négatif, elle échoue dès le deuxième cas :

Entrée Sortie attendue
2 4
0 0
-3 -6

Aucune erreur d'exécution (no_runtime_error)

Exécute le programme et réussit s'il ne lève aucune erreur (division par zéro, accès hors limites d'un tableau, variable non déclarée…), quelle que soit la sortie produite.

Champ à renseigner : Entrées (si le programme lit des valeurs).

Exemple d'usage : à cocher bloquante dans la plupart des barèmes — si le programme plante, aucune des autres vérifications dynamiques n'a de sens, donc le score doit être forcé à 0 plutôt que de dépendre du hasard des autres poids.


Combiner les vérifications (barème hybride)

L'exemple le plus représentatif de l'intérêt du barème : un exercice « avec une boucle Pour, calculez la somme de 1 à 10 » peut combiner :

Vérification Poids Bloquant
Aucune erreur d'exécution 1 Oui
Doit utiliser Pour 2 Non
Sortie exacte (« 55 ») 2 Non

Une solution qui plante (division par zéro, par exemple) obtient 0 % quoi qu'il arrive. Une solution qui affiche 55 avec une boucle TantQue obtient 3/5 = 60 % (elle ne plante pas et affiche la bonne sortie, mais rate la vérification « Doit utiliser Pour ») — correcte dans le résultat, mais pas dans la méthode demandée. Seule une solution qui utilise Pour et affiche 55 obtient 100 %.

Conseils pratiques

  • Commencez simple : une seule vérification « Sortie exacte » reproduit le comportement de la correction classique — ajoutez les autres progressivement.
  • Réservez l'option bloquante aux vérifications qui rendent les autres résultats sans objet (erreur d'exécution, tout particulièrement).
  • Préférez « Plusieurs cas de test » à une seule « Sortie exacte » dès que l'exercice se prête à plusieurs entrées différentes — c'est ce qui empêche le plus efficacement une réponse écrite en dur de passer.
  • Un seuil de réussite inférieur à 100 % permet d'accorder du crédit partiel plutôt que tout-ou-rien ; gardez-le à 100 % si chaque vérification est réellement indispensable.
  • Le bouton « Supprimer le barème » sur la page de configuration ramène l'exercice ou le défi à la correction simple (comparaison de sortie), sans perdre les entrées/sortie attendues déjà renseignées.