{
  "chapter": {
    "id": "programmation",
    "level": "snt",
    "theme": "Programmation",
    "title": "9. Révision de fin d'année",
    "description": "Toutes les notions des huit chapitres de programmation mêlées :\nvariables, types, entrées et sorties, conditions, boucles bornées et\nnon bornées, fonctions, listes, lecture d'un message d'erreur. Pour\nréviser en fin d'année ou en début de première.",
    "parties": [
      {
        "id": "variables",
        "title": "Variables, types et affichage"
      },
      {
        "id": "conditions",
        "title": "Conditions"
      },
      {
        "id": "boucles",
        "title": "Boucles"
      },
      {
        "id": "fonctions",
        "title": "Fonctions"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "affectation",
        "sequence"
      ],
      "title": "Une suite d'affectations",
      "partie": "variables",
      "statement": "Ce programme note les dimensions d'une photographie, en pixels.\n```python\nlargeur = 1024\nhauteur = 768\nhauteur = largeur\nlargeur = 640\n```\nQue vaut `hauteur` à la fin ?",
      "options": [
        {
          "text": "768",
          "correct": false,
          "feedback": "Erreur : c'est la valeur du départ, mais la troisième ligne l'a\nremplacée par celle de `largeur`. Une affectation écrase sans\nprévenir ce que la variable contenait."
        },
        {
          "text": "1024",
          "correct": true,
          "feedback": "Bonne réponse : la troisième ligne recopie dans `hauteur` la valeur\nque `largeur` porte à cet instant, soit 1024. La ligne suivante ne\ntouche qu'à `largeur`."
        },
        {
          "text": "640",
          "correct": false,
          "feedback": "Erreur : suppose qu'une affectation crée un lien durable entre les\ndeux variables. Elle recopie une valeur une fois pour toutes, puis\nchaque variable vit de son côté."
        },
        {
          "text": "1792",
          "correct": false,
          "feedback": "Erreur : additionne les deux valeurs. Le signe `=` ne calcule rien,\nil range dans la variable de gauche la valeur trouvée à droite."
        }
      ],
      "explanation": "En Python, le signe `=` n'énonce pas une égalité mathématique, il range\nune valeur dans une variable. Une affectation se lit de droite à gauche,\net seule la variable écrite à gauche change.",
      "variants": [
        {
          "statement": "Ce programme note les dimensions d'une photographie, en pixels.\n```python\nlargeur = 1024\nhauteur = 768\nhauteur = largeur\nlargeur = 640\n```\nQue vaut `hauteur` à la fin ?",
          "options": [
            {
              "text": "768",
              "correct": false,
              "feedback": "Erreur : c'est la valeur du départ, mais la troisième ligne l'a\nremplacée par celle de `largeur`. Une affectation écrase sans\nprévenir ce que la variable contenait."
            },
            {
              "text": "1024",
              "correct": true,
              "feedback": "Bonne réponse : la troisième ligne recopie dans `hauteur` la valeur\nque `largeur` porte à cet instant, soit 1024. La ligne suivante ne\ntouche qu'à `largeur`."
            },
            {
              "text": "640",
              "correct": false,
              "feedback": "Erreur : suppose qu'une affectation crée un lien durable entre les\ndeux variables. Elle recopie une valeur une fois pour toutes, puis\nchaque variable vit de son côté."
            },
            {
              "text": "1792",
              "correct": false,
              "feedback": "Erreur : additionne les deux valeurs. Le signe `=` ne calcule rien,\nil range dans la variable de gauche la valeur trouvée à droite."
            }
          ],
          "explanation": "En Python, le signe `=` n'énonce pas une égalité mathématique, il range\nune valeur dans une variable. Une affectation se lit de droite à gauche,\net seule la variable écrite à gauche change."
        },
        {
          "statement": "Ce programme note les dimensions d'une photographie, en pixels.\n```python\nlargeur = 800\nhauteur = 600\nhauteur = largeur\nlargeur = 320\n```\nQue vaut `hauteur` à la fin ?",
          "options": [
            {
              "text": "600",
              "correct": false,
              "feedback": "Erreur : c'est la valeur du départ, mais la troisième ligne l'a\nremplacée par celle de `largeur`. Une affectation écrase sans\nprévenir ce que la variable contenait."
            },
            {
              "text": "800",
              "correct": true,
              "feedback": "Bonne réponse : la troisième ligne recopie dans `hauteur` la valeur\nque `largeur` porte à cet instant, soit 800. La ligne suivante ne\ntouche qu'à `largeur`."
            },
            {
              "text": "320",
              "correct": false,
              "feedback": "Erreur : suppose qu'une affectation crée un lien durable entre les\ndeux variables. Elle recopie une valeur une fois pour toutes, puis\nchaque variable vit de son côté."
            },
            {
              "text": "1400",
              "correct": false,
              "feedback": "Erreur : additionne les deux valeurs. Le signe `=` ne calcule rien,\nil range dans la variable de gauche la valeur trouvée à droite."
            }
          ],
          "explanation": "En Python, le signe `=` n'énonce pas une égalité mathématique, il range\nune valeur dans une variable. Une affectation se lit de droite à gauche,\net seule la variable écrite à gauche change."
        },
        {
          "statement": "Ce programme note les dimensions d'une photographie, en pixels.\n```python\nlargeur = 1920\nhauteur = 1080\nhauteur = largeur\nlargeur = 1280\n```\nQue vaut `hauteur` à la fin ?",
          "options": [
            {
              "text": "1080",
              "correct": false,
              "feedback": "Erreur : c'est la valeur du départ, mais la troisième ligne l'a\nremplacée par celle de `largeur`. Une affectation écrase sans\nprévenir ce que la variable contenait."
            },
            {
              "text": "1920",
              "correct": true,
              "feedback": "Bonne réponse : la troisième ligne recopie dans `hauteur` la valeur\nque `largeur` porte à cet instant, soit 1920. La ligne suivante ne\ntouche qu'à `largeur`."
            },
            {
              "text": "1280",
              "correct": false,
              "feedback": "Erreur : suppose qu'une affectation crée un lien durable entre les\ndeux variables. Elle recopie une valeur une fois pour toutes, puis\nchaque variable vit de son côté."
            },
            {
              "text": "3000",
              "correct": false,
              "feedback": "Erreur : additionne les deux valeurs. Le signe `=` ne calcule rien,\nil range dans la variable de gauche la valeur trouvée à droite."
            }
          ],
          "explanation": "En Python, le signe `=` n'énonce pas une égalité mathématique, il range\nune valeur dans une variable. Une affectation se lit de droite à gauche,\net seule la variable écrite à gauche change."
        }
      ]
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "types",
        "chaine"
      ],
      "title": "Repérer une chaîne de caractères",
      "partie": "variables",
      "statement": "Un objet connecté range quatre informations dans quatre variables.\n\n```python\nville = \"Lyon\"\naltitude = 173\nhumide = True\nvitesse = 12.5\n```\n\nLaquelle contient une chaîne de caractères ?",
      "options": [
        {
          "text": "`altitude`, qui contient `173`",
          "correct": false,
          "feedback": "Erreur : 173 est un nombre entier. Il s'écrit sans guillemets et\nsans point décimal, et se prête aux calculs."
        },
        {
          "text": "`humide`, qui contient `True`",
          "correct": false,
          "feedback": "Erreur : `True` est un booléen, l'une des deux seules valeurs de\nvérité. Il s'écrit sans guillemets et ne peut valoir que vrai ou\nfaux."
        },
        {
          "text": "`vitesse`, qui contient `12.5`",
          "correct": false,
          "feedback": "Erreur : 12.5 est un nombre flottant, reconnaissable à son point\ndécimal. Les guillemets sont absents."
        },
        {
          "text": "`ville`, qui contient `\"Lyon\"`",
          "correct": true,
          "feedback": "Bonne réponse : les guillemets font de `Lyon` une suite de\ncaractères, et non un nom de variable."
        }
      ],
      "explanation": "Les quatre types de base se reconnaissent à l'écriture : guillemets pour\nune chaîne, chiffres seuls pour un entier, point décimal pour un\nflottant, `True` ou `False` pour un booléen."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "affectation",
        "compteur"
      ],
      "title": "Un compteur de messages",
      "partie": "variables",
      "statement": "Une application compte les messages reçus.\n```python\nmessages = 0\nmessages = messages + 3\nmessages = messages + 2\nprint(messages)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "5",
          "correct": true,
          "feedback": "Bonne réponse : la variable part de 0, reçoit 3, puis 2 de plus.\nChaque ligne lit la valeur courante, ajoute un nombre et range le\ntotal au même endroit."
        },
        {
          "text": "3",
          "correct": false,
          "feedback": "Erreur : ne retient que la première addition. La ligne d'après\nrepart de 3 et ajoute encore 2."
        },
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : suppose que la dernière addition remplace tout ce qui a\nété calculé avant. Elle ajoute 2 à ce que la variable contient\ndéjà."
        },
        {
          "text": "0",
          "correct": false,
          "feedback": "Erreur : retient la valeur du départ. Les deux lignes qui suivent\nmodifient bel et bien la variable avant l'affichage."
        }
      ],
      "explanation": "La ligne `messages = messages + 3` se lit en deux temps : on calcule\nd'abord `messages + 3` avec la valeur actuelle, puis on range le\nrésultat dans `messages`. C'est le schéma de tout compteur.",
      "variants": [
        {
          "statement": "Une application compte les messages reçus.\n```python\nmessages = 0\nmessages = messages + 3\nmessages = messages + 2\nprint(messages)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "5",
              "correct": true,
              "feedback": "Bonne réponse : la variable part de 0, reçoit 3, puis 2 de plus.\nChaque ligne lit la valeur courante, ajoute un nombre et range le\ntotal au même endroit."
            },
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : ne retient que la première addition. La ligne d'après\nrepart de 3 et ajoute encore 2."
            },
            {
              "text": "2",
              "correct": false,
              "feedback": "Erreur : suppose que la dernière addition remplace tout ce qui a\nété calculé avant. Elle ajoute 2 à ce que la variable contient\ndéjà."
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : retient la valeur du départ. Les deux lignes qui suivent\nmodifient bel et bien la variable avant l'affichage."
            }
          ],
          "explanation": "La ligne `messages = messages + 3` se lit en deux temps : on calcule\nd'abord `messages + 3` avec la valeur actuelle, puis on range le\nrésultat dans `messages`. C'est le schéma de tout compteur."
        },
        {
          "statement": "Une application compte les messages reçus.\n```python\nmessages = 0\nmessages = messages + 5\nmessages = messages + 4\nprint(messages)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "9",
              "correct": true,
              "feedback": "Bonne réponse : la variable part de 0, reçoit 5, puis 4 de plus.\nChaque ligne lit la valeur courante, ajoute un nombre et range le\ntotal au même endroit."
            },
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : ne retient que la première addition. La ligne d'après\nrepart de 5 et ajoute encore 4."
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : suppose que la dernière addition remplace tout ce qui a\nété calculé avant. Elle ajoute 4 à ce que la variable contient\ndéjà."
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : retient la valeur du départ. Les deux lignes qui suivent\nmodifient bel et bien la variable avant l'affichage."
            }
          ],
          "explanation": "La ligne `messages = messages + 5` se lit en deux temps : on calcule\nd'abord `messages + 5` avec la valeur actuelle, puis on range le\nrésultat dans `messages`. C'est le schéma de tout compteur."
        },
        {
          "statement": "Une application compte les messages reçus.\n```python\nmessages = 0\nmessages = messages + 7\nmessages = messages + 1\nprint(messages)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "8",
              "correct": true,
              "feedback": "Bonne réponse : la variable part de 0, reçoit 7, puis 1 de plus.\nChaque ligne lit la valeur courante, ajoute un nombre et range le\ntotal au même endroit."
            },
            {
              "text": "7",
              "correct": false,
              "feedback": "Erreur : ne retient que la première addition. La ligne d'après\nrepart de 7 et ajoute encore 1."
            },
            {
              "text": "1",
              "correct": false,
              "feedback": "Erreur : suppose que la dernière addition remplace tout ce qui a\nété calculé avant. Elle ajoute 1 à ce que la variable contient\ndéjà."
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : retient la valeur du départ. Les deux lignes qui suivent\nmodifient bel et bien la variable avant l'affichage."
            }
          ],
          "explanation": "La ligne `messages = messages + 7` se lit en deux temps : on calcule\nd'abord `messages + 7` avec la valeur actuelle, puis on range le\nrésultat dans `messages`. C'est le schéma de tout compteur."
        }
      ]
    },
    {
      "id": "q04",
      "difficulty": 2,
      "skills": [
        "affectation",
        "echange"
      ],
      "title": "Un échange de coordonnées manqué",
      "partie": "variables",
      "statement": "Ce programme voudrait échanger la latitude et la longitude relevées par\nun récepteur.\n```python\nlat = 45\nlon = 4\nlat = lon\nlon = lat\nprint(lat, lon)\n```\nQu'affiche-t-il ?",
      "options": [
        {
          "text": "`45 4`",
          "correct": false,
          "feedback": "Erreur : ce sont les valeurs du départ, comme si les deux\naffectations n'avaient rien fait. La troisième ligne modifie\npourtant `lat`."
        },
        {
          "text": "`4 45`",
          "correct": false,
          "feedback": "Erreur : c'est l'échange souhaité, mais le programme ne l'obtient\npas. La valeur 45 disparaît dès que `lat` reçoit celle de `lon`."
        },
        {
          "text": "`4 4`",
          "correct": true,
          "feedback": "Bonne réponse : `lat` prend la valeur 4, puis `lon` reçoit cette\nmême valeur 4. L'ancienne valeur de `lat` n'existe plus nulle part."
        },
        {
          "text": "`45 45`",
          "correct": false,
          "feedback": "Erreur : c'est le résultat du même enchaînement mené dans l'autre\nsens, où `lon` recevrait d'abord la valeur de `lat`."
        }
      ],
      "explanation": "Échanger deux variables réclame une troisième variable, chargée de\ngarder la valeur qui va être écrasée. Sans elle, la première\naffectation détruit ce que la seconde devait recopier.",
      "variants": [
        {
          "statement": "Ce programme voudrait échanger la latitude et la longitude relevées par\nun récepteur.\n```python\nlat = 45\nlon = 4\nlat = lon\nlon = lat\nprint(lat, lon)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "`45 4`",
              "correct": false,
              "feedback": "Erreur : ce sont les valeurs du départ, comme si les deux\naffectations n'avaient rien fait. La troisième ligne modifie\npourtant `lat`."
            },
            {
              "text": "`4 45`",
              "correct": false,
              "feedback": "Erreur : c'est l'échange souhaité, mais le programme ne l'obtient\npas. La valeur 45 disparaît dès que `lat` reçoit celle de `lon`."
            },
            {
              "text": "`4 4`",
              "correct": true,
              "feedback": "Bonne réponse : `lat` prend la valeur 4, puis `lon` reçoit cette\nmême valeur 4. L'ancienne valeur de `lat` n'existe plus nulle part."
            },
            {
              "text": "`45 45`",
              "correct": false,
              "feedback": "Erreur : c'est le résultat du même enchaînement mené dans l'autre\nsens, où `lon` recevrait d'abord la valeur de `lat`."
            }
          ],
          "explanation": "Échanger deux variables réclame une troisième variable, chargée de\ngarder la valeur qui va être écrasée. Sans elle, la première\naffectation détruit ce que la seconde devait recopier."
        },
        {
          "statement": "Ce programme voudrait échanger la latitude et la longitude relevées par\nun récepteur.\n```python\nlat = 48\nlon = 2\nlat = lon\nlon = lat\nprint(lat, lon)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "`48 2`",
              "correct": false,
              "feedback": "Erreur : ce sont les valeurs du départ, comme si les deux\naffectations n'avaient rien fait. La troisième ligne modifie\npourtant `lat`."
            },
            {
              "text": "`2 48`",
              "correct": false,
              "feedback": "Erreur : c'est l'échange souhaité, mais le programme ne l'obtient\npas. La valeur 48 disparaît dès que `lat` reçoit celle de `lon`."
            },
            {
              "text": "`2 2`",
              "correct": true,
              "feedback": "Bonne réponse : `lat` prend la valeur 2, puis `lon` reçoit cette\nmême valeur 2. L'ancienne valeur de `lat` n'existe plus nulle part."
            },
            {
              "text": "`48 48`",
              "correct": false,
              "feedback": "Erreur : c'est le résultat du même enchaînement mené dans l'autre\nsens, où `lon` recevrait d'abord la valeur de `lat`."
            }
          ],
          "explanation": "Échanger deux variables réclame une troisième variable, chargée de\ngarder la valeur qui va être écrasée. Sans elle, la première\naffectation détruit ce que la seconde devait recopier."
        },
        {
          "statement": "Ce programme voudrait échanger la latitude et la longitude relevées par\nun récepteur.\n```python\nlat = 43\nlon = 7\nlat = lon\nlon = lat\nprint(lat, lon)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "`43 7`",
              "correct": false,
              "feedback": "Erreur : ce sont les valeurs du départ, comme si les deux\naffectations n'avaient rien fait. La troisième ligne modifie\npourtant `lat`."
            },
            {
              "text": "`7 43`",
              "correct": false,
              "feedback": "Erreur : c'est l'échange souhaité, mais le programme ne l'obtient\npas. La valeur 43 disparaît dès que `lat` reçoit celle de `lon`."
            },
            {
              "text": "`7 7`",
              "correct": true,
              "feedback": "Bonne réponse : `lat` prend la valeur 7, puis `lon` reçoit cette\nmême valeur 7. L'ancienne valeur de `lat` n'existe plus nulle part."
            },
            {
              "text": "`43 43`",
              "correct": false,
              "feedback": "Erreur : c'est le résultat du même enchaînement mené dans l'autre\nsens, où `lon` recevrait d'abord la valeur de `lat`."
            }
          ],
          "explanation": "Échanger deux variables réclame une troisième variable, chargée de\ngarder la valeur qui va être écrasée. Sans elle, la première\naffectation détruit ce que la seconde devait recopier."
        }
      ]
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "division-entiere",
        "operateurs"
      ],
      "title": "La division entière",
      "partie": "variables",
      "statement": "Un programme range 7 photographies sur des pages de 2 photographies.\n```python\nprint(7 // 2)\n```\nQu'affiche-t-il ?",
      "options": [
        {
          "text": "3.5",
          "correct": false,
          "feedback": "Erreur : c'est le résultat de `7 / 2`, la division ordinaire, qui\ndonne un flottant. La barre doublée demande le quotient entier."
        },
        {
          "text": "3",
          "correct": true,
          "feedback": "Bonne réponse : `//` donne le nombre de fois que 2 tient dans 7,\nsoit 3, en abandonnant la partie décimale."
        },
        {
          "text": "1",
          "correct": false,
          "feedback": "Erreur : c'est le résultat de `7 % 2`, qui donne le reste de la\ndivision et non le quotient."
        },
        {
          "text": "4",
          "correct": false,
          "feedback": "Erreur : arrondit 3.5 au plus proche. La division entière\nn'arrondit pas, elle abandonne la partie décimale."
        }
      ],
      "explanation": "Trois opérateurs cohabitent : `/` donne un quotient décimal, `//` le\nquotient entier, `%` le reste. Ici, 7 vaut 3 fois 2, plus 1.",
      "variants": [
        {
          "statement": "Un programme range 7 photographies sur des pages de 2 photographies.\n```python\nprint(7 // 2)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "3.5",
              "correct": false,
              "feedback": "Erreur : c'est le résultat de `7 / 2`, la division ordinaire, qui\ndonne un flottant. La barre doublée demande le quotient entier."
            },
            {
              "text": "3",
              "correct": true,
              "feedback": "Bonne réponse : `//` donne le nombre de fois que 2 tient dans 7,\nsoit 3, en abandonnant la partie décimale."
            },
            {
              "text": "1",
              "correct": false,
              "feedback": "Erreur : c'est le résultat de `7 % 2`, qui donne le reste de la\ndivision et non le quotient."
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : arrondit 3.5 au plus proche. La division entière\nn'arrondit pas, elle abandonne la partie décimale."
            }
          ],
          "explanation": "Trois opérateurs cohabitent : `/` donne un quotient décimal, `//` le\nquotient entier, `%` le reste. Ici, 7 vaut 3 fois 2, plus 1."
        },
        {
          "statement": "Un programme range 14 photographies sur des pages de 5 photographies.\n```python\nprint(14 // 5)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "2.8",
              "correct": false,
              "feedback": "Erreur : c'est le résultat de `14 / 5`, la division ordinaire, qui\ndonne un flottant. La barre doublée demande le quotient entier."
            },
            {
              "text": "2",
              "correct": true,
              "feedback": "Bonne réponse : `//` donne le nombre de fois que 5 tient dans 14,\nsoit 2, en abandonnant la partie décimale."
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : c'est le résultat de `14 % 5`, qui donne le reste de la\ndivision et non le quotient."
            },
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : arrondit 2.8 au plus proche. La division entière\nn'arrondit pas, elle abandonne la partie décimale."
            }
          ],
          "explanation": "Trois opérateurs cohabitent : `/` donne un quotient décimal, `//` le\nquotient entier, `%` le reste. Ici, 14 vaut 2 fois 5, plus 4."
        },
        {
          "statement": "Un programme range 19 photographies sur des pages de 4 photographies.\n```python\nprint(19 // 4)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "4.75",
              "correct": false,
              "feedback": "Erreur : c'est le résultat de `19 / 4`, la division ordinaire, qui\ndonne un flottant. La barre doublée demande le quotient entier."
            },
            {
              "text": "4",
              "correct": true,
              "feedback": "Bonne réponse : `//` donne le nombre de fois que 4 tient dans 19,\nsoit 4, en abandonnant la partie décimale."
            },
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : c'est le résultat de `19 % 4`, qui donne le reste de la\ndivision et non le quotient."
            },
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : arrondit 4.75 au plus proche. La division entière\nn'arrondit pas, elle abandonne la partie décimale."
            }
          ],
          "explanation": "Trois opérateurs cohabitent : `/` donne un quotient décimal, `//` le\nquotient entier, `%` le reste. Ici, 19 vaut 4 fois 4, plus 3."
        }
      ]
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "booleens",
        "and-or-not"
      ],
      "title": "Une condition avec and",
      "partie": "conditions",
      "statement": "L'éclairage d'un escalier lit deux capteurs.\n\n```python\nsombre = True\npresence = False\nprint(sombre and presence)\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`False`",
          "correct": true,
          "feedback": "Bonne réponse : `and` n'est vrai que si ses deux côtés le sont. Le\nsecond vaut ici `False`, donc le résultat aussi."
        },
        {
          "text": "`True`",
          "correct": false,
          "feedback": "Erreur : ce serait le résultat de `or`, vrai dès qu'un seul côté\nl'est. Avec `and`, les deux sont exigés en même temps."
        },
        {
          "text": "`True False`",
          "correct": false,
          "feedback": "Erreur : suppose que `print` affiche les deux variables. Il ne\nreçoit qu'une seule valeur, celle que `and` vient de calculer."
        },
        {
          "text": "`None`",
          "correct": false,
          "feedback": "Erreur : `None` marque l'absence de valeur. Une opération entre\nbooléens donne toujours `True` ou `False`."
        }
      ],
      "explanation": "`and` exige les deux conditions, `or` se contente d'une seule, `not`\ninverse. Ces trois mots suffisent à écrire la règle de commande d'un\nobjet connecté."
    },
    {
      "id": "q07",
      "difficulty": 2,
      "skills": [
        "condition",
        "and-or-not"
      ],
      "title": "La bonne condition d'alerte",
      "partie": "conditions",
      "statement": "Un capteur sur pile doit lancer une alerte lorsque son niveau de\nbatterie est strictement inférieur à 20 et que l'appareil n'est pas en\ncharge. La variable `en_charge` vaut `True` pendant une recharge.\nQuelle ligne convient ?",
      "options": [
        {
          "text": "```python\nif niveau < 20 or not en_charge:\n```",
          "correct": false,
          "feedback": "Erreur : avec `or`, une seule des deux conditions suffit. L'alerte\npartirait pour un capteur chargé à 80 %, au seul motif qu'il n'est\npas branché."
        },
        {
          "text": "```python\nif niveau < 20 and en_charge:\n```",
          "correct": false,
          "feedback": "Erreur : cette ligne réclame l'inverse, une batterie basse pendant\nla recharge. L'alerte ne partirait jamais pour un appareil\ndébranché."
        },
        {
          "text": "```python\nif not niveau < 20 and not en_charge:\n```",
          "correct": false,
          "feedback": "Erreur : `not niveau < 20` exige un niveau d'au moins 20. La\ncondition serait vraie pour un capteur plein et débranché, jamais\npour un capteur vide."
        },
        {
          "text": "```python\nif niveau < 20 and not en_charge:\n```",
          "correct": true,
          "feedback": "Bonne réponse : `and` réunit les deux exigences et `not` inverse la\nvaleur de `en_charge`, ce qui traduit « pas en charge »."
        }
      ],
      "explanation": "Traduire une phrase en condition demande de repérer le mot de liaison,\nici « et », et la négation, ici « n'est pas ». `and` traduit le premier,\n`not` la seconde."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "input",
        "saisie",
        "types"
      ],
      "title": "Ce que renvoie input",
      "partie": "variables",
      "statement": "Un questionnaire en ligne réclame un nombre.\n\n```python\nreponse = input(\"Combien de messages as-tu reçus ? \")\n```\n\nL'utilisateur tape 12. Que contient `reponse` ?",
      "options": [
        {
          "text": "L'entier 12, converti par Python",
          "correct": false,
          "feedback": "Erreur : aucune conversion n'a lieu d'elle-même. Obtenir un entier\ndemande de l'écrire, avec `int(reponse)`."
        },
        {
          "text": "Le flottant 12.0, issu du clavier",
          "correct": false,
          "feedback": "Erreur : aucun point décimal n'a été saisi, et le clavier ne\nconvertit rien. La saisie arrive telle qu'elle a été tapée."
        },
        {
          "text": "La chaîne de caractères « 12 »",
          "correct": true,
          "feedback": "Bonne réponse : `input` rend toujours une chaîne, faite ici des deux\ncaractères 1 et 2, même quand la saisie ressemble à un nombre."
        },
        {
          "text": "Le booléen True, la saisie n'étant pas vide",
          "correct": false,
          "feedback": "Erreur : un booléen ne sert qu'à répondre par vrai ou faux. `input`\nrend le texte saisi, sans le juger."
        }
      ],
      "explanation": "`input` rend toujours une chaîne de caractères. Pour calculer avec la\nsaisie, il faut la convertir, par exemple avec `int(reponse)` pour\nobtenir un entier."
    },
    {
      "id": "q09",
      "difficulty": 2,
      "skills": [
        "input",
        "types",
        "erreur"
      ],
      "title": "Une addition qui échoue",
      "partie": "variables",
      "statement": "Ce programme s'arrête sur une erreur.\n\n```python\nreponse = input(\"Nombre de messages : \")\ntotal = reponse + 1\nprint(total)\n```\n\nPourquoi ?",
      "options": [
        {
          "text": "Parce que la variable `total` n'a pas été créée avant d'être\nutilisée",
          "correct": false,
          "feedback": "Erreur : la deuxième ligne crée justement `total`. Une variable naît\nde sa première affectation, elle n'a pas à être annoncée."
        },
        {
          "text": "Parce que `input` renvoie une chaîne, et non un entier",
          "correct": true,
          "feedback": "Bonne réponse : Python refuse d'ajouter un entier à une chaîne et\nsignale `TypeError: can only concatenate str (not \"int\") to str`.\nIl faut écrire `int(reponse) + 1`."
        },
        {
          "text": "Parce que le texte affiché par `input` contient un deux-points",
          "correct": false,
          "feedback": "Erreur : le texte passé à `input` est un simple message d'invite.\nSon contenu n'a aucun effet sur le calcul qui suit."
        },
        {
          "text": "Parce que `print` ne sait afficher que des chaînes de caractères",
          "correct": false,
          "feedback": "Erreur : `print` affiche aussi bien un entier qu'une chaîne.\nL'erreur survient avant l'affichage, sur la ligne de l'addition."
        }
      ],
      "explanation": "Le signe `+` additionne deux nombres et met deux chaînes bout à bout,\nmais ne mélange pas les deux genres. Toute saisie destinée à un calcul\ndoit d'abord être convertie."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "comparaison",
        "condition"
      ],
      "title": "Tester une égalité",
      "partie": "variables",
      "statement": "Un thermostat doit réagir quand la température relevée vaut exactement\n30. Quelle ligne écrit ce test ?",
      "options": [
        {
          "text": "`if temperature == 30:`",
          "correct": true,
          "feedback": "Bonne réponse : le signe doublé compare deux valeurs et donne `True`\nou `False`. C'est exactement ce qu'une condition attend."
        },
        {
          "text": "`if temperature = 30:`",
          "correct": false,
          "feedback": "Erreur : le signe simple range une valeur dans une variable, il ne\ncompare rien. Python refuse la ligne et signale une `SyntaxError`."
        },
        {
          "text": "`if temperature === 30:`",
          "correct": false,
          "feedback": "Erreur : le signe triplé vient d'un autre langage. Python ne le\nconnaît pas et signale `SyntaxError: invalid syntax`."
        },
        {
          "text": "`if egale(temperature, 30):`",
          "correct": false,
          "feedback": "Erreur : aucune fonction de ce nom n'existe. Python signale\n`NameError: name 'egale' is not defined` au moment de l'appel."
        }
      ],
      "explanation": "Un signe simple affecte, un signe doublé compare. La confusion est si\nrépandue que Python propose lui-même la correction dans son message\nd'erreur."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "condition",
        "elif"
      ],
      "title": "La branche retenue",
      "partie": "conditions",
      "statement": "Un capteur de qualité de l'air range son relevé en trois catégories.\n\n```python\nindice = 55\nif indice < 50:\n    print(\"bon\")\nelif indice < 100:\n    print(\"moyen\")\nelse:\n    print(\"mauvais\")\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`bon`",
          "correct": false,
          "feedback": "Erreur : 55 n'est pas inférieur à 50. La première condition est\ndonc fausse et sa branche est sautée."
        },
        {
          "text": "`mauvais`",
          "correct": false,
          "feedback": "Erreur : la branche `else` ne sert que si aucune condition n'a été\ntrouvée vraie. Celle du `elif` l'est ici."
        },
        {
          "text": "`moyen`",
          "correct": true,
          "feedback": "Bonne réponse : la première condition est fausse, la deuxième est\nvraie puisque 55 est inférieur à 100. Seule sa branche s'exécute."
        },
        {
          "text": "`bon` puis `moyen`",
          "correct": false,
          "feedback": "Erreur : suppose que plusieurs branches peuvent s'exécuter. Un\nenchaînement `if`, `elif`, `else` n'en retient jamais qu'une."
        }
      ],
      "explanation": "Les conditions sont examinées de haut en bas et la première vraie\nl'emporte. Celles qui la suivent ne sont même pas évaluées."
    },
    {
      "id": "q12",
      "difficulty": 3,
      "skills": [
        "condition",
        "elif",
        "erreur"
      ],
      "title": "Une branche jamais atteinte",
      "partie": "conditions",
      "statement": "Ce programme classe une vitesse relevée par un radar, mais il n'affiche\njamais « autoroute », quelle que soit la vitesse.\n\n```python\nvitesse = 120\nif vitesse > 50:\n    print(\"hors agglomération\")\nelif vitesse > 110:\n    print(\"autoroute\")\nelse:\n    print(\"agglomération\")\n```\n\nComment l'expliquer ?",
      "options": [
        {
          "text": "Parce qu'une instruction `elif` ne peut pas tester deux fois la\nmême variable",
          "correct": false,
          "feedback": "Erreur : rien n'interdit de tester plusieurs fois la même variable.\nC'est même le procédé habituel pour découper un intervalle."
        },
        {
          "text": "Parce que la branche `else` est choisie dès que deux conditions\nsont vraies",
          "correct": false,
          "feedback": "Erreur : `else` n'entre en jeu que si toutes les conditions sont\nfausses. La première étant vraie, `else` est écarté."
        },
        {
          "text": "Parce qu'il manque un `if` devant la seconde condition, qui n'est\njamais lue",
          "correct": false,
          "feedback": "Erreur : `elif` joue déjà le rôle d'un `if` rattaché au premier\ntest. La ligne est correctement écrite, c'est son rang qui pose\nproblème."
        },
        {
          "text": "Parce que toute vitesse dépassant 110 dépasse déjà 50, si bien que\nla première branche l'emporte",
          "correct": true,
          "feedback": "Bonne réponse : 120 est supérieur à 50, donc la première branche est\nretenue et le reste de l'enchaînement est ignoré. Il faudrait\ntester d'abord le seuil le plus élevé."
        }
      ],
      "explanation": "Dans un enchaînement de conditions, l'ordre compte autant que les\nseuils. Quand les intervalles se recouvrent, la condition la plus\nexigeante doit venir en tête."
    },
    {
      "id": "q13",
      "difficulty": 1,
      "skills": [
        "boucle-bornee",
        "for",
        "range"
      ],
      "title": "Le nombre de tours d'une boucle",
      "partie": "boucles",
      "statement": "Un programme de test envoie plusieurs requêtes à un serveur.\n\n```python\nfor i in range(5):\n    print(\"requête envoyée\")\n```\n\nCombien de fois le message est-il affiché ?",
      "options": [
        {
          "text": "4 fois, la valeur 0 ne comptant pas pour un tour",
          "correct": false,
          "feedback": "Erreur : `range(5)` commence bel et bien à 0, et ce premier passage\nexécute la ligne comme tous les autres."
        },
        {
          "text": "5 fois, une par valeur parcourue",
          "correct": true,
          "feedback": "Bonne réponse : `range(5)` fournit 0, 1, 2, 3 et 4, soit cinq\nvaleurs, donc cinq tours de boucle."
        },
        {
          "text": "6 fois, les valeurs allant de 0 à 5 comprises",
          "correct": false,
          "feedback": "Erreur : la borne écrite est exclue. `range(5)` s'arrête à 4 et ne\nfournit jamais la valeur 5."
        },
        {
          "text": "1 fois, seul le dernier tour affichant le message",
          "correct": false,
          "feedback": "Erreur : la ligne indentée est exécutée à chaque tour, et non au\nseul dernier. C'est tout l'intérêt d'une boucle."
        }
      ],
      "explanation": "`range(n)` parcourt les entiers de 0 à `n - 1`, soit exactement `n`\nvaleurs. Le nombre de tours se lit donc directement sur l'argument."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "for",
        "range",
        "compteur"
      ],
      "title": "Une plage de valeurs",
      "partie": "boucles",
      "statement": "Un objet connecté effectue un relevé à chaque heure d'une plage\nhoraire.\n```python\ncompte = 0\nfor heure in range(2, 8):\n    compte = compte + 1\nprint(compte)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "6",
          "correct": true,
          "feedback": "Bonne réponse : la boucle parcourt 2, 3, 4, 5, 6 et 7, soit 6\nvaleurs. La borne 8 est exclue."
        },
        {
          "text": "5",
          "correct": false,
          "feedback": "Erreur : retire une valeur de trop. La borne de départ, elle, est\nbien parcourue."
        },
        {
          "text": "7",
          "correct": false,
          "feedback": "Erreur : compte la borne 8, qui est pourtant exclue. La boucle\ns'arrête à 7."
        },
        {
          "text": "8",
          "correct": false,
          "feedback": "Erreur : prend la borne de fin pour le nombre de tours. Le départ\nà 2 retire 2 valeurs au compte."
        }
      ],
      "explanation": "`range(a, b)` parcourt les entiers de `a` à `b - 1`, soit `b - a`\nvaleurs. Ici, 8 moins 2 donne bien 6 tours.",
      "variants": [
        {
          "statement": "Un objet connecté effectue un relevé à chaque heure d'une plage\nhoraire.\n```python\ncompte = 0\nfor heure in range(2, 8):\n    compte = compte + 1\nprint(compte)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "6",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt 2, 3, 4, 5, 6 et 7, soit 6\nvaleurs. La borne 8 est exclue."
            },
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : retire une valeur de trop. La borne de départ, elle, est\nbien parcourue."
            },
            {
              "text": "7",
              "correct": false,
              "feedback": "Erreur : compte la borne 8, qui est pourtant exclue. La boucle\ns'arrête à 7."
            },
            {
              "text": "8",
              "correct": false,
              "feedback": "Erreur : prend la borne de fin pour le nombre de tours. Le départ\nà 2 retire 2 valeurs au compte."
            }
          ],
          "explanation": "`range(a, b)` parcourt les entiers de `a` à `b - 1`, soit `b - a`\nvaleurs. Ici, 8 moins 2 donne bien 6 tours."
        },
        {
          "statement": "Un objet connecté effectue un relevé à chaque heure d'une plage\nhoraire.\n```python\ncompte = 0\nfor heure in range(3, 10):\n    compte = compte + 1\nprint(compte)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "7",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt 3, 4, 5, 6, 7, 8 et 9, soit 7\nvaleurs. La borne 10 est exclue."
            },
            {
              "text": "6",
              "correct": false,
              "feedback": "Erreur : retire une valeur de trop. La borne de départ, elle, est\nbien parcourue."
            },
            {
              "text": "8",
              "correct": false,
              "feedback": "Erreur : compte la borne 10, qui est pourtant exclue. La boucle\ns'arrête à 9."
            },
            {
              "text": "10",
              "correct": false,
              "feedback": "Erreur : prend la borne de fin pour le nombre de tours. Le départ\nà 3 retire 3 valeurs au compte."
            }
          ],
          "explanation": "`range(a, b)` parcourt les entiers de `a` à `b - 1`, soit `b - a`\nvaleurs. Ici, 10 moins 3 donne bien 7 tours."
        },
        {
          "statement": "Un objet connecté effectue un relevé à chaque heure d'une plage\nhoraire.\n```python\ncompte = 0\nfor heure in range(6, 15):\n    compte = compte + 1\nprint(compte)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "9",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt 6, 7, 8, 9, 10, 11, 12, 13 et 14, soit 9\nvaleurs. La borne 15 est exclue."
            },
            {
              "text": "8",
              "correct": false,
              "feedback": "Erreur : retire une valeur de trop. La borne de départ, elle, est\nbien parcourue."
            },
            {
              "text": "10",
              "correct": false,
              "feedback": "Erreur : compte la borne 15, qui est pourtant exclue. La boucle\ns'arrête à 14."
            },
            {
              "text": "15",
              "correct": false,
              "feedback": "Erreur : prend la borne de fin pour le nombre de tours. Le départ\nà 6 retire 6 valeurs au compte."
            }
          ],
          "explanation": "`range(a, b)` parcourt les entiers de `a` à `b - 1`, soit `b - a`\nvaleurs. Ici, 15 moins 6 donne bien 9 tours."
        }
      ]
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "while",
        "trace",
        "compteur"
      ],
      "title": "Une file de signalements traitée par lots",
      "partie": "boucles",
      "statement": "Une équipe de modération traite 100 signalements par lots de 30.\n```python\nrestants = 100\ntours = 0\nwhile restants > 0:\n    restants = restants - 30\n    tours = tours + 1\nprint(tours, restants)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`3 10`",
          "correct": false,
          "feedback": "Erreur : après 3 tours il reste 10 signalements, ce qui est\nstrictement supérieur à 0. La condition est encore vraie et un\ntour de plus a lieu."
        },
        {
          "text": "`4 0`",
          "correct": false,
          "feedback": "Erreur : la soustraction retire 30 à chaque tour, sans s'ajuster au\nreste. Le dernier tour fait passer le total sous zéro."
        },
        {
          "text": "`4 -20`",
          "correct": true,
          "feedback": "Bonne réponse : les valeurs passent par 70, 40, 10 puis -20, soit\n4 tours. La condition n'est examinée qu'en début de tour."
        },
        {
          "text": "`5 -50`",
          "correct": false,
          "feedback": "Erreur : ajoute un tour de trop. Dès que `restants` vaut -20, la\ncondition est fausse et la boucle s'arrête."
        }
      ],
      "explanation": "Une boucle `while` examine sa condition avant chaque tour, jamais au\nmilieu. Le dernier lot est donc entamé en entier, même s'il dépasse ce\nqui restait à traiter.",
      "variants": [
        {
          "statement": "Une équipe de modération traite 100 signalements par lots de 30.\n```python\nrestants = 100\ntours = 0\nwhile restants > 0:\n    restants = restants - 30\n    tours = tours + 1\nprint(tours, restants)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`3 10`",
              "correct": false,
              "feedback": "Erreur : après 3 tours il reste 10 signalements, ce qui est\nstrictement supérieur à 0. La condition est encore vraie et un\ntour de plus a lieu."
            },
            {
              "text": "`4 0`",
              "correct": false,
              "feedback": "Erreur : la soustraction retire 30 à chaque tour, sans s'ajuster au\nreste. Le dernier tour fait passer le total sous zéro."
            },
            {
              "text": "`4 -20`",
              "correct": true,
              "feedback": "Bonne réponse : les valeurs passent par 70, 40, 10 puis -20, soit\n4 tours. La condition n'est examinée qu'en début de tour."
            },
            {
              "text": "`5 -50`",
              "correct": false,
              "feedback": "Erreur : ajoute un tour de trop. Dès que `restants` vaut -20, la\ncondition est fausse et la boucle s'arrête."
            }
          ],
          "explanation": "Une boucle `while` examine sa condition avant chaque tour, jamais au\nmilieu. Le dernier lot est donc entamé en entier, même s'il dépasse ce\nqui restait à traiter."
        },
        {
          "statement": "Une équipe de modération traite 90 signalements par lots de 20.\n```python\nrestants = 90\ntours = 0\nwhile restants > 0:\n    restants = restants - 20\n    tours = tours + 1\nprint(tours, restants)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`4 10`",
              "correct": false,
              "feedback": "Erreur : après 4 tours il reste 10 signalements, ce qui est\nstrictement supérieur à 0. La condition est encore vraie et un\ntour de plus a lieu."
            },
            {
              "text": "`5 0`",
              "correct": false,
              "feedback": "Erreur : la soustraction retire 20 à chaque tour, sans s'ajuster au\nreste. Le dernier tour fait passer le total sous zéro."
            },
            {
              "text": "`5 -10`",
              "correct": true,
              "feedback": "Bonne réponse : les valeurs passent par 70, 50, 30, 10 puis -10, soit\n5 tours. La condition n'est examinée qu'en début de tour."
            },
            {
              "text": "`6 -30`",
              "correct": false,
              "feedback": "Erreur : ajoute un tour de trop. Dès que `restants` vaut -10, la\ncondition est fausse et la boucle s'arrête."
            }
          ],
          "explanation": "Une boucle `while` examine sa condition avant chaque tour, jamais au\nmilieu. Le dernier lot est donc entamé en entier, même s'il dépasse ce\nqui restait à traiter."
        },
        {
          "statement": "Une équipe de modération traite 120 signalements par lots de 50.\n```python\nrestants = 120\ntours = 0\nwhile restants > 0:\n    restants = restants - 50\n    tours = tours + 1\nprint(tours, restants)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`2 20`",
              "correct": false,
              "feedback": "Erreur : après 2 tours il reste 20 signalements, ce qui est\nstrictement supérieur à 0. La condition est encore vraie et un\ntour de plus a lieu."
            },
            {
              "text": "`3 0`",
              "correct": false,
              "feedback": "Erreur : la soustraction retire 50 à chaque tour, sans s'ajuster au\nreste. Le dernier tour fait passer le total sous zéro."
            },
            {
              "text": "`3 -30`",
              "correct": true,
              "feedback": "Bonne réponse : les valeurs passent par 70, 20 puis -30, soit\n3 tours. La condition n'est examinée qu'en début de tour."
            },
            {
              "text": "`4 -80`",
              "correct": false,
              "feedback": "Erreur : ajoute un tour de trop. Dès que `restants` vaut -30, la\ncondition est fausse et la boucle s'arrête."
            }
          ],
          "explanation": "Une boucle `while` examine sa condition avant chaque tour, jamais au\nmilieu. Le dernier lot est donc entamé en entier, même s'il dépasse ce\nqui restait à traiter."
        }
      ]
    },
    {
      "id": "q16",
      "difficulty": 3,
      "skills": [
        "boucle-non-bornee",
        "while",
        "erreur"
      ],
      "title": "Une boucle qui ne s'arrête pas",
      "partie": "boucles",
      "statement": "Ce programme doit décompter les blocs d'un fichier restant à recevoir.\n\n```python\nrestants = 10\nwhile restants > 0:\n    print(restants)\n```\n\nQue fait-il réellement ?",
      "options": [
        {
          "text": "Il affiche 10 une seule fois, puis passe à la suite",
          "correct": false,
          "feedback": "Erreur : `while` n'exécute pas son bloc une fois pour toutes. Il le\nrépète tant que la condition reste vraie."
        },
        {
          "text": "Il n'affiche rien, la condition étant fausse au départ",
          "correct": false,
          "feedback": "Erreur : 10 est bien supérieur à 0. La condition est donc vraie dès\nle premier examen et la boucle démarre."
        },
        {
          "text": "Il affiche les nombres de 10 jusqu'à 1, puis s'arrête",
          "correct": false,
          "feedback": "Erreur : c'est le comportement attendu, mais aucune ligne ne\ndiminue `restants`. Il faudrait ajouter `restants = restants - 1`\ndans le bloc."
        },
        {
          "text": "Il affiche 10 sans jamais s'arrêter",
          "correct": true,
          "feedback": "Bonne réponse : rien ne modifie `restants`, qui vaut 10 à chaque\nexamen de la condition. Le même nombre défile indéfiniment."
        }
      ],
      "explanation": "Toute boucle `while` réclame une instruction qui rapproche la condition\nde sa fin. Sans elle, le programme tourne sans terme et il faut\nl'interrompre à la main."
    },
    {
      "id": "q17",
      "difficulty": 3,
      "skills": [
        "while",
        "trace",
        "croissance"
      ],
      "title": "Une mémoire qui se remplit",
      "partie": "boucles",
      "statement": "Le nombre de relevés stockés par un capteur est multiplié par trois\nchaque jour.\n```python\nreleves = 5\njours = 0\nwhile releves < 100:\n    releves = releves * 3\n    jours = jours + 1\nprint(jours, releves)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`3 135`",
          "correct": true,
          "feedback": "Bonne réponse : les valeurs passent par 15, 45 puis 135, soit 3\ntours. Le dernier a lieu parce que 45 est encore inférieur à 100."
        },
        {
          "text": "`2 45`",
          "correct": false,
          "feedback": "Erreur : s'arrête à 45, qui est pourtant inférieur à 100. La\ncondition reste vraie, donc un tour de plus a lieu."
        },
        {
          "text": "`3 45`",
          "correct": false,
          "feedback": "Erreur : compte 3 tours mais garde la valeur du tour précédent. Le\ndernier tour multiplie encore par 3."
        },
        {
          "text": "`4 135`",
          "correct": false,
          "feedback": "Erreur : ajoute un tour de trop. Une fois `releves` à 135, la\ncondition est fausse et la boucle s'arrête."
        }
      ],
      "explanation": "Une multiplication répétée fait grimper une valeur très vite : 3\ntours suffisent à passer de 5 à 135. La boucle s'arrête au premier\nexamen où la condition devient fausse.",
      "variants": [
        {
          "statement": "Le nombre de relevés stockés par un capteur est multiplié par trois\nchaque jour.\n```python\nreleves = 5\njours = 0\nwhile releves < 100:\n    releves = releves * 3\n    jours = jours + 1\nprint(jours, releves)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`3 135`",
              "correct": true,
              "feedback": "Bonne réponse : les valeurs passent par 15, 45 puis 135, soit 3\ntours. Le dernier a lieu parce que 45 est encore inférieur à 100."
            },
            {
              "text": "`2 45`",
              "correct": false,
              "feedback": "Erreur : s'arrête à 45, qui est pourtant inférieur à 100. La\ncondition reste vraie, donc un tour de plus a lieu."
            },
            {
              "text": "`3 45`",
              "correct": false,
              "feedback": "Erreur : compte 3 tours mais garde la valeur du tour précédent. Le\ndernier tour multiplie encore par 3."
            },
            {
              "text": "`4 135`",
              "correct": false,
              "feedback": "Erreur : ajoute un tour de trop. Une fois `releves` à 135, la\ncondition est fausse et la boucle s'arrête."
            }
          ],
          "explanation": "Une multiplication répétée fait grimper une valeur très vite : 3\ntours suffisent à passer de 5 à 135. La boucle s'arrête au premier\nexamen où la condition devient fausse."
        },
        {
          "statement": "Le nombre de relevés stockés par un capteur est multiplié par deux\nchaque jour.\n```python\nreleves = 4\njours = 0\nwhile releves < 50:\n    releves = releves * 2\n    jours = jours + 1\nprint(jours, releves)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`4 64`",
              "correct": true,
              "feedback": "Bonne réponse : les valeurs passent par 8, 16, 32 puis 64, soit 4\ntours. Le dernier a lieu parce que 32 est encore inférieur à 50."
            },
            {
              "text": "`3 32`",
              "correct": false,
              "feedback": "Erreur : s'arrête à 32, qui est pourtant inférieur à 50. La\ncondition reste vraie, donc un tour de plus a lieu."
            },
            {
              "text": "`4 32`",
              "correct": false,
              "feedback": "Erreur : compte 4 tours mais garde la valeur du tour précédent. Le\ndernier tour multiplie encore par 2."
            },
            {
              "text": "`5 64`",
              "correct": false,
              "feedback": "Erreur : ajoute un tour de trop. Une fois `releves` à 64, la\ncondition est fausse et la boucle s'arrête."
            }
          ],
          "explanation": "Une multiplication répétée fait grimper une valeur très vite : 4\ntours suffisent à passer de 4 à 64. La boucle s'arrête au premier\nexamen où la condition devient fausse."
        },
        {
          "statement": "Le nombre de relevés stockés par un capteur est multiplié par deux\nchaque jour.\n```python\nreleves = 6\njours = 0\nwhile releves < 100:\n    releves = releves * 2\n    jours = jours + 1\nprint(jours, releves)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`5 192`",
              "correct": true,
              "feedback": "Bonne réponse : les valeurs passent par 12, 24, 48, 96 puis 192, soit 5\ntours. Le dernier a lieu parce que 96 est encore inférieur à 100."
            },
            {
              "text": "`4 96`",
              "correct": false,
              "feedback": "Erreur : s'arrête à 96, qui est pourtant inférieur à 100. La\ncondition reste vraie, donc un tour de plus a lieu."
            },
            {
              "text": "`5 96`",
              "correct": false,
              "feedback": "Erreur : compte 5 tours mais garde la valeur du tour précédent. Le\ndernier tour multiplie encore par 2."
            },
            {
              "text": "`6 192`",
              "correct": false,
              "feedback": "Erreur : ajoute un tour de trop. Une fois `releves` à 192, la\ncondition est fausse et la boucle s'arrête."
            }
          ],
          "explanation": "Une multiplication répétée fait grimper une valeur très vite : 5\ntours suffisent à passer de 6 à 192. La boucle s'arrête au premier\nexamen où la condition devient fausse."
        }
      ]
    },
    {
      "id": "q18",
      "difficulty": 1,
      "skills": [
        "for",
        "range",
        "pas"
      ],
      "title": "Une boucle de cinq en cinq",
      "partie": "boucles",
      "statement": "Un programme de cartographie place un repère tous les cinq\nkilomètres le long d'un parcours.\n```python\ncompteur = 0\nfor borne in range(5, 31, 5):\n    compteur = compteur + 1\nprint(compteur)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`6`",
          "correct": true,
          "feedback": "Bonne réponse : la boucle parcourt 5, 10, 15, 20, 25 et 30,\nsoit 6 valeurs, car le pas de 5 part de 5 et s'arrête avant\n31. Le compteur est donc incrémenté 6 fois."
        },
        {
          "text": "`5`",
          "correct": false,
          "feedback": "Erreur : tu as oublié la première valeur, 5, ou la dernière,\n30. Les valeurs parcourues sont 5, 10, 15, 20, 25 et 30."
        },
        {
          "text": "`26`",
          "correct": false,
          "feedback": "Erreur : tu as compté les entiers de 5 à 30 en ignorant le\npas. Le troisième nombre de `range` fixe justement l'écart\nentre deux valeurs successives."
        },
        {
          "text": "`31`",
          "correct": false,
          "feedback": "Erreur : tu as pris la borne de fin pour le nombre de tours.\nCette borne est exclue, et le pas de 5 ne retient que 6\nvaleurs."
        }
      ],
      "explanation": "Dans `range(debut, fin, pas)`, la boucle part de `debut`, avance\nde `pas` à chaque tour et s'arrête avant `fin`. Ici elle prend les\n6 valeurs 5, 10, 15, 20, 25 et 30.",
      "variants": [
        {
          "statement": "Un programme de cartographie place un repère tous les cinq\nkilomètres le long d'un parcours.\n```python\ncompteur = 0\nfor borne in range(5, 31, 5):\n    compteur = compteur + 1\nprint(compteur)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`6`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt 5, 10, 15, 20, 25 et 30,\nsoit 6 valeurs, car le pas de 5 part de 5 et s'arrête avant\n31. Le compteur est donc incrémenté 6 fois."
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : tu as oublié la première valeur, 5, ou la dernière,\n30. Les valeurs parcourues sont 5, 10, 15, 20, 25 et 30."
            },
            {
              "text": "`26`",
              "correct": false,
              "feedback": "Erreur : tu as compté les entiers de 5 à 30 en ignorant le\npas. Le troisième nombre de `range` fixe justement l'écart\nentre deux valeurs successives."
            },
            {
              "text": "`31`",
              "correct": false,
              "feedback": "Erreur : tu as pris la borne de fin pour le nombre de tours.\nCette borne est exclue, et le pas de 5 ne retient que 6\nvaleurs."
            }
          ],
          "explanation": "Dans `range(debut, fin, pas)`, la boucle part de `debut`, avance\nde `pas` à chaque tour et s'arrête avant `fin`. Ici elle prend les\n6 valeurs 5, 10, 15, 20, 25 et 30."
        },
        {
          "statement": "Un programme de cartographie place un repère tous les dix\nkilomètres le long d'un parcours.\n```python\ncompteur = 0\nfor borne in range(10, 101, 10):\n    compteur = compteur + 1\nprint(compteur)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`10`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt 10, 20, 30, 40, 50, 60, 70, 80, 90 et 100,\nsoit 10 valeurs, car le pas de 10 part de 10 et s'arrête avant\n101. Le compteur est donc incrémenté 10 fois."
            },
            {
              "text": "`9`",
              "correct": false,
              "feedback": "Erreur : tu as oublié la première valeur, 10, ou la dernière,\n100. Les valeurs parcourues sont 10, 20, 30, 40, 50, 60, 70, 80, 90 et 100."
            },
            {
              "text": "`91`",
              "correct": false,
              "feedback": "Erreur : tu as compté les entiers de 10 à 100 en ignorant le\npas. Le troisième nombre de `range` fixe justement l'écart\nentre deux valeurs successives."
            },
            {
              "text": "`101`",
              "correct": false,
              "feedback": "Erreur : tu as pris la borne de fin pour le nombre de tours.\nCette borne est exclue, et le pas de 10 ne retient que 10\nvaleurs."
            }
          ],
          "explanation": "Dans `range(debut, fin, pas)`, la boucle part de `debut`, avance\nde `pas` à chaque tour et s'arrête avant `fin`. Ici elle prend les\n10 valeurs 10, 20, 30, 40, 50, 60, 70, 80, 90 et 100."
        },
        {
          "statement": "Un programme de cartographie place un repère tous les trois\nkilomètres le long d'un parcours.\n```python\ncompteur = 0\nfor borne in range(3, 22, 3):\n    compteur = compteur + 1\nprint(compteur)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`7`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle parcourt 3, 6, 9, 12, 15, 18 et 21,\nsoit 7 valeurs, car le pas de 3 part de 3 et s'arrête avant\n22. Le compteur est donc incrémenté 7 fois."
            },
            {
              "text": "`6`",
              "correct": false,
              "feedback": "Erreur : tu as oublié la première valeur, 3, ou la dernière,\n21. Les valeurs parcourues sont 3, 6, 9, 12, 15, 18 et 21."
            },
            {
              "text": "`19`",
              "correct": false,
              "feedback": "Erreur : tu as compté les entiers de 3 à 21 en ignorant le\npas. Le troisième nombre de `range` fixe justement l'écart\nentre deux valeurs successives."
            },
            {
              "text": "`22`",
              "correct": false,
              "feedback": "Erreur : tu as pris la borne de fin pour le nombre de tours.\nCette borne est exclue, et le pas de 3 ne retient que 7\nvaleurs."
            }
          ],
          "explanation": "Dans `range(debut, fin, pas)`, la boucle part de `debut`, avance\nde `pas` à chaque tour et s'arrête avant `fin`. Ici elle prend les\n7 valeurs 3, 6, 9, 12, 15, 18 et 21."
        }
      ]
    },
    {
      "id": "q19",
      "difficulty": 1,
      "skills": [
        "condition",
        "elif"
      ],
      "title": "La branche d'un relevé doux",
      "partie": "conditions",
      "statement": "Une station météo qualifie la température relevée.\n```python\ntemp = 19\nif temp < 5:\n    resultat = \"gel\"\nelif temp < 18:\n    resultat = \"frais\"\nelif temp < 25:\n    resultat = \"doux\"\nelse:\n    resultat = \"chaud\"\nprint(resultat)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`frais`",
          "correct": false,
          "feedback": "Erreur : la condition `temp < 18` est fausse pour 19, donc\ncette branche est ignorée et l'examen se poursuit avec la\nsuivante."
        },
        {
          "text": "`doux`",
          "correct": true,
          "feedback": "Bonne réponse : 19 n'est ni inférieur à 5 ni inférieur à 18,\nmais il est inférieur à 25. C'est donc la troisième branche\nqui s'exécute."
        },
        {
          "text": "`chaud`",
          "correct": false,
          "feedback": "Erreur : la branche `else` ne s'exécute que si toutes les\nconditions précédentes sont fausses. Or `temp < 25` est vraie\npour 19."
        },
        {
          "text": "`frais` puis `doux`",
          "correct": false,
          "feedback": "Erreur : dans une suite `if`, `elif`, `else`, une seule\nbranche s'exécute. Dès qu'une condition est vraie, les autres\nne sont plus examinées."
        }
      ],
      "explanation": "Les conditions d'une suite `if`, `elif`, `else` sont examinées\ndans l'ordre, et la première qui est vraie emporte tout : son bloc\ns'exécute, les suivants sont abandonnés."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "while",
        "division-entiere",
        "compteur"
      ],
      "title": "Un partage répété par deux",
      "partie": "boucles",
      "statement": "Un programme réduit de moitié la taille d'une image tant qu'elle\ndépasse dix unités.\n```python\nreste = 100\ntours = 0\nwhile reste > 10:\n    reste = reste // 2\n    tours = tours + 1\nprint(tours)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`4`",
          "correct": true,
          "feedback": "Bonne réponse : `reste` prend successivement 50, 25, 12 puis 6. À ce moment la condition devient fausse, après 4\ntours."
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : tu t'es arrêté à 12, qui dépasse encore 10. Un tour\nsupplémentaire a lieu et ramène `reste` à 6."
        },
        {
          "text": "`5`",
          "correct": false,
          "feedback": "Erreur : tu as compté un tour de trop. Une fois `reste` à 6,\nla condition est fausse et la boucle s'arrête sans exécuter\nson bloc."
        },
        {
          "text": "`10`",
          "correct": false,
          "feedback": "Erreur : tu as pris le seuil de la condition pour le nombre\nde tours. Comme la valeur est divisée par deux à chaque fois,\nelle descend bien plus vite."
        }
      ],
      "explanation": "Une boucle non bornée répète son bloc tant que sa condition est\nvraie, sans qu'on sache d'avance combien de tours auront lieu.\nIci, la division entière par deux mène de 100 à 6 en 4 tours.",
      "variants": [
        {
          "statement": "Un programme réduit de moitié la taille d'une image tant qu'elle\ndépasse dix unités.\n```python\nreste = 100\ntours = 0\nwhile reste > 10:\n    reste = reste // 2\n    tours = tours + 1\nprint(tours)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`4`",
              "correct": true,
              "feedback": "Bonne réponse : `reste` prend successivement 50, 25, 12 puis 6. À ce moment la condition devient fausse, après 4\ntours."
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : tu t'es arrêté à 12, qui dépasse encore 10. Un tour\nsupplémentaire a lieu et ramène `reste` à 6."
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : tu as compté un tour de trop. Une fois `reste` à 6,\nla condition est fausse et la boucle s'arrête sans exécuter\nson bloc."
            },
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : tu as pris le seuil de la condition pour le nombre\nde tours. Comme la valeur est divisée par deux à chaque fois,\nelle descend bien plus vite."
            }
          ],
          "explanation": "Une boucle non bornée répète son bloc tant que sa condition est\nvraie, sans qu'on sache d'avance combien de tours auront lieu.\nIci, la division entière par deux mène de 100 à 6 en 4 tours."
        },
        {
          "statement": "Un programme réduit de moitié la taille d'une image tant qu'elle\ndépasse dix unités.\n```python\nreste = 60\ntours = 0\nwhile reste > 10:\n    reste = reste // 2\n    tours = tours + 1\nprint(tours)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`3`",
              "correct": true,
              "feedback": "Bonne réponse : `reste` prend successivement 30, 15 puis 7. À ce moment la condition devient fausse, après 3\ntours."
            },
            {
              "text": "`2`",
              "correct": false,
              "feedback": "Erreur : tu t'es arrêté à 15, qui dépasse encore 10. Un tour\nsupplémentaire a lieu et ramène `reste` à 7."
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : tu as compté un tour de trop. Une fois `reste` à 7,\nla condition est fausse et la boucle s'arrête sans exécuter\nson bloc."
            },
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : tu as pris le seuil de la condition pour le nombre\nde tours. Comme la valeur est divisée par deux à chaque fois,\nelle descend bien plus vite."
            }
          ],
          "explanation": "Une boucle non bornée répète son bloc tant que sa condition est\nvraie, sans qu'on sache d'avance combien de tours auront lieu.\nIci, la division entière par deux mène de 60 à 7 en 3 tours."
        },
        {
          "statement": "Un programme réduit de moitié la taille d'une image tant qu'elle\ndépasse trente unités.\n```python\nreste = 1000\ntours = 0\nwhile reste > 30:\n    reste = reste // 2\n    tours = tours + 1\nprint(tours)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`6`",
              "correct": true,
              "feedback": "Bonne réponse : `reste` prend successivement 500, 250, 125, 62, 31 puis 15. À ce moment la condition devient fausse, après 6\ntours."
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : tu t'es arrêté à 31, qui dépasse encore 30. Un tour\nsupplémentaire a lieu et ramène `reste` à 15."
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : tu as compté un tour de trop. Une fois `reste` à 15,\nla condition est fausse et la boucle s'arrête sans exécuter\nson bloc."
            },
            {
              "text": "`30`",
              "correct": false,
              "feedback": "Erreur : tu as pris le seuil de la condition pour le nombre\nde tours. Comme la valeur est divisée par deux à chaque fois,\nelle descend bien plus vite."
            }
          ],
          "explanation": "Une boucle non bornée répète son bloc tant que sa condition est\nvraie, sans qu'on sache d'avance combien de tours auront lieu.\nIci, la division entière par deux mène de 1000 à 15 en 6 tours."
        }
      ]
    },
    {
      "id": "q21",
      "difficulty": 2,
      "skills": [
        "fonction",
        "arguments"
      ],
      "title": "Un argument manquant",
      "partie": "fonctions",
      "statement": "Un programme calcule le coût d'un trajet.\n```python\ndef cout(distance, prix_km):\n    return distance * prix_km\n\nprint(cout(12))\n```\nQue se passe-t-il à l'exécution ?",
      "options": [
        {
          "text": "Python signale une erreur, car il manque un argument\nà l'appel",
          "correct": true,
          "feedback": "Bonne réponse : la fonction attend deux arguments et n'en\nreçoit qu'un. Python refuse l'appel en signalant l'argument\nabsent, ici `prix_km`."
        },
        {
          "text": "Le programme affiche `12`, la valeur du seul\nargument fourni",
          "correct": false,
          "feedback": "Erreur : la fonction ne peut pas calculer son produit sans\nconnaître le prix. Aucun calcul n'a lieu, l'appel est refusé\navant."
        },
        {
          "text": "Le programme affiche `0`, le paramètre absent\nvalant zéro",
          "correct": false,
          "feedback": "Erreur : un paramètre sans argument ne prend aucune valeur. Il faut\nfournir l'argument au moment de l'appel, sinon Python refuse\nd'exécuter la fonction.\n"
        },
        {
          "text": "Le programme demande la valeur manquante à\nl'utilisateur",
          "correct": false,
          "feedback": "Erreur : une fonction ne dialogue jamais d'elle-même avec\nl'utilisateur. Elle reçoit ses arguments au moment de l'appel,\net seulement là."
        }
      ],
      "explanation": "Un appel doit fournir autant d'arguments que la définition compte\nde paramètres, dans le même ordre. Ici, `cout(12, 3)` renverrait\n36, tandis que `cout(12)` est refusé."
    },
    {
      "id": "q22",
      "difficulty": 2,
      "skills": [
        "for",
        "accumulateur",
        "trace"
      ],
      "title": "Le cumul de relevés croissants",
      "partie": "boucles",
      "statement": "Dans ce modèle, un capteur relève une température toutes les heures,\net elle monte de 2 degrés entre deux relevés.\n```python\ntemperature = 18\ntotal = 0\nfor heure in range(4):\n    total = total + temperature\n    temperature = temperature + 2\nprint(total, temperature)\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`84 24`",
          "correct": false,
          "feedback": "Erreur : 24 est la dernière température ajoutée au total. Mais la\nligne suivante l'augmente encore de 2 avant la fin du dernier\ntour.\n"
        },
        {
          "text": "`92 26`",
          "correct": false,
          "feedback": "Erreur : ce total correspond à 20, 22, 24 et 26. Or l'ajout au\ntotal a lieu avant l'augmentation, donc la première température\ncomptée vaut 18.\n"
        },
        {
          "text": "`84 26`",
          "correct": true,
          "feedback": "Bonne réponse : la boucle ajoute 18, 20, 22 puis 24, ce qui fait\n84. Après le dernier ajout, `temperature` augmente encore une fois\net vaut 26.\n"
        },
        {
          "text": "`60 24`",
          "correct": false,
          "feedback": "Erreur : `range(4)` fait bien 4 tours, pour `heure` valant de 0\nà 3. Ce résultat correspondrait à 3 tours seulement.\n"
        }
      ],
      "explanation": "Le schéma de l'accumulateur revient sans cesse : une variable mise à\n0 avant la boucle, augmentée à chaque tour. L'ordre des instructions\ncompte : ajouter puis augmenter ne donne pas le même total\nqu'augmenter puis ajouter.",
      "variants": [
        {
          "statement": "Dans ce modèle, un capteur relève une température toutes les heures,\net elle monte de 2 degrés entre deux relevés.\n```python\ntemperature = 18\ntotal = 0\nfor heure in range(4):\n    total = total + temperature\n    temperature = temperature + 2\nprint(total, temperature)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`84 24`",
              "correct": false,
              "feedback": "Erreur : 24 est la dernière température ajoutée au total. Mais la\nligne suivante l'augmente encore de 2 avant la fin du dernier\ntour.\n"
            },
            {
              "text": "`92 26`",
              "correct": false,
              "feedback": "Erreur : ce total correspond à 20, 22, 24 et 26. Or l'ajout au\ntotal a lieu avant l'augmentation, donc la première température\ncomptée vaut 18.\n"
            },
            {
              "text": "`84 26`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle ajoute 18, 20, 22 puis 24, ce qui fait\n84. Après le dernier ajout, `temperature` augmente encore une fois\net vaut 26.\n"
            },
            {
              "text": "`60 24`",
              "correct": false,
              "feedback": "Erreur : `range(4)` fait bien 4 tours, pour `heure` valant de 0\nà 3. Ce résultat correspondrait à 3 tours seulement.\n"
            }
          ],
          "explanation": "Le schéma de l'accumulateur revient sans cesse : une variable mise à\n0 avant la boucle, augmentée à chaque tour. L'ordre des instructions\ncompte : ajouter puis augmenter ne donne pas le même total\nqu'augmenter puis ajouter."
        },
        {
          "statement": "Dans ce modèle, un capteur relève une température toutes les heures,\net elle monte de 3 degrés entre deux relevés.\n```python\ntemperature = 20\ntotal = 0\nfor heure in range(3):\n    total = total + temperature\n    temperature = temperature + 3\nprint(total, temperature)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`69 26`",
              "correct": false,
              "feedback": "Erreur : 26 est la dernière température ajoutée au total. Mais la\nligne suivante l'augmente encore de 3 avant la fin du dernier\ntour.\n"
            },
            {
              "text": "`78 29`",
              "correct": false,
              "feedback": "Erreur : ce total correspond à 23, 26 et 29. Or l'ajout au\ntotal a lieu avant l'augmentation, donc la première température\ncomptée vaut 20.\n"
            },
            {
              "text": "`69 29`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle ajoute 20, 23 puis 26, ce qui fait\n69. Après le dernier ajout, `temperature` augmente encore une fois\net vaut 29.\n"
            },
            {
              "text": "`43 26`",
              "correct": false,
              "feedback": "Erreur : `range(3)` fait bien 3 tours, pour `heure` valant de 0\nà 2. Ce résultat correspondrait à 2 tours seulement.\n"
            }
          ],
          "explanation": "Le schéma de l'accumulateur revient sans cesse : une variable mise à\n0 avant la boucle, augmentée à chaque tour. L'ordre des instructions\ncompte : ajouter puis augmenter ne donne pas le même total\nqu'augmenter puis ajouter."
        },
        {
          "statement": "Dans ce modèle, un capteur relève une température toutes les heures,\net elle monte de 2 degrés entre deux relevés.\n```python\ntemperature = 15\ntotal = 0\nfor heure in range(5):\n    total = total + temperature\n    temperature = temperature + 2\nprint(total, temperature)\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`95 23`",
              "correct": false,
              "feedback": "Erreur : 23 est la dernière température ajoutée au total. Mais la\nligne suivante l'augmente encore de 2 avant la fin du dernier\ntour.\n"
            },
            {
              "text": "`105 25`",
              "correct": false,
              "feedback": "Erreur : ce total correspond à 17, 19, 21, 23 et 25. Or l'ajout au\ntotal a lieu avant l'augmentation, donc la première température\ncomptée vaut 15.\n"
            },
            {
              "text": "`95 25`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle ajoute 15, 17, 19, 21 puis 23, ce qui fait\n95. Après le dernier ajout, `temperature` augmente encore une fois\net vaut 25.\n"
            },
            {
              "text": "`72 23`",
              "correct": false,
              "feedback": "Erreur : `range(5)` fait bien 5 tours, pour `heure` valant de 0\nà 4. Ce résultat correspondrait à 4 tours seulement.\n"
            }
          ],
          "explanation": "Le schéma de l'accumulateur revient sans cesse : une variable mise à\n0 avant la boucle, augmentée à chaque tour. L'ordre des instructions\ncompte : ajouter puis augmenter ne donne pas le même total\nqu'augmenter puis ajouter."
        }
      ]
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "boucle-bornee",
        "condition",
        "maximum"
      ],
      "title": "Un maximum jamais trouvé",
      "partie": "boucles",
      "statement": "Ce programme modélise une nuit d'hiver où la température baisse de\n2 degrés entre deux relevés, puis cherche la plus haute.\n\n```python\nmaxi = 0\nfor releve in range(4):\n    t = -1 - 2 * releve\n    if t > maxi:\n        maxi = t\nprint(maxi)\n```\n\nQu'affiche-t-il ?",
      "options": [
        {
          "text": "`-1`",
          "correct": false,
          "feedback": "Erreur : c'est la réponse attendue, mais le programme ne la trouve\npas. Les températures valent -1, -3, -5 et -7, et aucune ne dépasse\nla valeur de départ de `maxi`.\n"
        },
        {
          "text": "`0`",
          "correct": true,
          "feedback": "Bonne réponse : les quatre températures sont négatives, donc aucune\nn'est supérieure à 0. La variable `maxi` garde la valeur de son\ninitialisation.\n"
        },
        {
          "text": "`-7`",
          "correct": false,
          "feedback": "Erreur : c'est la dernière température calculée, et la plus basse.\nLa variable `maxi` ne change que si le test `t > maxi` est vrai, ce\nqui n'arrive jamais.\n"
        },
        {
          "text": "Une erreur, car `maxi` n'est jamais modifiée",
          "correct": false,
          "feedback": "Erreur : une variable qui garde sa valeur ne provoque aucune\nerreur. Le programme s'exécute normalement et affiche la valeur\ninitiale de `maxi`.\n"
        }
      ],
      "explanation": "Initialiser un maximum à 0 revient à supposer qu'au moins une valeur\nest positive. Mieux vaut partir de la première valeur réellement\ncalculée, puis la comparer aux suivantes."
    },
    {
      "id": "q24",
      "difficulty": 2,
      "skills": [
        "completer-code",
        "condition",
        "boucle-bornee"
      ],
      "title": "Compter les mesures fortes",
      "partie": "boucles",
      "statement": "Ce programme doit afficher le nombre de mesures strictement supérieures\nà 25. L'utilisateur saisit successivement 12, 31, 8, 45 et 27.\n\n```python\ncompte = 0\nfor i in range(5):\n    m = int(input())\n    ...\nprint(compte)\n```\n\nPar quoi remplacer les points de suspension ?",
      "options": [
        {
          "text": "```python\nif m > 25:\n    compte = m\n```",
          "correct": false,
          "feedback": "Erreur : range la mesure elle-même dans le compteur au lieu de\nl'augmenter. Le programme afficherait 27, la dernière mesure\nretenue."
        },
        {
          "text": "```python\nif compte > 25:\n    compte = compte + 1\n```",
          "correct": false,
          "feedback": "Erreur : teste le compteur au lieu de la mesure. Comme il part de 0\net n'atteint jamais 25, le programme afficherait 0."
        },
        {
          "text": "```python\nif m > 25:\n    compte = compte + m\n```",
          "correct": false,
          "feedback": "Erreur : ajoute la valeur de la mesure au lieu d'ajouter 1. Le\nprogramme afficherait 103, la somme des mesures retenues."
        },
        {
          "text": "```python\nif m > 25:\n    compte = compte + 1\n```",
          "correct": true,
          "feedback": "Bonne réponse : à chaque mesure supérieure à 25, le compteur avance\nde 1. Le programme affiche 3, pour 31, 45 et 27."
        }
      ],
      "explanation": "Un compteur avance toujours de 1, quelle que soit la valeur examinée.\nC'est ce qui le distingue d'un accumulateur, qui ajoute la valeur\nelle-même."
    },
    {
      "id": "q25",
      "difficulty": 2,
      "skills": [
        "fonction",
        "parametres",
        "return"
      ],
      "title": "Le volume d'un groupe de discussion",
      "partie": "fonctions",
      "statement": "Dans un groupe de discussion, chaque membre envoie le même nombre de\nmessages, et chaque message pèse 2 kilo-octets.\n```python\ndef volume(membres, messages):\n    return membres * messages * 2\nprint(volume(20, 15))\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "600",
          "correct": true,
          "feedback": "Bonne réponse : 20 membres envoient chacun 15 messages, soit 300\nmessages, et chacun pèse 2 kilo-octets."
        },
        {
          "text": "300",
          "correct": false,
          "feedback": "Erreur : oublie les 2 kilo-octets par message. C'est le nombre de\nmessages, et non le volume échangé."
        },
        {
          "text": "70",
          "correct": false,
          "feedback": "Erreur : additionne les deux paramètres au lieu de les multiplier,\navant de multiplier par 2."
        },
        {
          "text": "40",
          "correct": false,
          "feedback": "Erreur : ne tient compte que du nombre de membres. Les deux\nparamètres interviennent dans le calcul."
        }
      ],
      "explanation": "Un appel de fonction remplace les paramètres par les arguments donnés,\nexécute le corps, puis rend la valeur écrite après `return`. C'est\ncette valeur que `print` affiche.",
      "variants": [
        {
          "statement": "Dans un groupe de discussion, chaque membre envoie le même nombre de\nmessages, et chaque message pèse 2 kilo-octets.\n```python\ndef volume(membres, messages):\n    return membres * messages * 2\nprint(volume(20, 15))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "600",
              "correct": true,
              "feedback": "Bonne réponse : 20 membres envoient chacun 15 messages, soit 300\nmessages, et chacun pèse 2 kilo-octets."
            },
            {
              "text": "300",
              "correct": false,
              "feedback": "Erreur : oublie les 2 kilo-octets par message. C'est le nombre de\nmessages, et non le volume échangé."
            },
            {
              "text": "70",
              "correct": false,
              "feedback": "Erreur : additionne les deux paramètres au lieu de les multiplier,\navant de multiplier par 2."
            },
            {
              "text": "40",
              "correct": false,
              "feedback": "Erreur : ne tient compte que du nombre de membres. Les deux\nparamètres interviennent dans le calcul."
            }
          ],
          "explanation": "Un appel de fonction remplace les paramètres par les arguments donnés,\nexécute le corps, puis rend la valeur écrite après `return`. C'est\ncette valeur que `print` affiche."
        },
        {
          "statement": "Dans un groupe de discussion, chaque membre envoie le même nombre de\nmessages, et chaque message pèse 3 kilo-octets.\n```python\ndef volume(membres, messages):\n    return membres * messages * 3\nprint(volume(10, 12))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "360",
              "correct": true,
              "feedback": "Bonne réponse : 10 membres envoient chacun 12 messages, soit 120\nmessages, et chacun pèse 3 kilo-octets."
            },
            {
              "text": "120",
              "correct": false,
              "feedback": "Erreur : oublie les 3 kilo-octets par message. C'est le nombre de\nmessages, et non le volume échangé."
            },
            {
              "text": "66",
              "correct": false,
              "feedback": "Erreur : additionne les deux paramètres au lieu de les multiplier,\navant de multiplier par 3."
            },
            {
              "text": "30",
              "correct": false,
              "feedback": "Erreur : ne tient compte que du nombre de membres. Les deux\nparamètres interviennent dans le calcul."
            }
          ],
          "explanation": "Un appel de fonction remplace les paramètres par les arguments donnés,\nexécute le corps, puis rend la valeur écrite après `return`. C'est\ncette valeur que `print` affiche."
        },
        {
          "statement": "Dans un groupe de discussion, chaque membre envoie le même nombre de\nmessages, et chaque message pèse 2 kilo-octets.\n```python\ndef volume(membres, messages):\n    return membres * messages * 2\nprint(volume(25, 8))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "400",
              "correct": true,
              "feedback": "Bonne réponse : 25 membres envoient chacun 8 messages, soit 200\nmessages, et chacun pèse 2 kilo-octets."
            },
            {
              "text": "200",
              "correct": false,
              "feedback": "Erreur : oublie les 2 kilo-octets par message. C'est le nombre de\nmessages, et non le volume échangé."
            },
            {
              "text": "66",
              "correct": false,
              "feedback": "Erreur : additionne les deux paramètres au lieu de les multiplier,\navant de multiplier par 2."
            },
            {
              "text": "50",
              "correct": false,
              "feedback": "Erreur : ne tient compte que du nombre de membres. Les deux\nparamètres interviennent dans le calcul."
            }
          ],
          "explanation": "Un appel de fonction remplace les paramètres par les arguments donnés,\nexécute le corps, puis rend la valeur écrite après `return`. C'est\ncette valeur que `print` affiche."
        }
      ]
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "fonction",
        "parametres",
        "ordre"
      ],
      "title": "L'ordre des arguments",
      "partie": "fonctions",
      "statement": "Cette fonction calcule l'écart entre deux bornes kilométriques.\n```python\ndef ecart(depart, arrivee):\n    return arrivee - depart\nprint(ecart(12, 5), ecart(5, 12))\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`7 -7`",
          "correct": false,
          "feedback": "Erreur : intervertit les deux résultats. Au premier appel, `depart`\nvaut 12 et `arrivee` vaut 5, donc la soustraction donne un nombre\nnégatif."
        },
        {
          "text": "`7 7`",
          "correct": false,
          "feedback": "Erreur : suppose que la fonction rend toujours un écart positif.\nElle applique la soustraction telle qu'elle est écrite, sans\ncorriger le signe."
        },
        {
          "text": "`-7 7`",
          "correct": true,
          "feedback": "Bonne réponse : les arguments prennent la place des paramètres dans\nl'ordre, d'où 5 moins 12 au premier appel et 12 moins 5 au second."
        },
        {
          "text": "`-7 -7`",
          "correct": false,
          "feedback": "Erreur : donne le même signe aux deux appels. Échanger les\narguments change bien le signe du résultat."
        }
      ],
      "explanation": "Les arguments d'un appel prennent la place des paramètres dans l'ordre\noù ils sont écrits. Deux appels aux mêmes valeurs rangées autrement ne\ndonnent donc pas le même résultat.",
      "variants": [
        {
          "statement": "Cette fonction calcule l'écart entre deux bornes kilométriques.\n```python\ndef ecart(depart, arrivee):\n    return arrivee - depart\nprint(ecart(12, 5), ecart(5, 12))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`7 -7`",
              "correct": false,
              "feedback": "Erreur : intervertit les deux résultats. Au premier appel, `depart`\nvaut 12 et `arrivee` vaut 5, donc la soustraction donne un nombre\nnégatif."
            },
            {
              "text": "`7 7`",
              "correct": false,
              "feedback": "Erreur : suppose que la fonction rend toujours un écart positif.\nElle applique la soustraction telle qu'elle est écrite, sans\ncorriger le signe."
            },
            {
              "text": "`-7 7`",
              "correct": true,
              "feedback": "Bonne réponse : les arguments prennent la place des paramètres dans\nl'ordre, d'où 5 moins 12 au premier appel et 12 moins 5 au second."
            },
            {
              "text": "`-7 -7`",
              "correct": false,
              "feedback": "Erreur : donne le même signe aux deux appels. Échanger les\narguments change bien le signe du résultat."
            }
          ],
          "explanation": "Les arguments d'un appel prennent la place des paramètres dans l'ordre\noù ils sont écrits. Deux appels aux mêmes valeurs rangées autrement ne\ndonnent donc pas le même résultat."
        },
        {
          "statement": "Cette fonction calcule l'écart entre deux bornes kilométriques.\n```python\ndef ecart(depart, arrivee):\n    return arrivee - depart\nprint(ecart(20, 8), ecart(8, 20))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`12 -12`",
              "correct": false,
              "feedback": "Erreur : intervertit les deux résultats. Au premier appel, `depart`\nvaut 20 et `arrivee` vaut 8, donc la soustraction donne un nombre\nnégatif."
            },
            {
              "text": "`12 12`",
              "correct": false,
              "feedback": "Erreur : suppose que la fonction rend toujours un écart positif.\nElle applique la soustraction telle qu'elle est écrite, sans\ncorriger le signe."
            },
            {
              "text": "`-12 12`",
              "correct": true,
              "feedback": "Bonne réponse : les arguments prennent la place des paramètres dans\nl'ordre, d'où 8 moins 20 au premier appel et 20 moins 8 au second."
            },
            {
              "text": "`-12 -12`",
              "correct": false,
              "feedback": "Erreur : donne le même signe aux deux appels. Échanger les\narguments change bien le signe du résultat."
            }
          ],
          "explanation": "Les arguments d'un appel prennent la place des paramètres dans l'ordre\noù ils sont écrits. Deux appels aux mêmes valeurs rangées autrement ne\ndonnent donc pas le même résultat."
        },
        {
          "statement": "Cette fonction calcule l'écart entre deux bornes kilométriques.\n```python\ndef ecart(depart, arrivee):\n    return arrivee - depart\nprint(ecart(30, 21), ecart(21, 30))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`9 -9`",
              "correct": false,
              "feedback": "Erreur : intervertit les deux résultats. Au premier appel, `depart`\nvaut 30 et `arrivee` vaut 21, donc la soustraction donne un nombre\nnégatif."
            },
            {
              "text": "`9 9`",
              "correct": false,
              "feedback": "Erreur : suppose que la fonction rend toujours un écart positif.\nElle applique la soustraction telle qu'elle est écrite, sans\ncorriger le signe."
            },
            {
              "text": "`-9 9`",
              "correct": true,
              "feedback": "Bonne réponse : les arguments prennent la place des paramètres dans\nl'ordre, d'où 21 moins 30 au premier appel et 30 moins 21 au second."
            },
            {
              "text": "`-9 -9`",
              "correct": false,
              "feedback": "Erreur : donne le même signe aux deux appels. Échanger les\narguments change bien le signe du résultat."
            }
          ],
          "explanation": "Les arguments d'un appel prennent la place des paramètres dans l'ordre\noù ils sont écrits. Deux appels aux mêmes valeurs rangées autrement ne\ndonnent donc pas le même résultat."
        }
      ]
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "fonction",
        "retour",
        "print"
      ],
      "title": "Afficher ou renvoyer",
      "partie": "fonctions",
      "statement": "Ce programme emploie une fonction qui double une valeur.\n\n```python\ndef doubler(valeur):\n    print(valeur * 2)\n\nresultat = doubler(5)\nprint(resultat)\n```\n\nQu'affiche-t-il ?",
      "options": [
        {
          "text": "`10` puis `10`",
          "correct": false,
          "feedback": "Erreur : suppose que la fonction rend ce qu'elle affiche. Sans\n`return`, l'appel ne rapporte aucune valeur utilisable."
        },
        {
          "text": "`10` puis `None`",
          "correct": true,
          "feedback": "Bonne réponse : la fonction affiche 10, puis `resultat` reçoit\n`None`, faute de `return`. La dernière ligne montre cette absence\nde valeur."
        },
        {
          "text": "`10` seulement",
          "correct": false,
          "feedback": "Erreur : oublie la dernière ligne, qui affiche `resultat`. Elle\ns'exécute et montre ce que l'appel a rapporté."
        },
        {
          "text": "`None` puis `10`",
          "correct": false,
          "feedback": "Erreur : inverse l'ordre. L'affichage écrit dans le corps de la\nfonction a lieu pendant l'appel, donc avant la dernière ligne."
        }
      ],
      "explanation": "Afficher et renvoyer sont deux actions distinctes. `print` montre une\nvaleur à l'écran, `return` la rend au programme qui a fait l'appel. Une\nfonction sans `return` rend `None`."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "fonction",
        "parametres",
        "portee"
      ],
      "title": "Un résultat qui se perd",
      "partie": "fonctions",
      "statement": "Ce programme veut doubler la largeur d'une image.\n```python\ndef agrandir(valeur):\n    valeur = valeur * 2\n    return valeur\nlargeur = 640\nagrandir(largeur)\nprint(largeur)\n```\nQu'affiche-t-il ?",
      "options": [
        {
          "text": "1280",
          "correct": false,
          "feedback": "Erreur : c'est la valeur calculée par la fonction, mais personne ne\nla recueille. Il faudrait écrire `largeur = agrandir(largeur)`."
        },
        {
          "text": "None",
          "correct": false,
          "feedback": "Erreur : la fonction contient bien un `return`, et de toute façon la\ndernière ligne affiche `largeur`, pas le résultat de l'appel."
        },
        {
          "text": "0",
          "correct": false,
          "feedback": "Erreur : la variable n'est jamais remise à zéro. Elle garde la\nvaleur reçue au moment de sa création."
        },
        {
          "text": "640",
          "correct": true,
          "feedback": "Bonne réponse : le paramètre `valeur` est une variable propre à la\nfonction. La modifier ne change rien à `largeur`, et le résultat\nrendu n'est rangé nulle part."
        }
      ],
      "explanation": "Une fonction travaille sur ses propres variables. Pour conserver ce\nqu'elle calcule, il faut ranger la valeur rendue dans une variable au\nmoment de l'appel.",
      "variants": [
        {
          "statement": "Ce programme veut doubler la largeur d'une image.\n```python\ndef agrandir(valeur):\n    valeur = valeur * 2\n    return valeur\nlargeur = 640\nagrandir(largeur)\nprint(largeur)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "1280",
              "correct": false,
              "feedback": "Erreur : c'est la valeur calculée par la fonction, mais personne ne\nla recueille. Il faudrait écrire `largeur = agrandir(largeur)`."
            },
            {
              "text": "None",
              "correct": false,
              "feedback": "Erreur : la fonction contient bien un `return`, et de toute façon la\ndernière ligne affiche `largeur`, pas le résultat de l'appel."
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : la variable n'est jamais remise à zéro. Elle garde la\nvaleur reçue au moment de sa création."
            },
            {
              "text": "640",
              "correct": true,
              "feedback": "Bonne réponse : le paramètre `valeur` est une variable propre à la\nfonction. La modifier ne change rien à `largeur`, et le résultat\nrendu n'est rangé nulle part."
            }
          ],
          "explanation": "Une fonction travaille sur ses propres variables. Pour conserver ce\nqu'elle calcule, il faut ranger la valeur rendue dans une variable au\nmoment de l'appel."
        },
        {
          "statement": "Ce programme veut tripler la largeur d'une image.\n```python\ndef agrandir(valeur):\n    valeur = valeur * 3\n    return valeur\nlargeur = 800\nagrandir(largeur)\nprint(largeur)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "2400",
              "correct": false,
              "feedback": "Erreur : c'est la valeur calculée par la fonction, mais personne ne\nla recueille. Il faudrait écrire `largeur = agrandir(largeur)`."
            },
            {
              "text": "None",
              "correct": false,
              "feedback": "Erreur : la fonction contient bien un `return`, et de toute façon la\ndernière ligne affiche `largeur`, pas le résultat de l'appel."
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : la variable n'est jamais remise à zéro. Elle garde la\nvaleur reçue au moment de sa création."
            },
            {
              "text": "800",
              "correct": true,
              "feedback": "Bonne réponse : le paramètre `valeur` est une variable propre à la\nfonction. La modifier ne change rien à `largeur`, et le résultat\nrendu n'est rangé nulle part."
            }
          ],
          "explanation": "Une fonction travaille sur ses propres variables. Pour conserver ce\nqu'elle calcule, il faut ranger la valeur rendue dans une variable au\nmoment de l'appel."
        },
        {
          "statement": "Ce programme veut doubler la largeur d'une image.\n```python\ndef agrandir(valeur):\n    valeur = valeur * 2\n    return valeur\nlargeur = 300\nagrandir(largeur)\nprint(largeur)\n```\nQu'affiche-t-il ?",
          "options": [
            {
              "text": "600",
              "correct": false,
              "feedback": "Erreur : c'est la valeur calculée par la fonction, mais personne ne\nla recueille. Il faudrait écrire `largeur = agrandir(largeur)`."
            },
            {
              "text": "None",
              "correct": false,
              "feedback": "Erreur : la fonction contient bien un `return`, et de toute façon la\ndernière ligne affiche `largeur`, pas le résultat de l'appel."
            },
            {
              "text": "0",
              "correct": false,
              "feedback": "Erreur : la variable n'est jamais remise à zéro. Elle garde la\nvaleur reçue au moment de sa création."
            },
            {
              "text": "300",
              "correct": true,
              "feedback": "Bonne réponse : le paramètre `valeur` est une variable propre à la\nfonction. La modifier ne change rien à `largeur`, et le résultat\nrendu n'est rangé nulle part."
            }
          ],
          "explanation": "Une fonction travaille sur ses propres variables. Pour conserver ce\nqu'elle calcule, il faut ranger la valeur rendue dans une variable au\nmoment de l'appel."
        }
      ]
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "fonction",
        "booleens",
        "retour"
      ],
      "title": "Une fonction qui renvoie un booléen",
      "partie": "fonctions",
      "statement": "Une moyenne n'a de sens qu'à partir de trois relevés. Les variables\n`matin` et `journee` contiennent des nombres de relevés.\n\n```python\ndef assez(nombre):\n    return nombre >= 3\n\nmatin = 2\njournee = 4\nprint(assez(matin), assez(journee), assez(matin) or assez(journee))\n```\n\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`False True True`",
          "correct": true,
          "feedback": "Bonne réponse : `matin` vaut 2 et `journee` vaut 4. Le `or` est vrai\ndès qu'un seul des deux appels l'est.\n"
        },
        {
          "text": "`False True False`",
          "correct": false,
          "feedback": "Erreur : `or` est vrai dès qu'une des deux valeurs l'est, et\nl'appel sur `journee` rend justement `True`."
        },
        {
          "text": "`True False True`",
          "correct": false,
          "feedback": "Erreur : intervertit les deux valeurs. C'est `journee`, avec ses\nquatre relevés, qui atteint le seuil de trois.\n"
        },
        {
          "text": "`False False True`",
          "correct": false,
          "feedback": "Erreur : `journee` vaut 4, ce qui est bien supérieur ou égal à 3.\nLe deuxième appel rend `True`.\n"
        }
      ],
      "explanation": "Une fonction peut rendre un booléen, qui s'emploie alors comme une\ncondition ou se combine avec `and`, `or` et `not`. La comparaison\nécrite après `return` est évaluée avant d'être rendue."
    },
    {
      "id": "q30",
      "difficulty": 3,
      "skills": [
        "fonction",
        "appel",
        "erreur"
      ],
      "title": "Un appel incomplet",
      "partie": "fonctions",
      "statement": "Ce programme s'arrête sur une erreur au moment de l'appel.\n\n```python\ndef surface(largeur, hauteur):\n    return largeur * hauteur\n\nprint(surface(12))\n```\n\nQue reproche Python à ce programme ?",
      "options": [
        {
          "text": "La fonction est appelée avant d'avoir été définie dans le fichier",
          "correct": false,
          "feedback": "Erreur : la définition est bien écrite avant l'appel. Ce reproche\nvaudrait pour un appel placé plus haut dans le fichier, et Python\nsignalerait alors une `NameError`."
        },
        {
          "text": "La valeur 12 n'est pas d'un type admis pour une multiplication",
          "correct": false,
          "feedback": "Erreur : 12 est un entier, parfaitement multipliable. Le calcul n'a\nmême pas lieu, l'appel échouant avant."
        },
        {
          "text": "La fonction attend deux paramètres, or un seul argument est fourni",
          "correct": true,
          "feedback": "Bonne réponse : Python signale `TypeError: surface() missing 1\nrequired positional argument: 'hauteur'`. Il faut fournir autant\nd'arguments que de paramètres."
        },
        {
          "text": "La fonction ne renvoie rien, donc `print` n'a rien à afficher",
          "correct": false,
          "feedback": "Erreur : la fonction contient un `return` et rendrait bien une\nvaleur si l'appel aboutissait. L'erreur survient avant."
        }
      ],
      "explanation": "Un appel doit fournir une valeur pour chaque paramètre, dans l'ordre.\nLe message d'erreur nomme même le paramètre qui manque, ici `hauteur`."
    },
    {
      "id": "q31",
      "difficulty": 1,
      "skills": [
        "affectation",
        "mise-a-jour",
        "trace"
      ],
      "title": "Le niveau de la batterie",
      "partie": "variables",
      "statement": "Qu'affiche ce programme ?\n```python\nbatterie = 80\nbatterie = batterie - 15\nprint(batterie)\n```",
      "options": [
        {
          "text": "`65`",
          "correct": true,
          "feedback": "Bonne réponse : la seconde ligne calcule `80 - 15` puis range\nle résultat dans `batterie`, qui vaut désormais 65.\n"
        },
        {
          "text": "`80`",
          "correct": false,
          "feedback": "Erreur : la seconde ligne modifie bien `batterie`. L'ancienne\nvaleur est remplacée par le résultat du calcul.\n"
        },
        {
          "text": "`15`",
          "correct": false,
          "feedback": "Erreur : 15 est la quantité retirée, pas le niveau restant.\n"
        },
        {
          "text": "Une erreur, car une variable ne peut pas être réaffectée",
          "correct": false,
          "feedback": "Erreur : réaffecter une variable est l'opération la plus\ncourante en programmation. Sa valeur change, son nom reste.\n"
        }
      ],
      "explanation": "`batterie = batterie - 15` se lit de droite à gauche : on calcule\navec la valeur actuelle, puis on range le résultat dans la variable.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nbatterie = 80\nbatterie = batterie - 15\nprint(batterie)\n```",
          "options": [
            {
              "text": "`65`",
              "correct": true,
              "feedback": "Bonne réponse : la seconde ligne calcule `80 - 15` puis range\nle résultat dans `batterie`, qui vaut désormais 65.\n"
            },
            {
              "text": "`80`",
              "correct": false,
              "feedback": "Erreur : la seconde ligne modifie bien `batterie`. L'ancienne\nvaleur est remplacée par le résultat du calcul.\n"
            },
            {
              "text": "`15`",
              "correct": false,
              "feedback": "Erreur : 15 est la quantité retirée, pas le niveau restant.\n"
            },
            {
              "text": "Une erreur, car une variable ne peut pas être réaffectée",
              "correct": false,
              "feedback": "Erreur : réaffecter une variable est l'opération la plus\ncourante en programmation. Sa valeur change, son nom reste.\n"
            }
          ],
          "explanation": "`batterie = batterie - 15` se lit de droite à gauche : on calcule\navec la valeur actuelle, puis on range le résultat dans la variable."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nbatterie = 100\nbatterie = batterie - 40\nprint(batterie)\n```",
          "options": [
            {
              "text": "`60`",
              "correct": true,
              "feedback": "Bonne réponse : la seconde ligne calcule `100 - 40` puis range\nle résultat dans `batterie`, qui vaut désormais 60.\n"
            },
            {
              "text": "`100`",
              "correct": false,
              "feedback": "Erreur : la seconde ligne modifie bien `batterie`. L'ancienne\nvaleur est remplacée par le résultat du calcul.\n"
            },
            {
              "text": "`40`",
              "correct": false,
              "feedback": "Erreur : 40 est la quantité retirée, pas le niveau restant.\n"
            },
            {
              "text": "Une erreur, car une variable ne peut pas être réaffectée",
              "correct": false,
              "feedback": "Erreur : réaffecter une variable est l'opération la plus\ncourante en programmation. Sa valeur change, son nom reste.\n"
            }
          ],
          "explanation": "`batterie = batterie - 40` se lit de droite à gauche : on calcule\navec la valeur actuelle, puis on range le résultat dans la variable."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nbatterie = 55\nbatterie = batterie - 20\nprint(batterie)\n```",
          "options": [
            {
              "text": "`35`",
              "correct": true,
              "feedback": "Bonne réponse : la seconde ligne calcule `55 - 20` puis range\nle résultat dans `batterie`, qui vaut désormais 35.\n"
            },
            {
              "text": "`55`",
              "correct": false,
              "feedback": "Erreur : la seconde ligne modifie bien `batterie`. L'ancienne\nvaleur est remplacée par le résultat du calcul.\n"
            },
            {
              "text": "`20`",
              "correct": false,
              "feedback": "Erreur : 20 est la quantité retirée, pas le niveau restant.\n"
            },
            {
              "text": "Une erreur, car une variable ne peut pas être réaffectée",
              "correct": false,
              "feedback": "Erreur : réaffecter une variable est l'opération la plus\ncourante en programmation. Sa valeur change, son nom reste.\n"
            }
          ],
          "explanation": "`batterie = batterie - 20` se lit de droite à gauche : on calcule\navec la valeur actuelle, puis on range le résultat dans la variable."
        }
      ]
    },
    {
      "id": "q32",
      "difficulty": 2,
      "skills": [
        "division-entiere",
        "modulo",
        "trace"
      ],
      "title": "Une durée en minutes et secondes",
      "partie": "variables",
      "statement": "Un chronomètre a mesuré 125 secondes. Qu'affiche ce programme ?\n```python\nsecondes = 125\nminutes = secondes // 60\nreste = secondes % 60\nprint(minutes, reste)\n```",
      "options": [
        {
          "text": "`2 5`",
          "correct": true,
          "feedback": "Bonne réponse : `//` donne le quotient entier de la division par\n60, soit 2 minutes, et `%` le reste, soit 5 secondes.\n"
        },
        {
          "text": "`2 65`",
          "correct": false,
          "feedback": "Erreur : le reste n'est pas `secondes - 60`. Il faut retirer\n2 fois 60, ce que `%` fait directement : 5.\n"
        },
        {
          "text": "`2 125`",
          "correct": false,
          "feedback": "Erreur : `%` ne renvoie pas le nombre de départ mais ce qui\nreste une fois les minutes entières retirées.\n"
        },
        {
          "text": "Une erreur, car `%` n'existe pas en Python",
          "correct": false,
          "feedback": "Erreur : `%` est l'opérateur modulo, qui calcule le reste d'une\ndivision entière.\n"
        }
      ],
      "explanation": "`//` et `%` vont de pair : le premier compte les parts entières, le\nsecond ce qui reste. Ensemble ils convertissent des secondes en\nminutes et secondes.",
      "variants": [
        {
          "statement": "Un chronomètre a mesuré 125 secondes. Qu'affiche ce programme ?\n```python\nsecondes = 125\nminutes = secondes // 60\nreste = secondes % 60\nprint(minutes, reste)\n```",
          "options": [
            {
              "text": "`2 5`",
              "correct": true,
              "feedback": "Bonne réponse : `//` donne le quotient entier de la division par\n60, soit 2 minutes, et `%` le reste, soit 5 secondes.\n"
            },
            {
              "text": "`2 65`",
              "correct": false,
              "feedback": "Erreur : le reste n'est pas `secondes - 60`. Il faut retirer\n2 fois 60, ce que `%` fait directement : 5.\n"
            },
            {
              "text": "`2 125`",
              "correct": false,
              "feedback": "Erreur : `%` ne renvoie pas le nombre de départ mais ce qui\nreste une fois les minutes entières retirées.\n"
            },
            {
              "text": "Une erreur, car `%` n'existe pas en Python",
              "correct": false,
              "feedback": "Erreur : `%` est l'opérateur modulo, qui calcule le reste d'une\ndivision entière.\n"
            }
          ],
          "explanation": "`//` et `%` vont de pair : le premier compte les parts entières, le\nsecond ce qui reste. Ensemble ils convertissent des secondes en\nminutes et secondes."
        },
        {
          "statement": "Un chronomètre a mesuré 200 secondes. Qu'affiche ce programme ?\n```python\nsecondes = 200\nminutes = secondes // 60\nreste = secondes % 60\nprint(minutes, reste)\n```",
          "options": [
            {
              "text": "`3 20`",
              "correct": true,
              "feedback": "Bonne réponse : `//` donne le quotient entier de la division par\n60, soit 3 minutes, et `%` le reste, soit 20 secondes.\n"
            },
            {
              "text": "`3 140`",
              "correct": false,
              "feedback": "Erreur : le reste n'est pas `secondes - 60`. Il faut retirer\n3 fois 60, ce que `%` fait directement : 20.\n"
            },
            {
              "text": "`3 200`",
              "correct": false,
              "feedback": "Erreur : `%` ne renvoie pas le nombre de départ mais ce qui\nreste une fois les minutes entières retirées.\n"
            },
            {
              "text": "Une erreur, car `%` n'existe pas en Python",
              "correct": false,
              "feedback": "Erreur : `%` est l'opérateur modulo, qui calcule le reste d'une\ndivision entière.\n"
            }
          ],
          "explanation": "`//` et `%` vont de pair : le premier compte les parts entières, le\nsecond ce qui reste. Ensemble ils convertissent des secondes en\nminutes et secondes."
        },
        {
          "statement": "Un chronomètre a mesuré 150 secondes. Qu'affiche ce programme ?\n```python\nsecondes = 150\nminutes = secondes // 60\nreste = secondes % 60\nprint(minutes, reste)\n```",
          "options": [
            {
              "text": "`2 30`",
              "correct": true,
              "feedback": "Bonne réponse : `//` donne le quotient entier de la division par\n60, soit 2 minutes, et `%` le reste, soit 30 secondes.\n"
            },
            {
              "text": "`2 90`",
              "correct": false,
              "feedback": "Erreur : le reste n'est pas `secondes - 60`. Il faut retirer\n2 fois 60, ce que `%` fait directement : 30.\n"
            },
            {
              "text": "`2 150`",
              "correct": false,
              "feedback": "Erreur : `%` ne renvoie pas le nombre de départ mais ce qui\nreste une fois les minutes entières retirées.\n"
            },
            {
              "text": "Une erreur, car `%` n'existe pas en Python",
              "correct": false,
              "feedback": "Erreur : `%` est l'opérateur modulo, qui calcule le reste d'une\ndivision entière.\n"
            }
          ],
          "explanation": "`//` et `%` vont de pair : le premier compte les parts entières, le\nsecond ce qui reste. Ensemble ils convertissent des secondes en\nminutes et secondes."
        }
      ]
    },
    {
      "id": "q33",
      "difficulty": 2,
      "skills": [
        "affectation",
        "copie",
        "trace"
      ],
      "title": "Une valeur recopiée puis modifiée",
      "partie": "variables",
      "statement": "Qu'affiche ce programme ?\n```python\ncapteur = 10\nmemoire = capteur\ncapteur = 20\nprint(memoire)\n```",
      "options": [
        {
          "text": "`10`",
          "correct": true,
          "feedback": "Bonne réponse : `memoire = capteur` recopie la valeur 10 au\nmoment de l'affectation. Changer `capteur` ensuite ne touche pas\n`memoire`.\n"
        },
        {
          "text": "`20`",
          "correct": false,
          "feedback": "Erreur : les deux variables ne restent pas liées. `memoire` a\nreçu une copie de la valeur, pas un lien vers `capteur`.\n"
        },
        {
          "text": "`30`",
          "correct": false,
          "feedback": "Erreur : aucune addition n'a lieu dans ce programme.\n"
        },
        {
          "text": "Une erreur, car `memoire` n'a jamais reçu de nombre",
          "correct": false,
          "feedback": "Erreur : `memoire = capteur` lui donne bien un nombre, celui que\n`capteur` contenait alors.\n"
        }
      ],
      "explanation": "Une affectation recopie une valeur à un instant donné. Les deux\nvariables vivent ensuite leur vie séparément.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ncapteur = 10\nmemoire = capteur\ncapteur = 20\nprint(memoire)\n```",
          "options": [
            {
              "text": "`10`",
              "correct": true,
              "feedback": "Bonne réponse : `memoire = capteur` recopie la valeur 10 au\nmoment de l'affectation. Changer `capteur` ensuite ne touche pas\n`memoire`.\n"
            },
            {
              "text": "`20`",
              "correct": false,
              "feedback": "Erreur : les deux variables ne restent pas liées. `memoire` a\nreçu une copie de la valeur, pas un lien vers `capteur`.\n"
            },
            {
              "text": "`30`",
              "correct": false,
              "feedback": "Erreur : aucune addition n'a lieu dans ce programme.\n"
            },
            {
              "text": "Une erreur, car `memoire` n'a jamais reçu de nombre",
              "correct": false,
              "feedback": "Erreur : `memoire = capteur` lui donne bien un nombre, celui que\n`capteur` contenait alors.\n"
            }
          ],
          "explanation": "Une affectation recopie une valeur à un instant donné. Les deux\nvariables vivent ensuite leur vie séparément."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ncapteur = 5\nmemoire = capteur\ncapteur = 50\nprint(memoire)\n```",
          "options": [
            {
              "text": "`5`",
              "correct": true,
              "feedback": "Bonne réponse : `memoire = capteur` recopie la valeur 5 au\nmoment de l'affectation. Changer `capteur` ensuite ne touche pas\n`memoire`.\n"
            },
            {
              "text": "`50`",
              "correct": false,
              "feedback": "Erreur : les deux variables ne restent pas liées. `memoire` a\nreçu une copie de la valeur, pas un lien vers `capteur`.\n"
            },
            {
              "text": "`55`",
              "correct": false,
              "feedback": "Erreur : aucune addition n'a lieu dans ce programme.\n"
            },
            {
              "text": "Une erreur, car `memoire` n'a jamais reçu de nombre",
              "correct": false,
              "feedback": "Erreur : `memoire = capteur` lui donne bien un nombre, celui que\n`capteur` contenait alors.\n"
            }
          ],
          "explanation": "Une affectation recopie une valeur à un instant donné. Les deux\nvariables vivent ensuite leur vie séparément."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ncapteur = 7\nmemoire = capteur\ncapteur = 70\nprint(memoire)\n```",
          "options": [
            {
              "text": "`7`",
              "correct": true,
              "feedback": "Bonne réponse : `memoire = capteur` recopie la valeur 7 au\nmoment de l'affectation. Changer `capteur` ensuite ne touche pas\n`memoire`.\n"
            },
            {
              "text": "`70`",
              "correct": false,
              "feedback": "Erreur : les deux variables ne restent pas liées. `memoire` a\nreçu une copie de la valeur, pas un lien vers `capteur`.\n"
            },
            {
              "text": "`77`",
              "correct": false,
              "feedback": "Erreur : aucune addition n'a lieu dans ce programme.\n"
            },
            {
              "text": "Une erreur, car `memoire` n'a jamais reçu de nombre",
              "correct": false,
              "feedback": "Erreur : `memoire = capteur` lui donne bien un nombre, celui que\n`capteur` contenait alors.\n"
            }
          ],
          "explanation": "Une affectation recopie une valeur à un instant donné. Les deux\nvariables vivent ensuite leur vie séparément."
        }
      ]
    },
    {
      "id": "q34",
      "difficulty": 1,
      "skills": [
        "if-else",
        "comparaison",
        "trace"
      ],
      "title": "Le tarif selon l'âge",
      "partie": "conditions",
      "statement": "Qu'affiche ce programme ?\n```python\nage = 16\nif age < 18:\n    prix = 5\nelse:\n    prix = 8\nprint(prix)\n```",
      "options": [
        {
          "text": "`5`",
          "correct": true,
          "feedback": "Bonne réponse : avec `age` égal à 16, la condition `age < 18`\ndécide de la branche exécutée, et `prix` reçoit 5.\n"
        },
        {
          "text": "`8`",
          "correct": false,
          "feedback": "Erreur : c'est la valeur de l'autre branche. Relis la condition\n`age < 18` avec `age` égal à 16.\n"
        },
        {
          "text": "`5` puis `8`",
          "correct": false,
          "feedback": "Erreur : une seule des deux branches est exécutée, jamais les\ndeux.\n"
        },
        {
          "text": "Rien, `prix` n'étant affiché dans aucune branche",
          "correct": false,
          "feedback": "Erreur : le `print` est placé après le bloc `if`, il s'exécute\ndans tous les cas.\n"
        }
      ],
      "explanation": "`if` et `else` partagent les cas : la condition vraie conduit à la\npremière branche, fausse à la seconde. Attention à la frontière,\nici 18 n'est pas strictement inférieur à 18.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nage = 16\nif age < 18:\n    prix = 5\nelse:\n    prix = 8\nprint(prix)\n```",
          "options": [
            {
              "text": "`5`",
              "correct": true,
              "feedback": "Bonne réponse : avec `age` égal à 16, la condition `age < 18`\ndécide de la branche exécutée, et `prix` reçoit 5.\n"
            },
            {
              "text": "`8`",
              "correct": false,
              "feedback": "Erreur : c'est la valeur de l'autre branche. Relis la condition\n`age < 18` avec `age` égal à 16.\n"
            },
            {
              "text": "`5` puis `8`",
              "correct": false,
              "feedback": "Erreur : une seule des deux branches est exécutée, jamais les\ndeux.\n"
            },
            {
              "text": "Rien, `prix` n'étant affiché dans aucune branche",
              "correct": false,
              "feedback": "Erreur : le `print` est placé après le bloc `if`, il s'exécute\ndans tous les cas.\n"
            }
          ],
          "explanation": "`if` et `else` partagent les cas : la condition vraie conduit à la\npremière branche, fausse à la seconde. Attention à la frontière,\nici 18 n'est pas strictement inférieur à 18."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nage = 20\nif age < 18:\n    prix = 6\nelse:\n    prix = 9\nprint(prix)\n```",
          "options": [
            {
              "text": "`9`",
              "correct": true,
              "feedback": "Bonne réponse : avec `age` égal à 20, la condition `age < 18`\ndécide de la branche exécutée, et `prix` reçoit 9.\n"
            },
            {
              "text": "`6`",
              "correct": false,
              "feedback": "Erreur : c'est la valeur de l'autre branche. Relis la condition\n`age < 18` avec `age` égal à 20.\n"
            },
            {
              "text": "`6` puis `9`",
              "correct": false,
              "feedback": "Erreur : une seule des deux branches est exécutée, jamais les\ndeux.\n"
            },
            {
              "text": "Rien, `prix` n'étant affiché dans aucune branche",
              "correct": false,
              "feedback": "Erreur : le `print` est placé après le bloc `if`, il s'exécute\ndans tous les cas.\n"
            }
          ],
          "explanation": "`if` et `else` partagent les cas : la condition vraie conduit à la\npremière branche, fausse à la seconde. Attention à la frontière,\nici 18 n'est pas strictement inférieur à 18."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nage = 18\nif age < 18:\n    prix = 5\nelse:\n    prix = 8\nprint(prix)\n```",
          "options": [
            {
              "text": "`8`",
              "correct": true,
              "feedback": "Bonne réponse : avec `age` égal à 18, la condition `age < 18`\ndécide de la branche exécutée, et `prix` reçoit 8.\n"
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : c'est la valeur de l'autre branche. Relis la condition\n`age < 18` avec `age` égal à 18.\n"
            },
            {
              "text": "`5` puis `8`",
              "correct": false,
              "feedback": "Erreur : une seule des deux branches est exécutée, jamais les\ndeux.\n"
            },
            {
              "text": "Rien, `prix` n'étant affiché dans aucune branche",
              "correct": false,
              "feedback": "Erreur : le `print` est placé après le bloc `if`, il s'exécute\ndans tous les cas.\n"
            }
          ],
          "explanation": "`if` et `else` partagent les cas : la condition vraie conduit à la\npremière branche, fausse à la seconde. Attention à la frontière,\nici 18 n'est pas strictement inférieur à 18."
        }
      ]
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "elif",
        "ordre",
        "trace"
      ],
      "title": "Une mention selon la note",
      "partie": "conditions",
      "statement": "Qu'affiche ce programme ?\n```python\nnote = 14\nif note >= 16:\n    mention = \"TB\"\nelif note >= 12:\n    mention = \"B\"\nelif note >= 8:\n    mention = \"AB\"\nelse:\n    mention = \"I\"\nprint(mention)\n```",
      "options": [
        {
          "text": "`B`",
          "correct": true,
          "feedback": "Bonne réponse : les conditions sont testées dans l'ordre, et la\npremière vraie l'emporte. Avec 14, c'est celle qui donne\n`B`.\n"
        },
        {
          "text": "`TB`",
          "correct": false,
          "feedback": "Erreur : vérifie quelle condition est la première vraie en\npartant du haut, avec `note` égale à 14.\n"
        },
        {
          "text": "`AB`",
          "correct": false,
          "feedback": "Erreur : dès qu'une condition est vraie, les suivantes ne sont\nplus examinées, et une condition précédente n'est pas rattrapée.\n"
        },
        {
          "text": "Les quatre mentions, les conditions étant toutes testées",
          "correct": false,
          "feedback": "Erreur : une seule branche d'une chaîne `if`, `elif`, `else`\ns'exécute.\n"
        }
      ],
      "explanation": "Une chaîne `if`, `elif`, `else` s'arrête à la première condition\nvraie. L'ordre des conditions est donc essentiel.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nnote = 14\nif note >= 16:\n    mention = \"TB\"\nelif note >= 12:\n    mention = \"B\"\nelif note >= 8:\n    mention = \"AB\"\nelse:\n    mention = \"I\"\nprint(mention)\n```",
          "options": [
            {
              "text": "`B`",
              "correct": true,
              "feedback": "Bonne réponse : les conditions sont testées dans l'ordre, et la\npremière vraie l'emporte. Avec 14, c'est celle qui donne\n`B`.\n"
            },
            {
              "text": "`TB`",
              "correct": false,
              "feedback": "Erreur : vérifie quelle condition est la première vraie en\npartant du haut, avec `note` égale à 14.\n"
            },
            {
              "text": "`AB`",
              "correct": false,
              "feedback": "Erreur : dès qu'une condition est vraie, les suivantes ne sont\nplus examinées, et une condition précédente n'est pas rattrapée.\n"
            },
            {
              "text": "Les quatre mentions, les conditions étant toutes testées",
              "correct": false,
              "feedback": "Erreur : une seule branche d'une chaîne `if`, `elif`, `else`\ns'exécute.\n"
            }
          ],
          "explanation": "Une chaîne `if`, `elif`, `else` s'arrête à la première condition\nvraie. L'ordre des conditions est donc essentiel."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nnote = 17\nif note >= 16:\n    mention = \"TB\"\nelif note >= 12:\n    mention = \"B\"\nelif note >= 8:\n    mention = \"AB\"\nelse:\n    mention = \"I\"\nprint(mention)\n```",
          "options": [
            {
              "text": "`TB`",
              "correct": true,
              "feedback": "Bonne réponse : les conditions sont testées dans l'ordre, et la\npremière vraie l'emporte. Avec 17, c'est celle qui donne\n`TB`.\n"
            },
            {
              "text": "`B`",
              "correct": false,
              "feedback": "Erreur : vérifie quelle condition est la première vraie en\npartant du haut, avec `note` égale à 17.\n"
            },
            {
              "text": "`AB`",
              "correct": false,
              "feedback": "Erreur : dès qu'une condition est vraie, les suivantes ne sont\nplus examinées, et une condition précédente n'est pas rattrapée.\n"
            },
            {
              "text": "Les quatre mentions, les conditions étant toutes testées",
              "correct": false,
              "feedback": "Erreur : une seule branche d'une chaîne `if`, `elif`, `else`\ns'exécute.\n"
            }
          ],
          "explanation": "Une chaîne `if`, `elif`, `else` s'arrête à la première condition\nvraie. L'ordre des conditions est donc essentiel."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nnote = 9\nif note >= 16:\n    mention = \"TB\"\nelif note >= 12:\n    mention = \"B\"\nelif note >= 8:\n    mention = \"AB\"\nelse:\n    mention = \"I\"\nprint(mention)\n```",
          "options": [
            {
              "text": "`AB`",
              "correct": true,
              "feedback": "Bonne réponse : les conditions sont testées dans l'ordre, et la\npremière vraie l'emporte. Avec 9, c'est celle qui donne\n`AB`.\n"
            },
            {
              "text": "`B`",
              "correct": false,
              "feedback": "Erreur : vérifie quelle condition est la première vraie en\npartant du haut, avec `note` égale à 9.\n"
            },
            {
              "text": "`I`",
              "correct": false,
              "feedback": "Erreur : dès qu'une condition est vraie, les suivantes ne sont\nplus examinées, et une condition précédente n'est pas rattrapée.\n"
            },
            {
              "text": "Les quatre mentions, les conditions étant toutes testées",
              "correct": false,
              "feedback": "Erreur : une seule branche d'une chaîne `if`, `elif`, `else`\ns'exécute.\n"
            }
          ],
          "explanation": "Une chaîne `if`, `elif`, `else` s'arrête à la première condition\nvraie. L'ordre des conditions est donc essentiel."
        }
      ]
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "or",
        "condition-composee"
      ],
      "title": "Une alerte de température",
      "partie": "conditions",
      "statement": "Une serre déclenche une alerte quand la température est inférieure\nà $5$ ou supérieure à $35$, avec la condition\n`temperature < 5 or temperature > 35`. Parmi les relevés $3$,\n$20$ et $40$, lesquels déclenchent l'alerte ?",
      "options": [
        {
          "text": "$3$ et $40$",
          "correct": true,
          "feedback": "Bonne réponse : $3$ vérifie la première comparaison, $40$ la\nseconde, et `or` se contente d'une comparaison vraie. $20$ ne\nvérifie ni l'une ni l'autre.\n"
        },
        {
          "text": "$20$ seulement",
          "correct": false,
          "feedback": "Erreur : $20$ est la seule valeur qui ne déclenche rien, étant\nni trop froide ni trop chaude.\n"
        },
        {
          "text": "Aucun, une valeur ne pouvant être à la fois inférieure à $5$ et supérieure à $35$",
          "correct": false,
          "feedback": "Erreur : `or` n'exige pas les deux comparaisons à la fois, une\nseule suffit.\n"
        },
        {
          "text": "Les trois, la condition étant toujours vraie",
          "correct": false,
          "feedback": "Erreur : pour $20$, les deux comparaisons sont fausses, donc la\ncondition est fausse.\n"
        }
      ],
      "explanation": "`or` déclenche dès qu'une des conditions est vraie ; `and`\nexigerait les deux, ce qui serait ici impossible."
    },
    {
      "id": "q37",
      "difficulty": 3,
      "skills": [
        "not",
        "comparaison",
        "equivalence"
      ],
      "title": "La négation d'une comparaison",
      "partie": "conditions",
      "statement": "Un programme contient `if not (vitesse > 50):`. Quelle condition,\nécrite sans `not`, est exactement équivalente ?",
      "options": [
        {
          "text": "`vitesse <= 50`",
          "correct": true,
          "feedback": "Bonne réponse : ne pas être strictement supérieur à $50$, c'est\nêtre inférieur ou égal à $50$. La valeur $50$ elle-même rend la\ncondition vraie.\n"
        },
        {
          "text": "`vitesse < 50`",
          "correct": false,
          "feedback": "Erreur : pour `vitesse` égale à $50$, `vitesse > 50` est faux,\ndonc `not (vitesse > 50)` est vrai, alors que `vitesse < 50`\nest faux.\n"
        },
        {
          "text": "`vitesse >= 50`",
          "correct": false,
          "feedback": "Erreur : cette condition est presque l'opposé de celle\nattendue. La négation renverse le sens de la comparaison.\n"
        },
        {
          "text": "`vitesse == 50`",
          "correct": false,
          "feedback": "Erreur : la condition attendue est vraie pour toutes les\nvitesses jusqu'à $50$, pas seulement pour $50$.\n"
        }
      ],
      "explanation": "La négation de `>` est `<=`, celle de `<` est `>=`. La valeur\nfrontière change de camp, c'est là que se cachent les erreurs."
    },
    {
      "id": "q38",
      "difficulty": 2,
      "skills": [
        "if-sans-else",
        "trace"
      ],
      "title": "Un bonus accordé sous condition",
      "partie": "conditions",
      "statement": "Qu'affiche ce programme ?\n```python\nscore = 50\nif score > 40:\n    score = score + 10\nprint(score)\n```",
      "options": [
        {
          "text": "`60`",
          "correct": true,
          "feedback": "Bonne réponse : 50 est supérieur à 40, le bloc s'exécute\net ajoute 10. Le `print`, placé après le `if`, affiche la\nnouvelle valeur.\n"
        },
        {
          "text": "`50`",
          "correct": false,
          "feedback": "Erreur : la condition est vraie, le bloc modifie donc `score`\navant l'affichage.\n"
        },
        {
          "text": "`10`",
          "correct": false,
          "feedback": "Erreur : `score + 10` ajoute 10 à la valeur existante, il ne la\nremplace pas par 10.\n"
        },
        {
          "text": "Une erreur, car il manque un `else`",
          "correct": false,
          "feedback": "Erreur : `else` est facultatif. Sans lui, rien ne se passe quand\nla condition est fausse, et le programme continue.\n"
        }
      ],
      "explanation": "Un `if` sans `else` est fréquent : on agit seulement si la condition\nest vraie, et dans tous les cas la suite du programme s'exécute.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nscore = 50\nif score > 40:\n    score = score + 10\nprint(score)\n```",
          "options": [
            {
              "text": "`60`",
              "correct": true,
              "feedback": "Bonne réponse : 50 est supérieur à 40, le bloc s'exécute\net ajoute 10. Le `print`, placé après le `if`, affiche la\nnouvelle valeur.\n"
            },
            {
              "text": "`50`",
              "correct": false,
              "feedback": "Erreur : la condition est vraie, le bloc modifie donc `score`\navant l'affichage.\n"
            },
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : `score + 10` ajoute 10 à la valeur existante, il ne la\nremplace pas par 10.\n"
            },
            {
              "text": "Une erreur, car il manque un `else`",
              "correct": false,
              "feedback": "Erreur : `else` est facultatif. Sans lui, rien ne se passe quand\nla condition est fausse, et le programme continue.\n"
            }
          ],
          "explanation": "Un `if` sans `else` est fréquent : on agit seulement si la condition\nest vraie, et dans tous les cas la suite du programme s'exécute."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nscore = 70\nif score > 60:\n    score = score + 10\nprint(score)\n```",
          "options": [
            {
              "text": "`80`",
              "correct": true,
              "feedback": "Bonne réponse : 70 est supérieur à 60, le bloc s'exécute\net ajoute 10. Le `print`, placé après le `if`, affiche la\nnouvelle valeur.\n"
            },
            {
              "text": "`70`",
              "correct": false,
              "feedback": "Erreur : la condition est vraie, le bloc modifie donc `score`\navant l'affichage.\n"
            },
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : `score + 10` ajoute 10 à la valeur existante, il ne la\nremplace pas par 10.\n"
            },
            {
              "text": "Une erreur, car il manque un `else`",
              "correct": false,
              "feedback": "Erreur : `else` est facultatif. Sans lui, rien ne se passe quand\nla condition est fausse, et le programme continue.\n"
            }
          ],
          "explanation": "Un `if` sans `else` est fréquent : on agit seulement si la condition\nest vraie, et dans tous les cas la suite du programme s'exécute."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nscore = 41\nif score > 40:\n    score = score + 10\nprint(score)\n```",
          "options": [
            {
              "text": "`51`",
              "correct": true,
              "feedback": "Bonne réponse : 41 est supérieur à 40, le bloc s'exécute\net ajoute 10. Le `print`, placé après le `if`, affiche la\nnouvelle valeur.\n"
            },
            {
              "text": "`41`",
              "correct": false,
              "feedback": "Erreur : la condition est vraie, le bloc modifie donc `score`\navant l'affichage.\n"
            },
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : `score + 10` ajoute 10 à la valeur existante, il ne la\nremplace pas par 10.\n"
            },
            {
              "text": "Une erreur, car il manque un `else`",
              "correct": false,
              "feedback": "Erreur : `else` est facultatif. Sans lui, rien ne se passe quand\nla condition est fausse, et le programme continue.\n"
            }
          ],
          "explanation": "Un `if` sans `else` est fréquent : on agit seulement si la condition\nest vraie, et dans tous les cas la suite du programme s'exécute."
        }
      ]
    },
    {
      "id": "q39",
      "difficulty": 3,
      "skills": [
        "conditions-imbriquees",
        "and",
        "equivalence"
      ],
      "title": "Deux conditions imbriquées en une seule",
      "partie": "conditions",
      "statement": "Le programme d'un portail affiche l'état d'ouverture :\n```python\nif heure >= 8:\n    if heure < 18:\n        etat = \"ouvert\"\n    else:\n        etat = \"fermé\"\nelse:\n    etat = \"fermé\"\n```\nQuelle condition unique donne `\"ouvert\"` exactement dans les mêmes\ncas ?",
      "options": [
        {
          "text": "`heure >= 8 and heure < 18`",
          "correct": true,
          "feedback": "Bonne réponse : la branche `\"ouvert\"` n'est atteinte que si la\npremière condition est vraie et la seconde aussi. Deux `if`\nemboîtés équivalent à un `and`.\n"
        },
        {
          "text": "`heure >= 8 or heure < 18`",
          "correct": false,
          "feedback": "Erreur : avec `or`, toute heure conviendrait, puisque chaque\nheure vérifie au moins l'une des deux comparaisons.\n"
        },
        {
          "text": "`heure < 8 and heure >= 18`",
          "correct": false,
          "feedback": "Erreur : aucune heure n'est à la fois avant 8 et après 18. Cette\ncondition est toujours fausse.\n"
        },
        {
          "text": "`heure >= 8 or heure >= 18`",
          "correct": false,
          "feedback": "Erreur : cette condition revient à `heure >= 8` et donnerait\n`\"ouvert\"` aussi le soir, après 18 heures.\n"
        }
      ],
      "explanation": "Imbriquer deux `if` revient à exiger les deux conditions. Les\nécrire avec `and` rend le programme plus court et plus lisible."
    },
    {
      "id": "q40",
      "difficulty": 1,
      "skills": [
        "for",
        "accumulateur",
        "trace"
      ],
      "title": "Le total d'une semaine de pas",
      "partie": "boucles",
      "statement": "Qu'affiche ce programme ?\n```python\ntotal = 0\nfor pas in [4000, 6500, 5200]:\n    total = total + pas\nprint(total)\n```",
      "options": [
        {
          "text": "`15700`",
          "correct": true,
          "feedback": "Bonne réponse : à chaque tour, la valeur courante est ajoutée à\n`total`, qui finit par contenir la somme des trois relevés.\n"
        },
        {
          "text": "`5200`",
          "correct": false,
          "feedback": "Erreur : `total` ne garde pas la dernière valeur, il accumule\ntoutes les valeurs lues.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : 3 est le nombre de tours de boucle, pas la somme des\nvaleurs.\n"
        },
        {
          "text": "`0`",
          "correct": false,
          "feedback": "Erreur : `total` vaut 0 avant la boucle, mais chaque tour\nl'augmente.\n"
        }
      ],
      "explanation": "Un accumulateur part de zéro et grossit à chaque tour. C'est le\nschéma de toute somme calculée par une boucle.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ntotal = 0\nfor pas in [4000, 6500, 5200]:\n    total = total + pas\nprint(total)\n```",
          "options": [
            {
              "text": "`15700`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque tour, la valeur courante est ajoutée à\n`total`, qui finit par contenir la somme des trois relevés.\n"
            },
            {
              "text": "`5200`",
              "correct": false,
              "feedback": "Erreur : `total` ne garde pas la dernière valeur, il accumule\ntoutes les valeurs lues.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : 3 est le nombre de tours de boucle, pas la somme des\nvaleurs.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : `total` vaut 0 avant la boucle, mais chaque tour\nl'augmente.\n"
            }
          ],
          "explanation": "Un accumulateur part de zéro et grossit à chaque tour. C'est le\nschéma de toute somme calculée par une boucle."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ntotal = 0\nfor pas in [3000, 8000, 2500]:\n    total = total + pas\nprint(total)\n```",
          "options": [
            {
              "text": "`13500`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque tour, la valeur courante est ajoutée à\n`total`, qui finit par contenir la somme des trois relevés.\n"
            },
            {
              "text": "`2500`",
              "correct": false,
              "feedback": "Erreur : `total` ne garde pas la dernière valeur, il accumule\ntoutes les valeurs lues.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : 3 est le nombre de tours de boucle, pas la somme des\nvaleurs.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : `total` vaut 0 avant la boucle, mais chaque tour\nl'augmente.\n"
            }
          ],
          "explanation": "Un accumulateur part de zéro et grossit à chaque tour. C'est le\nschéma de toute somme calculée par une boucle."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ntotal = 0\nfor pas in [7000, 1500, 4500]:\n    total = total + pas\nprint(total)\n```",
          "options": [
            {
              "text": "`13000`",
              "correct": true,
              "feedback": "Bonne réponse : à chaque tour, la valeur courante est ajoutée à\n`total`, qui finit par contenir la somme des trois relevés.\n"
            },
            {
              "text": "`4500`",
              "correct": false,
              "feedback": "Erreur : `total` ne garde pas la dernière valeur, il accumule\ntoutes les valeurs lues.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : 3 est le nombre de tours de boucle, pas la somme des\nvaleurs.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : `total` vaut 0 avant la boucle, mais chaque tour\nl'augmente.\n"
            }
          ],
          "explanation": "Un accumulateur part de zéro et grossit à chaque tour. C'est le\nschéma de toute somme calculée par une boucle."
        }
      ]
    },
    {
      "id": "q41",
      "difficulty": 2,
      "skills": [
        "while",
        "doublement",
        "trace"
      ],
      "title": "Une surface qui double",
      "partie": "boucles",
      "statement": "Une bactérie double de population à chaque heure. Qu'affiche ce\nprogramme ?\n```python\npopulation = 1\nwhile population * 2 <= 100:\n    population = population * 2\nprint(population)\n```",
      "options": [
        {
          "text": "`64`",
          "correct": true,
          "feedback": "Bonne réponse : la population double tant que le double ne\ndépasse pas 100. Elle s'arrête à 64, car 128 dépasserait 100.\n"
        },
        {
          "text": "`128`",
          "correct": false,
          "feedback": "Erreur : 128 dépasse 100, la condition `population * 2 <= 100`\nest fausse avant ce doublement, et la boucle s'arrête.\n"
        },
        {
          "text": "`100`",
          "correct": false,
          "feedback": "Erreur : la population ne prend que des puissances de 2, elle\nn'atteint pas 100 exactement.\n"
        },
        {
          "text": "Rien, la boucle ne s'arrêtant jamais",
          "correct": false,
          "feedback": "Erreur : la population grandit à chaque tour, donc la condition\nfinit par devenir fausse.\n"
        }
      ],
      "explanation": "La condition d'un `while` est vérifiée avant chaque tour. Ici, elle\nregarde ce que donnerait le prochain doublement avant de l'autoriser.",
      "variants": [
        {
          "statement": "Une bactérie double de population à chaque heure. Qu'affiche ce\nprogramme ?\n```python\npopulation = 1\nwhile population * 2 <= 100:\n    population = population * 2\nprint(population)\n```",
          "options": [
            {
              "text": "`64`",
              "correct": true,
              "feedback": "Bonne réponse : la population double tant que le double ne\ndépasse pas 100. Elle s'arrête à 64, car 128 dépasserait 100.\n"
            },
            {
              "text": "`128`",
              "correct": false,
              "feedback": "Erreur : 128 dépasse 100, la condition `population * 2 <= 100`\nest fausse avant ce doublement, et la boucle s'arrête.\n"
            },
            {
              "text": "`100`",
              "correct": false,
              "feedback": "Erreur : la population ne prend que des puissances de 2, elle\nn'atteint pas 100 exactement.\n"
            },
            {
              "text": "Rien, la boucle ne s'arrêtant jamais",
              "correct": false,
              "feedback": "Erreur : la population grandit à chaque tour, donc la condition\nfinit par devenir fausse.\n"
            }
          ],
          "explanation": "La condition d'un `while` est vérifiée avant chaque tour. Ici, elle\nregarde ce que donnerait le prochain doublement avant de l'autoriser."
        },
        {
          "statement": "Une bactérie double de population à chaque heure. Qu'affiche ce\nprogramme ?\n```python\npopulation = 1\nwhile population * 2 <= 50:\n    population = population * 2\nprint(population)\n```",
          "options": [
            {
              "text": "`32`",
              "correct": true,
              "feedback": "Bonne réponse : la population double tant que le double ne\ndépasse pas 50. Elle s'arrête à 32, car 64 dépasserait 50.\n"
            },
            {
              "text": "`64`",
              "correct": false,
              "feedback": "Erreur : 64 dépasse 50, la condition `population * 2 <= 50`\nest fausse avant ce doublement, et la boucle s'arrête.\n"
            },
            {
              "text": "`50`",
              "correct": false,
              "feedback": "Erreur : la population ne prend que des puissances de 2, elle\nn'atteint pas 50 exactement.\n"
            },
            {
              "text": "Rien, la boucle ne s'arrêtant jamais",
              "correct": false,
              "feedback": "Erreur : la population grandit à chaque tour, donc la condition\nfinit par devenir fausse.\n"
            }
          ],
          "explanation": "La condition d'un `while` est vérifiée avant chaque tour. Ici, elle\nregarde ce que donnerait le prochain doublement avant de l'autoriser."
        },
        {
          "statement": "Une bactérie double de population à chaque heure. Qu'affiche ce\nprogramme ?\n```python\npopulation = 1\nwhile population * 2 <= 300:\n    population = population * 2\nprint(population)\n```",
          "options": [
            {
              "text": "`256`",
              "correct": true,
              "feedback": "Bonne réponse : la population double tant que le double ne\ndépasse pas 300. Elle s'arrête à 256, car 512 dépasserait 300.\n"
            },
            {
              "text": "`512`",
              "correct": false,
              "feedback": "Erreur : 512 dépasse 300, la condition `population * 2 <= 300`\nest fausse avant ce doublement, et la boucle s'arrête.\n"
            },
            {
              "text": "`300`",
              "correct": false,
              "feedback": "Erreur : la population ne prend que des puissances de 2, elle\nn'atteint pas 300 exactement.\n"
            },
            {
              "text": "Rien, la boucle ne s'arrêtant jamais",
              "correct": false,
              "feedback": "Erreur : la population grandit à chaque tour, donc la condition\nfinit par devenir fausse.\n"
            }
          ],
          "explanation": "La condition d'un `while` est vérifiée avant chaque tour. Ici, elle\nregarde ce que donnerait le prochain doublement avant de l'autoriser."
        }
      ]
    },
    {
      "id": "q42",
      "difficulty": 3,
      "skills": [
        "for",
        "drapeau",
        "booleen",
        "trace"
      ],
      "title": "Un drapeau levé dans une boucle",
      "partie": "boucles",
      "statement": "Des relevés de vent sont stockés dans une liste. Qu'affiche ce\nprogramme ?\n```python\nreleves = [3, 8, 12, 5]\ntempete = False\nfor v in releves:\n    if v > 10:\n        tempete = True\nprint(tempete)\n```",
      "options": [
        {
          "text": "`True`",
          "correct": true,
          "feedback": "Bonne réponse : `tempete` part de `False` et passe à `True` dès\nqu'un relevé dépasse 10. La liste contient une telle valeur.\n"
        },
        {
          "text": "`False`",
          "correct": false,
          "feedback": "Erreur : parcours la liste [3, 8, 12, 5] et regarde si un relevé dépasse\nstrictement 10. Le plus grand est 12.\n"
        },
        {
          "text": "`12`",
          "correct": false,
          "feedback": "Erreur : le programme affiche `tempete`, un booléen, pas un\nrelevé.\n"
        },
        {
          "text": "`True` puis `False`",
          "correct": false,
          "feedback": "Erreur : il n'y a qu'un seul `print`, après la boucle, donc une\nseule valeur affichée.\n"
        }
      ],
      "explanation": "Un drapeau est une variable booléenne que la boucle lève dès qu'une\ncondition est rencontrée. Après la boucle, il dit si cela s'est\nproduit au moins une fois.",
      "variants": [
        {
          "statement": "Des relevés de vent sont stockés dans une liste. Qu'affiche ce\nprogramme ?\n```python\nreleves = [3, 8, 12, 5]\ntempete = False\nfor v in releves:\n    if v > 10:\n        tempete = True\nprint(tempete)\n```",
          "options": [
            {
              "text": "`True`",
              "correct": true,
              "feedback": "Bonne réponse : `tempete` part de `False` et passe à `True` dès\nqu'un relevé dépasse 10. La liste contient une telle valeur.\n"
            },
            {
              "text": "`False`",
              "correct": false,
              "feedback": "Erreur : parcours la liste [3, 8, 12, 5] et regarde si un relevé dépasse\nstrictement 10. Le plus grand est 12.\n"
            },
            {
              "text": "`12`",
              "correct": false,
              "feedback": "Erreur : le programme affiche `tempete`, un booléen, pas un\nrelevé.\n"
            },
            {
              "text": "`True` puis `False`",
              "correct": false,
              "feedback": "Erreur : il n'y a qu'un seul `print`, après la boucle, donc une\nseule valeur affichée.\n"
            }
          ],
          "explanation": "Un drapeau est une variable booléenne que la boucle lève dès qu'une\ncondition est rencontrée. Après la boucle, il dit si cela s'est\nproduit au moins une fois."
        },
        {
          "statement": "Des relevés de vent sont stockés dans une liste. Qu'affiche ce\nprogramme ?\n```python\nreleves = [4, 9, 7, 2]\ntempete = False\nfor v in releves:\n    if v > 10:\n        tempete = True\nprint(tempete)\n```",
          "options": [
            {
              "text": "`False`",
              "correct": true,
              "feedback": "Bonne réponse : `tempete` part de `False` et passe à `True` dès\nqu'un relevé dépasse 10. La liste ne contient pas une telle valeur.\n"
            },
            {
              "text": "`True`",
              "correct": false,
              "feedback": "Erreur : parcours la liste [4, 9, 7, 2] et regarde si un relevé dépasse\nstrictement 10. Le plus grand est 9.\n"
            },
            {
              "text": "`9`",
              "correct": false,
              "feedback": "Erreur : le programme affiche `tempete`, un booléen, pas un\nrelevé.\n"
            },
            {
              "text": "`True` puis `False`",
              "correct": false,
              "feedback": "Erreur : il n'y a qu'un seul `print`, après la boucle, donc une\nseule valeur affichée.\n"
            }
          ],
          "explanation": "Un drapeau est une variable booléenne que la boucle lève dès qu'une\ncondition est rencontrée. Après la boucle, il dit si cela s'est\nproduit au moins une fois."
        }
      ]
    },
    {
      "id": "q43",
      "difficulty": 1,
      "skills": [
        "fonction",
        "return",
        "appel"
      ],
      "title": "Une conversion en kilomètres",
      "partie": "fonctions",
      "statement": "Qu'affiche ce programme ?\n```python\ndef en_km(metres):\n    return metres / 1000\n\nprint(en_km(2500))\n```",
      "options": [
        {
          "text": "`2.5`",
          "correct": true,
          "feedback": "Bonne réponse : l'appel `en_km(2500)` remplace `metres` par 2500,\net la division par 1000 donne 2.5, un flottant.\n"
        },
        {
          "text": "`2500000`",
          "correct": false,
          "feedback": "Erreur : la fonction divise par 1000, elle ne multiplie pas.\n"
        },
        {
          "text": "`2500`",
          "correct": false,
          "feedback": "Erreur : la valeur renvoyée est le résultat du calcul, pas\nl'argument reçu.\n"
        },
        {
          "text": "Rien, la fonction ne contenant pas de `print`",
          "correct": false,
          "feedback": "Erreur : le `print` est à l'extérieur, autour de l'appel. Il\naffiche la valeur renvoyée par `return`.\n"
        }
      ],
      "explanation": "`return` renvoie un résultat à l'endroit de l'appel ; `print`\nl'affiche. Les deux rôles sont distincts et se combinent.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef en_km(metres):\n    return metres / 1000\n\nprint(en_km(2500))\n```",
          "options": [
            {
              "text": "`2.5`",
              "correct": true,
              "feedback": "Bonne réponse : l'appel `en_km(2500)` remplace `metres` par 2500,\net la division par 1000 donne 2.5, un flottant.\n"
            },
            {
              "text": "`2500000`",
              "correct": false,
              "feedback": "Erreur : la fonction divise par 1000, elle ne multiplie pas.\n"
            },
            {
              "text": "`2500`",
              "correct": false,
              "feedback": "Erreur : la valeur renvoyée est le résultat du calcul, pas\nl'argument reçu.\n"
            },
            {
              "text": "Rien, la fonction ne contenant pas de `print`",
              "correct": false,
              "feedback": "Erreur : le `print` est à l'extérieur, autour de l'appel. Il\naffiche la valeur renvoyée par `return`.\n"
            }
          ],
          "explanation": "`return` renvoie un résultat à l'endroit de l'appel ; `print`\nl'affiche. Les deux rôles sont distincts et se combinent."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef en_km(metres):\n    return metres / 1000\n\nprint(en_km(800))\n```",
          "options": [
            {
              "text": "`0.8`",
              "correct": true,
              "feedback": "Bonne réponse : l'appel `en_km(800)` remplace `metres` par 800,\net la division par 1000 donne 0.8, un flottant.\n"
            },
            {
              "text": "`800000`",
              "correct": false,
              "feedback": "Erreur : la fonction divise par 1000, elle ne multiplie pas.\n"
            },
            {
              "text": "`800`",
              "correct": false,
              "feedback": "Erreur : la valeur renvoyée est le résultat du calcul, pas\nl'argument reçu.\n"
            },
            {
              "text": "Rien, la fonction ne contenant pas de `print`",
              "correct": false,
              "feedback": "Erreur : le `print` est à l'extérieur, autour de l'appel. Il\naffiche la valeur renvoyée par `return`.\n"
            }
          ],
          "explanation": "`return` renvoie un résultat à l'endroit de l'appel ; `print`\nl'affiche. Les deux rôles sont distincts et se combinent."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef en_km(metres):\n    return metres / 1000\n\nprint(en_km(12000))\n```",
          "options": [
            {
              "text": "`12.0`",
              "correct": true,
              "feedback": "Bonne réponse : l'appel `en_km(12000)` remplace `metres` par 12000,\net la division par 1000 donne 12.0, un flottant.\n"
            },
            {
              "text": "`12000000`",
              "correct": false,
              "feedback": "Erreur : la fonction divise par 1000, elle ne multiplie pas.\n"
            },
            {
              "text": "`12000`",
              "correct": false,
              "feedback": "Erreur : la valeur renvoyée est le résultat du calcul, pas\nl'argument reçu.\n"
            },
            {
              "text": "Rien, la fonction ne contenant pas de `print`",
              "correct": false,
              "feedback": "Erreur : le `print` est à l'extérieur, autour de l'appel. Il\naffiche la valeur renvoyée par `return`.\n"
            }
          ],
          "explanation": "`return` renvoie un résultat à l'endroit de l'appel ; `print`\nl'affiche. Les deux rôles sont distincts et se combinent."
        }
      ]
    },
    {
      "id": "q44",
      "difficulty": 2,
      "skills": [
        "fonction",
        "variable-locale",
        "portee"
      ],
      "title": "Une variable née dans la fonction",
      "partie": "fonctions",
      "statement": "Qu'affiche ce programme ?\n```python\ndef calcul(x):\n    resultat = x * 3\n    return resultat\n\ncalcul(4)\nprint(resultat)\n```",
      "options": [
        {
          "text": "Une erreur, `resultat` étant inconnu à l'extérieur",
          "correct": true,
          "feedback": "Bonne réponse : `resultat` est une variable locale, créée à\nchaque appel et détruite à la fin. Hors de la fonction, ce nom\nest inconnu.\n"
        },
        {
          "text": "Il affiche `12`, le résultat du calcul",
          "correct": false,
          "feedback": "Erreur : 12 est bien calculé, mais la valeur renvoyée par\n`calcul(4)` n'est rangée nulle part, et `resultat` reste\ninterne à la fonction.\n"
        },
        {
          "text": "Il affiche `4`, l'argument de l'appel",
          "correct": false,
          "feedback": "Erreur : 4 est l'argument de l'appel, pas une variable nommée\n`resultat`.\n"
        },
        {
          "text": "Il affiche `0`, valeur par défaut d'une variable non définie",
          "correct": false,
          "feedback": "Erreur : aucune variable `resultat` n'existe à l'extérieur, pas\nmême avec la valeur 0.\n"
        }
      ],
      "explanation": "Pour utiliser le résultat d'une fonction, on récupère ce que\n`return` renvoie, par exemple `r = calcul(4)`. Les variables\ninternes, elles, restent invisibles."
    },
    {
      "id": "q45",
      "difficulty": 3,
      "skills": [
        "fonction",
        "boucle",
        "accumulateur",
        "trace"
      ],
      "title": "Une fonction appelée dans une boucle",
      "partie": "fonctions",
      "statement": "Qu'affiche ce programme ?\n```python\ndef double(x):\n    return 2 * x\n\ntotal = 0\nfor v in [1, 2, 3]:\n    total = total + double(v)\nprint(total)\n```",
      "options": [
        {
          "text": "`12`",
          "correct": true,
          "feedback": "Bonne réponse : chaque valeur est doublée par la fonction avant\nd'être ajoutée. La somme des valeurs vaut 6, celle des doubles\n12.\n"
        },
        {
          "text": "`6`",
          "correct": false,
          "feedback": "Erreur : 6 est la somme des valeurs sans le doublement. La\nfonction est appelée à chaque tour.\n"
        },
        {
          "text": "`24`",
          "correct": false,
          "feedback": "Erreur : chaque valeur est doublée une seule fois, pas deux.\n"
        },
        {
          "text": "Une erreur, une fonction ne pouvant être appelée dans une boucle",
          "correct": false,
          "feedback": "Erreur : appeler une fonction dans une boucle est tout à fait\npermis, et très courant.\n"
        }
      ],
      "explanation": "Une fonction s'appelle autant de fois que nécessaire, y compris à\nchaque tour d'une boucle. Chaque appel renvoie son résultat, aussitôt\nutilisé.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef double(x):\n    return 2 * x\n\ntotal = 0\nfor v in [1, 2, 3]:\n    total = total + double(v)\nprint(total)\n```",
          "options": [
            {
              "text": "`12`",
              "correct": true,
              "feedback": "Bonne réponse : chaque valeur est doublée par la fonction avant\nd'être ajoutée. La somme des valeurs vaut 6, celle des doubles\n12.\n"
            },
            {
              "text": "`6`",
              "correct": false,
              "feedback": "Erreur : 6 est la somme des valeurs sans le doublement. La\nfonction est appelée à chaque tour.\n"
            },
            {
              "text": "`24`",
              "correct": false,
              "feedback": "Erreur : chaque valeur est doublée une seule fois, pas deux.\n"
            },
            {
              "text": "Une erreur, une fonction ne pouvant être appelée dans une boucle",
              "correct": false,
              "feedback": "Erreur : appeler une fonction dans une boucle est tout à fait\npermis, et très courant.\n"
            }
          ],
          "explanation": "Une fonction s'appelle autant de fois que nécessaire, y compris à\nchaque tour d'une boucle. Chaque appel renvoie son résultat, aussitôt\nutilisé."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef double(x):\n    return 2 * x\n\ntotal = 0\nfor v in [2, 5, 4]:\n    total = total + double(v)\nprint(total)\n```",
          "options": [
            {
              "text": "`22`",
              "correct": true,
              "feedback": "Bonne réponse : chaque valeur est doublée par la fonction avant\nd'être ajoutée. La somme des valeurs vaut 11, celle des doubles\n22.\n"
            },
            {
              "text": "`11`",
              "correct": false,
              "feedback": "Erreur : 11 est la somme des valeurs sans le doublement. La\nfonction est appelée à chaque tour.\n"
            },
            {
              "text": "`44`",
              "correct": false,
              "feedback": "Erreur : chaque valeur est doublée une seule fois, pas deux.\n"
            },
            {
              "text": "Une erreur, une fonction ne pouvant être appelée dans une boucle",
              "correct": false,
              "feedback": "Erreur : appeler une fonction dans une boucle est tout à fait\npermis, et très courant.\n"
            }
          ],
          "explanation": "Une fonction s'appelle autant de fois que nécessaire, y compris à\nchaque tour d'une boucle. Chaque appel renvoie son résultat, aussitôt\nutilisé."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef double(x):\n    return 2 * x\n\ntotal = 0\nfor v in [3, 3, 1]:\n    total = total + double(v)\nprint(total)\n```",
          "options": [
            {
              "text": "`14`",
              "correct": true,
              "feedback": "Bonne réponse : chaque valeur est doublée par la fonction avant\nd'être ajoutée. La somme des valeurs vaut 7, celle des doubles\n14.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : 7 est la somme des valeurs sans le doublement. La\nfonction est appelée à chaque tour.\n"
            },
            {
              "text": "`28`",
              "correct": false,
              "feedback": "Erreur : chaque valeur est doublée une seule fois, pas deux.\n"
            },
            {
              "text": "Une erreur, une fonction ne pouvant être appelée dans une boucle",
              "correct": false,
              "feedback": "Erreur : appeler une fonction dans une boucle est tout à fait\npermis, et très courant.\n"
            }
          ],
          "explanation": "Une fonction s'appelle autant de fois que nécessaire, y compris à\nchaque tour d'une boucle. Chaque appel renvoie son résultat, aussitôt\nutilisé."
        }
      ]
    }
  ]
}