{
  "chapter": {
    "id": "conditions-boucles",
    "level": "premiere",
    "theme": "Programmation",
    "title": "Conditions et boucles",
    "description": "Structures de contrôle Python : conditions\n(`if`/`elif`/`else`), opérateurs booléens\n(`and`/`or`/`not`), boucles bornées (`for`/`range`),\nboucles non bornées (`while`), boucles imbriquées et\ntraces d'exécution.",
    "parties": [
      {
        "id": "conditions",
        "title": "Conditions"
      },
      {
        "id": "for",
        "title": "Boucles bornées (for)"
      },
      {
        "id": "while",
        "title": "Boucles non bornées (while)"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "if"
      ],
      "title": "Syntaxe du if",
      "partie": "conditions",
      "statement": "Quelle est la syntaxe correcte d'une condition\nsimple en Python ?",
      "options": [
        {
          "text": "```python\nif (x > 0)\n    print(\"positif\");\n```\n",
          "correct": false,
          "feedback": "Erreur : syntaxe à la C/Java. En Python, pas\nde parenthèses obligatoires, deux-points\nobligatoires, indentation à la place des\naccolades.\n"
        },
        {
          "text": "```python\nif x > 0 then\n    print(\"positif\")\nend\n```\n",
          "correct": false,
          "feedback": "Erreur : `then` et `end` n'existent pas en\nPython (Lua, Ruby).\n"
        },
        {
          "text": "```python\nif x > 0:\n    print(\"positif\")\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : deux-points en fin de ligne,\nindentation (4 espaces conventionnels) pour\nle bloc.\n"
        },
        {
          "text": "```python\nif {x > 0} print(\"positif\")\n```\n",
          "correct": false,
          "feedback": "Erreur : pas d'accolades en Python.\n"
        }
      ],
      "explanation": "Python utilise l'**indentation significative**.\nTous les blocs (`if`, `for`, `while`, `def`...)\nse reconnaissent à leur indentation, pas à des\naccolades."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "elif"
      ],
      "title": "elif",
      "partie": "conditions",
      "statement": "À quoi sert le mot-clé `elif` en Python ?",
      "options": [
        {
          "text": "C'est simplement un alias pour le mot-clé `if`",
          "correct": false,
          "feedback": "Erreur : `elif` ne s'utilise qu'**après** un\n`if`.\n"
        },
        {
          "text": "C'est un alias pour `else`",
          "correct": false,
          "feedback": "Erreur : `else` n'a pas de condition. `elif`\na une condition.\n"
        },
        {
          "text": "Cela teste si une variable existe",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec l'existence.\n"
        },
        {
          "text": "C'est l'équivalent du `else if` en C/Java.",
          "correct": true,
          "feedback": "Bonne réponse : `elif` permet d'enchaîner\nplusieurs conditions sans imbrication\nexcessive. Mot-clé Python spécifique.\n"
        }
      ],
      "explanation": "Schéma général :\n```python\nif cond1:\n    ...\nelif cond2:\n    ...\nelse:\n    ...\n```\nAu plus **un** des blocs s'exécute (le premier\ndont la condition est vraie)."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "and-or-not"
      ],
      "title": "Opérateurs logiques",
      "partie": "conditions",
      "statement": "Comment écrit-on « x est positif **et** pair »\nen Python ?",
      "options": [
        {
          "text": "`x > 0 && x % 2 == 0`",
          "correct": false,
          "feedback": "Erreur : `&&` n'existe pas en Python. C'est\nla syntaxe C/Java.\n"
        },
        {
          "text": "`x > 0 and x % 2 == 0`",
          "correct": true,
          "feedback": "Bonne réponse : Python utilise les\nmots-clés `and`, `or`, `not`.\n"
        },
        {
          "text": "`x > 0 et x % 2 == 0`",
          "correct": false,
          "feedback": "Erreur : Python est en anglais.\n"
        },
        {
          "text": "`x > 0, x % 2 == 0`",
          "correct": false,
          "feedback": "Erreur : la virgule produit un tuple, pas\nune conjonction logique.\n"
        }
      ],
      "explanation": "Trois opérateurs : `and` (et), `or` (ou\ninclusif), `not` (négation). À noter :\névaluation paresseuse, `False and ...` ne\nteste pas la suite."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "range",
        "iterations"
      ],
      "title": "Boucle for range",
      "partie": "for",
      "statement": "Combien d'itérations effectue\n`for i in range(5):` ?",
      "options": [
        {
          "text": "Une infinité",
          "correct": false,
          "feedback": "Erreur : `range(5)` est borné.\n"
        },
        {
          "text": "4",
          "correct": false,
          "feedback": "Erreur : `range(5)` produit 5 valeurs, de 0\nà 4.\n"
        },
        {
          "text": "5",
          "correct": true,
          "feedback": "Bonne réponse : `range(n)` produit les\nentiers de 0 à n-1, soit **n valeurs** au\ntotal.\n"
        },
        {
          "text": "6",
          "correct": false,
          "feedback": "Erreur : 5 est exclu de la séquence.\n"
        }
      ],
      "explanation": "Convention Python : la borne supérieure est\n**exclue**. `range(5)` = `[0, 1, 2, 3, 4]`.",
      "variants": [
        {
          "statement": "Combien d'itérations effectue\n`for i in range(5):` ?",
          "options": [
            {
              "text": "Une infinité",
              "correct": false,
              "feedback": "Erreur : `range(5)` est borné.\n"
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : `range(5)` produit 5 valeurs, de 0\nà 4.\n"
            },
            {
              "text": "5",
              "correct": true,
              "feedback": "Bonne réponse : `range(n)` produit les\nentiers de 0 à n-1, soit **n valeurs** au\ntotal.\n"
            },
            {
              "text": "6",
              "correct": false,
              "feedback": "Erreur : 5 est exclu de la séquence.\n"
            }
          ],
          "explanation": "Convention Python : la borne supérieure est\n**exclue**. `range(5)` = `[0, 1, 2, 3, 4]`."
        },
        {
          "statement": "Combien d'itérations effectue\n`for i in range(8):` ?",
          "options": [
            {
              "text": "Une infinité",
              "correct": false,
              "feedback": "Erreur : `range(8)` est borné.\n"
            },
            {
              "text": "7",
              "correct": false,
              "feedback": "Erreur : `range(8)` produit 8 valeurs, de 0\nà 7.\n"
            },
            {
              "text": "8",
              "correct": true,
              "feedback": "Bonne réponse : `range(n)` produit les\nentiers de 0 à n-1, soit **n valeurs** au\ntotal.\n"
            },
            {
              "text": "9",
              "correct": false,
              "feedback": "Erreur : 8 est exclu de la séquence.\n"
            }
          ],
          "explanation": "Convention Python : la borne supérieure est\n**exclue**. `range(8)` = `[0, 1, 2, 3, 4, 5, 6, 7]`."
        },
        {
          "statement": "Combien d'itérations effectue\n`for i in range(12):` ?",
          "options": [
            {
              "text": "Une infinité",
              "correct": false,
              "feedback": "Erreur : `range(12)` est borné.\n"
            },
            {
              "text": "11",
              "correct": false,
              "feedback": "Erreur : `range(12)` produit 12 valeurs, de 0\nà 11.\n"
            },
            {
              "text": "12",
              "correct": true,
              "feedback": "Bonne réponse : `range(n)` produit les\nentiers de 0 à n-1, soit **n valeurs** au\ntotal.\n"
            },
            {
              "text": "13",
              "correct": false,
              "feedback": "Erreur : 12 est exclu de la séquence.\n"
            }
          ],
          "explanation": "Convention Python : la borne supérieure est\n**exclue**. `range(12)` = `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]`."
        }
      ]
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "range",
        "deux-arguments"
      ],
      "title": "range avec deux arguments",
      "partie": "for",
      "statement": "Que produit `list(range(2, 7))` ?",
      "options": [
        {
          "text": "`[3, 4, 5, 6, 7]`",
          "correct": false,
          "feedback": "Erreur : 2 est **inclus**, 7 est exclu.\n"
        },
        {
          "text": "`[2, 3, 4, 5, 6, 7]`",
          "correct": false,
          "feedback": "Erreur : 7 est **exclu**.\n"
        },
        {
          "text": "`[2, 3, 4, 5, 6]`",
          "correct": true,
          "feedback": "Bonne réponse : `range(a, b)` produit les\nentiers de `a` (inclus) à `b` (exclu).\n"
        },
        {
          "text": "`[2, 7]`",
          "correct": false,
          "feedback": "Erreur : ce serait `range(2, 7, 5)` qui\ndonne `[2]` seulement.\n"
        }
      ],
      "explanation": "Trois formes : `range(n)` (0 à n-1),\n`range(a, b)` (a à b-1),\n`range(a, b, pas)` (a à b-1 par pas).",
      "variants": [
        {
          "statement": "Que produit `list(range(2, 7))` ?",
          "options": [
            {
              "text": "`[3, 4, 5, 6, 7]`",
              "correct": false,
              "feedback": "Erreur : 2 est **inclus**, 7 est exclu.\n"
            },
            {
              "text": "`[2, 3, 4, 5, 6, 7]`",
              "correct": false,
              "feedback": "Erreur : 7 est **exclu**.\n"
            },
            {
              "text": "`[2, 3, 4, 5, 6]`",
              "correct": true,
              "feedback": "Bonne réponse : `range(a, b)` produit les\nentiers de `a` (inclus) à `b` (exclu).\n"
            },
            {
              "text": "`[2, 7]`",
              "correct": false,
              "feedback": "Erreur : ce serait `range(2, 7, 5)` qui\ndonne `[2]` seulement.\n"
            }
          ],
          "explanation": "Trois formes : `range(n)` (0 à n-1),\n`range(a, b)` (a à b-1),\n`range(a, b, pas)` (a à b-1 par pas)."
        },
        {
          "statement": "Que produit `list(range(3, 9))` ?",
          "options": [
            {
              "text": "`[4, 5, 6, 7, 8, 9]`",
              "correct": false,
              "feedback": "Erreur : 3 est **inclus**, 9 est exclu.\n"
            },
            {
              "text": "`[3, 4, 5, 6, 7, 8, 9]`",
              "correct": false,
              "feedback": "Erreur : 9 est **exclu**.\n"
            },
            {
              "text": "`[3, 4, 5, 6, 7, 8]`",
              "correct": true,
              "feedback": "Bonne réponse : `range(a, b)` produit les\nentiers de `a` (inclus) à `b` (exclu).\n"
            },
            {
              "text": "`[3, 9]`",
              "correct": false,
              "feedback": "Erreur : ce serait `range(3, 9, 6)` qui\ndonne `[3]` seulement.\n"
            }
          ],
          "explanation": "Trois formes : `range(n)` (0 à n-1),\n`range(a, b)` (a à b-1),\n`range(a, b, pas)` (a à b-1 par pas)."
        },
        {
          "statement": "Que produit `list(range(5, 12))` ?",
          "options": [
            {
              "text": "`[6, 7, 8, 9, 10, 11, 12]`",
              "correct": false,
              "feedback": "Erreur : 5 est **inclus**, 12 est exclu.\n"
            },
            {
              "text": "`[5, 6, 7, 8, 9, 10, 11, 12]`",
              "correct": false,
              "feedback": "Erreur : 12 est **exclu**.\n"
            },
            {
              "text": "`[5, 6, 7, 8, 9, 10, 11]`",
              "correct": true,
              "feedback": "Bonne réponse : `range(a, b)` produit les\nentiers de `a` (inclus) à `b` (exclu).\n"
            },
            {
              "text": "`[5, 12]`",
              "correct": false,
              "feedback": "Erreur : ce serait `range(5, 12, 7)` qui\ndonne `[5]` seulement.\n"
            }
          ],
          "explanation": "Trois formes : `range(n)` (0 à n-1),\n`range(a, b)` (a à b-1),\n`range(a, b, pas)` (a à b-1 par pas)."
        }
      ]
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "while"
      ],
      "title": "Boucle while",
      "partie": "while",
      "statement": "Que fait la boucle suivante ?\n```python\nn = 10\nwhile n > 0:\n    print(n)\n    n -= 1\n```",
      "options": [
        {
          "text": "Affiche les nombres de 1 à 10 dans l'ordre croissant",
          "correct": false,
          "feedback": "Erreur : la boucle décrémente, donc affiche\nen ordre **décroissant**.\n"
        },
        {
          "text": "Tourne sans fin, car `n` reste toujours positif",
          "correct": false,
          "feedback": "Erreur : `n -= 1` fait décroître `n` à chaque\ntour ; après dix tours, `n` vaut 0 et la\ncondition devient fausse.\n"
        },
        {
          "text": "N'affiche rien, car la condition est fausse au départ",
          "correct": false,
          "feedback": "Erreur : `n` vaut 10 au départ, donc `n > 0`\nest vrai et la boucle s'exécute au moins une\nfois.\n"
        },
        {
          "text": "Affiche les nombres de 10 à 1",
          "correct": true,
          "feedback": "Bonne réponse : à chaque tour, on affiche\n`n`, puis on le diminue de 1, d'où un\naffichage décroissant. Quand n atteint 0,\non sort.\n"
        }
      ],
      "explanation": "`while` est une boucle **non bornée** : on\nteste une condition à chaque tour. Bien\nvérifier que la condition finira par devenir\nfausse pour éviter une boucle infinie."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "for",
        "indentation"
      ],
      "title": "Indentation et corps de boucle",
      "partie": "for",
      "statement": "Qu'affiche ce code ?\n```python\nfor i in range(3):\n    print(\"a\")\nprint(\"b\")\n```",
      "options": [
        {
          "text": "`a a a b`",
          "correct": true,
          "feedback": "Bonne réponse : seule la ligne indentée fait\npartie du corps de la boucle, donc `a` est\naffiché trois fois ; `print(\"b\")` n'est\nexécuté qu'une fois, après la boucle.\n"
        },
        {
          "text": "`a b a b a b`",
          "correct": false,
          "feedback": "Erreur : `print(\"b\")` n'est pas indenté, il ne\nfait pas partie du corps de la boucle et n'est\nexécuté qu'une seule fois, après les trois\ntours.\n"
        },
        {
          "text": "`a b`",
          "correct": false,
          "feedback": "Erreur : `range(3)` fournit trois valeurs, le\ncorps de la boucle s'exécute donc trois fois\net `a` est affiché trois fois.\n"
        },
        {
          "text": "`a a a`",
          "correct": false,
          "feedback": "Erreur : le programme ne s'arrête pas à la fin\nde la boucle ; l'instruction `print(\"b\")`, qui\nla suit, est exécutée une fois.\n"
        }
      ],
      "explanation": "L'indentation délimite le corps de la boucle :\ntout ce qui est décalé se répète, ce qui revient\nau niveau de la ligne `for` ne s'exécute qu'une\nfois, après le dernier tour."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "while",
        "trace"
      ],
      "title": "Valeur finale d'un while",
      "partie": "while",
      "statement": "Que vaut `x` à la fin de ce code ?\n```python\nx = 1\nwhile x < 20:\n    x = x * 3\n```",
      "options": [
        {
          "text": "9",
          "correct": false,
          "feedback": "Erreur : pour `x = 9`, la condition `9 < 20`\nest encore vraie, donc un tour supplémentaire\na lieu et `x` passe à 27.\n"
        },
        {
          "text": "20",
          "correct": false,
          "feedback": "Erreur : `x` ne prend que les valeurs 1, 3, 9, 27 ;\nrien ne l'arrête exactement sur la borne\n20.\n"
        },
        {
          "text": "27",
          "correct": true,
          "feedback": "Bonne réponse : trace : 1 → 3 → 9 → 27. Pour\n`x = 27`, la condition `27 < 20` est fausse,\nla boucle s'arrête.\n"
        },
        {
          "text": "81",
          "correct": false,
          "feedback": "Erreur : un tour de trop. Dès que `x` vaut 27,\nla condition est fausse et le corps n'est plus\nexécuté.\n"
        }
      ],
      "explanation": "Une boucle `while` peut dépasser la borne de sa\ncondition : le corps s'exécute une dernière fois\nquand la condition est encore vraie, et c'est\ncette valeur qui reste à la sortie.",
      "variants": [
        {
          "statement": "Que vaut `x` à la fin de ce code ?\n```python\nx = 1\nwhile x < 20:\n    x = x * 3\n```",
          "options": [
            {
              "text": "9",
              "correct": false,
              "feedback": "Erreur : pour `x = 9`, la condition `9 < 20`\nest encore vraie, donc un tour supplémentaire\na lieu et `x` passe à 27.\n"
            },
            {
              "text": "20",
              "correct": false,
              "feedback": "Erreur : `x` ne prend que les valeurs 1, 3, 9, 27 ;\nrien ne l'arrête exactement sur la borne\n20.\n"
            },
            {
              "text": "27",
              "correct": true,
              "feedback": "Bonne réponse : trace : 1 → 3 → 9 → 27. Pour\n`x = 27`, la condition `27 < 20` est fausse,\nla boucle s'arrête.\n"
            },
            {
              "text": "81",
              "correct": false,
              "feedback": "Erreur : un tour de trop. Dès que `x` vaut 27,\nla condition est fausse et le corps n'est plus\nexécuté.\n"
            }
          ],
          "explanation": "Une boucle `while` peut dépasser la borne de sa\ncondition : le corps s'exécute une dernière fois\nquand la condition est encore vraie, et c'est\ncette valeur qui reste à la sortie."
        },
        {
          "statement": "Que vaut `x` à la fin de ce code ?\n```python\nx = 1\nwhile x < 50:\n    x = x * 2\n```",
          "options": [
            {
              "text": "32",
              "correct": false,
              "feedback": "Erreur : pour `x = 32`, la condition `32 < 50`\nest encore vraie, donc un tour supplémentaire\na lieu et `x` passe à 64.\n"
            },
            {
              "text": "50",
              "correct": false,
              "feedback": "Erreur : `x` ne prend que les valeurs 1, 2, 4, 8, 16, 32, 64 ;\nrien ne l'arrête exactement sur la borne\n50.\n"
            },
            {
              "text": "64",
              "correct": true,
              "feedback": "Bonne réponse : trace : 1 → 2 → 4 → 8 → 16 → 32 → 64. Pour\n`x = 64`, la condition `64 < 50` est fausse,\nla boucle s'arrête.\n"
            },
            {
              "text": "128",
              "correct": false,
              "feedback": "Erreur : un tour de trop. Dès que `x` vaut 64,\nla condition est fausse et le corps n'est plus\nexécuté.\n"
            }
          ],
          "explanation": "Une boucle `while` peut dépasser la borne de sa\ncondition : le corps s'exécute une dernière fois\nquand la condition est encore vraie, et c'est\ncette valeur qui reste à la sortie."
        },
        {
          "statement": "Que vaut `x` à la fin de ce code ?\n```python\nx = 2\nwhile x < 100:\n    x = x * 3\n```",
          "options": [
            {
              "text": "54",
              "correct": false,
              "feedback": "Erreur : pour `x = 54`, la condition `54 < 100`\nest encore vraie, donc un tour supplémentaire\na lieu et `x` passe à 162.\n"
            },
            {
              "text": "100",
              "correct": false,
              "feedback": "Erreur : `x` ne prend que les valeurs 2, 6, 18, 54, 162 ;\nrien ne l'arrête exactement sur la borne\n100.\n"
            },
            {
              "text": "162",
              "correct": true,
              "feedback": "Bonne réponse : trace : 2 → 6 → 18 → 54 → 162. Pour\n`x = 162`, la condition `162 < 100` est fausse,\nla boucle s'arrête.\n"
            },
            {
              "text": "486",
              "correct": false,
              "feedback": "Erreur : un tour de trop. Dès que `x` vaut 162,\nla condition est fausse et le corps n'est plus\nexécuté.\n"
            }
          ],
          "explanation": "Une boucle `while` peut dépasser la borne de sa\ncondition : le corps s'exécute une dernière fois\nquand la condition est encore vraie, et c'est\ncette valeur qui reste à la sortie."
        }
      ]
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "parite"
      ],
      "title": "Test de parité",
      "partie": "conditions",
      "statement": "Quelle expression vaut `True` si et seulement\nsi `n` est **pair** ?",
      "options": [
        {
          "text": "`n // 2 == 0`",
          "correct": false,
          "feedback": "Erreur : ce test est vrai pour n = 0 ou\nn = 1, pas pour la parité.\n"
        },
        {
          "text": "`n % 2 == 0`",
          "correct": true,
          "feedback": "Bonne réponse : `%` donne le reste. Reste\nnul ⇔ pair.\n"
        },
        {
          "text": "`n / 2 == 0`",
          "correct": false,
          "feedback": "Erreur : ce test est rarement vrai (n = 0\nuniquement).\n"
        },
        {
          "text": "`n == 2`",
          "correct": false,
          "feedback": "Erreur : seulement vrai pour n = 2.\n"
        }
      ],
      "explanation": "Idiome très courant. Variante :\n`bool(n % 2)` est `True` ⇔ `n` est impair."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "for",
        "accumulateur"
      ],
      "title": "Boucle simple",
      "partie": "for",
      "statement": "Que fait ce code ?\n```python\ntotal = 0\nfor i in range(1, 5):\n    total += i\nprint(total)\n```",
      "options": [
        {
          "text": "Affiche `10` (= 1+2+3+4)",
          "correct": true,
          "feedback": "Bonne réponse : `range(1, 5)` produit\n`[1, 2, 3, 4]`. La somme vaut 10. C'est\nle schéma « accumulateur ».\n"
        },
        {
          "text": "Boucle infinie",
          "correct": false,
          "feedback": "Erreur : `for` sur un range est toujours\nfini.\n"
        },
        {
          "text": "Affiche `4`",
          "correct": false,
          "feedback": "Erreur : on additionne plusieurs valeurs.\n"
        },
        {
          "text": "Affiche `15` (= 1+2+3+4+5)",
          "correct": false,
          "feedback": "Erreur : 5 est **exclu** de `range(1, 5)`.\n"
        }
      ],
      "explanation": "Schéma d'accumulation à connaître. En Python,\non peut aussi écrire `sum(range(1, 5))`.",
      "variants": [
        {
          "statement": "Que fait ce code ?\n```python\ntotal = 0\nfor i in range(1, 5):\n    total += i\nprint(total)\n```",
          "options": [
            {
              "text": "Affiche `10` (= 1+2+3+4)",
              "correct": true,
              "feedback": "Bonne réponse : `range(1, 5)` produit\n`[1, 2, 3, 4]`. La somme vaut 10. C'est\nle schéma « accumulateur ».\n"
            },
            {
              "text": "Boucle infinie",
              "correct": false,
              "feedback": "Erreur : `for` sur un range est toujours\nfini.\n"
            },
            {
              "text": "Affiche `4`",
              "correct": false,
              "feedback": "Erreur : on additionne plusieurs valeurs.\n"
            },
            {
              "text": "Affiche `15` (= 1+2+3+4+5)",
              "correct": false,
              "feedback": "Erreur : 5 est **exclu** de `range(1, 5)`.\n"
            }
          ],
          "explanation": "Schéma d'accumulation à connaître. En Python,\non peut aussi écrire `sum(range(1, 5))`."
        },
        {
          "statement": "Que fait ce code ?\n```python\ntotal = 0\nfor i in range(1, 7):\n    total += i\nprint(total)\n```",
          "options": [
            {
              "text": "Affiche `21` (= 1+2+3+4+5+6)",
              "correct": true,
              "feedback": "Bonne réponse : `range(1, 7)` produit\n`[1, 2, 3, 4, 5, 6]`. La somme vaut 21. C'est\nle schéma « accumulateur ».\n"
            },
            {
              "text": "Boucle infinie",
              "correct": false,
              "feedback": "Erreur : `for` sur un range est toujours\nfini.\n"
            },
            {
              "text": "Affiche `6`",
              "correct": false,
              "feedback": "Erreur : on additionne plusieurs valeurs.\n"
            },
            {
              "text": "Affiche `28` (= 1+2+3+4+5+6+7)",
              "correct": false,
              "feedback": "Erreur : 7 est **exclu** de `range(1, 7)`.\n"
            }
          ],
          "explanation": "Schéma d'accumulation à connaître. En Python,\non peut aussi écrire `sum(range(1, 7))`."
        },
        {
          "statement": "Que fait ce code ?\n```python\ntotal = 0\nfor i in range(2, 6):\n    total += i\nprint(total)\n```",
          "options": [
            {
              "text": "Affiche `14` (= 2+3+4+5)",
              "correct": true,
              "feedback": "Bonne réponse : `range(2, 6)` produit\n`[2, 3, 4, 5]`. La somme vaut 14. C'est\nle schéma « accumulateur ».\n"
            },
            {
              "text": "Boucle infinie",
              "correct": false,
              "feedback": "Erreur : `for` sur un range est toujours\nfini.\n"
            },
            {
              "text": "Affiche `5`",
              "correct": false,
              "feedback": "Erreur : on additionne plusieurs valeurs.\n"
            },
            {
              "text": "Affiche `20` (= 2+3+4+5+6)",
              "correct": false,
              "feedback": "Erreur : 6 est **exclu** de `range(2, 6)`.\n"
            }
          ],
          "explanation": "Schéma d'accumulation à connaître. En Python,\non peut aussi écrire `sum(range(2, 6))`."
        }
      ]
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "boucles-imbriquees"
      ],
      "title": "Boucles imbriquées",
      "partie": "for",
      "statement": "Combien de fois `print(...)` est-il exécuté ?\n```python\nfor i in range(3):\n    for j in range(2):\n        print(i, j)\n```",
      "options": [
        {
          "text": "5",
          "correct": false,
          "feedback": "Erreur : ce serait additif (3 + 2), mais\nc'est multiplicatif.\n"
        },
        {
          "text": "3",
          "correct": false,
          "feedback": "Erreur : la double boucle multiplie les\nitérations.\n"
        },
        {
          "text": "9",
          "correct": false,
          "feedback": "Erreur : ce serait `range(3) × range(3)`.\n"
        },
        {
          "text": "6",
          "correct": true,
          "feedback": "Bonne réponse : 3 × 2 = 6 itérations.\nCouples : (0,0), (0,1), (1,0), (1,1), (2,0), (2,1).\n"
        }
      ],
      "explanation": "Règle : pour des boucles imbriquées, le total\nest le produit des nombres d'itérations.",
      "variants": [
        {
          "statement": "Combien de fois `print(...)` est-il exécuté ?\n```python\nfor i in range(3):\n    for j in range(2):\n        print(i, j)\n```",
          "options": [
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : ce serait additif (3 + 2), mais\nc'est multiplicatif.\n"
            },
            {
              "text": "3",
              "correct": false,
              "feedback": "Erreur : la double boucle multiplie les\nitérations.\n"
            },
            {
              "text": "9",
              "correct": false,
              "feedback": "Erreur : ce serait `range(3) × range(3)`.\n"
            },
            {
              "text": "6",
              "correct": true,
              "feedback": "Bonne réponse : 3 × 2 = 6 itérations.\nCouples : (0,0), (0,1), (1,0), (1,1), (2,0), (2,1).\n"
            }
          ],
          "explanation": "Règle : pour des boucles imbriquées, le total\nest le produit des nombres d'itérations."
        },
        {
          "statement": "Combien de fois `print(...)` est-il exécuté ?\n```python\nfor i in range(4):\n    for j in range(3):\n        print(i, j)\n```",
          "options": [
            {
              "text": "7",
              "correct": false,
              "feedback": "Erreur : ce serait additif (4 + 3), mais\nc'est multiplicatif.\n"
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : la double boucle multiplie les\nitérations.\n"
            },
            {
              "text": "16",
              "correct": false,
              "feedback": "Erreur : ce serait `range(4) × range(4)`.\n"
            },
            {
              "text": "12",
              "correct": true,
              "feedback": "Bonne réponse : 4 × 3 = 12 itérations.\nCouples : (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2), (3,0), (3,1), (3,2).\n"
            }
          ],
          "explanation": "Règle : pour des boucles imbriquées, le total\nest le produit des nombres d'itérations."
        },
        {
          "statement": "Combien de fois `print(...)` est-il exécuté ?\n```python\nfor i in range(2):\n    for j in range(5):\n        print(i, j)\n```",
          "options": [
            {
              "text": "7",
              "correct": false,
              "feedback": "Erreur : ce serait additif (2 + 5), mais\nc'est multiplicatif.\n"
            },
            {
              "text": "2",
              "correct": false,
              "feedback": "Erreur : la double boucle multiplie les\nitérations.\n"
            },
            {
              "text": "4",
              "correct": false,
              "feedback": "Erreur : ce serait `range(2) × range(2)`.\n"
            },
            {
              "text": "10",
              "correct": true,
              "feedback": "Bonne réponse : 2 × 5 = 10 itérations.\nCouples : (0,0), (0,1), (0,2), (0,3), (0,4), (1,0), (1,1), (1,2), (1,3), (1,4).\n"
            }
          ],
          "explanation": "Règle : pour des boucles imbriquées, le total\nest le produit des nombres d'itérations."
        }
      ]
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "logique-courte"
      ],
      "title": "Évaluation paresseuse",
      "partie": "conditions",
      "statement": "Pourquoi `False and 1/0 == 0` ne lève-t-il pas\n`ZeroDivisionError` en Python ?",
      "options": [
        {
          "text": "Parce que `==` est prioritaire sur `/`, donc la division n'est jamais calculée",
          "correct": false,
          "feedback": "Erreur : `/` est prioritaire sur `==`. Évaluée\nseule, l'expression `1/0 == 0` lève bien\n`ZeroDivisionError`.\n"
        },
        {
          "text": "Parce que la division par zéro est tolérée par Python",
          "correct": false,
          "feedback": "Erreur : `1/0` lève bien `ZeroDivisionError`.\n"
        },
        {
          "text": "Parce que les nombres entiers ne plantent jamais",
          "correct": false,
          "feedback": "Erreur : `1/0` plante en général.\n"
        },
        {
          "text": "Parce que `and` est paresseux : si le premier est `False`, le second est ignoré",
          "correct": true,
          "feedback": "Bonne réponse : Python optimise. Cela permet\ndes idiomes comme `if liste and liste[0] >\n0:` (test de non-vide avant d'accéder à\nl'indice 0).\n"
        }
      ],
      "explanation": "Évaluation paresseuse : `and` s'arrête au\npremier `False` ; `or` s'arrête au premier\n`True`. Très utile pour court-circuiter des\ntests coûteux ou risqués."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "while",
        "booleen",
        "trace"
      ],
      "title": "Trace d'un arrêt anticipé",
      "partie": "while",
      "statement": "Que produit ce code ?\n```python\ni = 0\nfini = False\nwhile i < 10 and not fini:\n    if i == 3:\n        fini = True\n    else:\n        print(i)\n    i = i + 1\n```",
      "options": [
        {
          "text": "0 1 2 3 4 5 6 7 8 9",
          "correct": false,
          "feedback": "Erreur : dès que `fini` passe à `True`, la\ncondition `not fini` devient fausse et la\nboucle s'arrête, bien avant `i = 10`.\n"
        },
        {
          "text": "Aucune sortie",
          "correct": false,
          "feedback": "Erreur : au départ `fini` vaut `False`, donc\n`not fini` vaut `True` et la boucle démarre ;\nl'arrêt n'intervient qu'à i = 3.\n"
        },
        {
          "text": "0 1 2",
          "correct": true,
          "feedback": "Bonne réponse : on affiche 0 1 2 ; au tour\ni = 3, la branche `if` met `fini` à `True` sans\nafficher, puis la condition du `while` devient\nfausse.\n"
        },
        {
          "text": "0 1 2 3",
          "correct": false,
          "feedback": "Erreur : au tour i = 3, c'est la branche `if`\nqui s'exécute, pas le `else` : le 3 n'est pas\naffiché.\n"
        }
      ],
      "explanation": "Tracer pas à pas : à chaque tour avec i inférieur\nà 3, print(i) ; à i = 3, `fini`\npasse à `True`, puis le test `not fini` échoue.",
      "variants": [
        {
          "statement": "Que produit ce code ?\n```python\ni = 0\nfini = False\nwhile i < 10 and not fini:\n    if i == 3:\n        fini = True\n    else:\n        print(i)\n    i = i + 1\n```",
          "options": [
            {
              "text": "0 1 2 3 4 5 6 7 8 9",
              "correct": false,
              "feedback": "Erreur : dès que `fini` passe à `True`, la\ncondition `not fini` devient fausse et la\nboucle s'arrête, bien avant `i = 10`.\n"
            },
            {
              "text": "Aucune sortie",
              "correct": false,
              "feedback": "Erreur : au départ `fini` vaut `False`, donc\n`not fini` vaut `True` et la boucle démarre ;\nl'arrêt n'intervient qu'à i = 3.\n"
            },
            {
              "text": "0 1 2",
              "correct": true,
              "feedback": "Bonne réponse : on affiche 0 1 2 ; au tour\ni = 3, la branche `if` met `fini` à `True` sans\nafficher, puis la condition du `while` devient\nfausse.\n"
            },
            {
              "text": "0 1 2 3",
              "correct": false,
              "feedback": "Erreur : au tour i = 3, c'est la branche `if`\nqui s'exécute, pas le `else` : le 3 n'est pas\naffiché.\n"
            }
          ],
          "explanation": "Tracer pas à pas : à chaque tour avec i inférieur\nà 3, print(i) ; à i = 3, `fini`\npasse à `True`, puis le test `not fini` échoue."
        },
        {
          "statement": "Que produit ce code ?\n```python\ni = 0\nfini = False\nwhile i < 10 and not fini:\n    if i == 5:\n        fini = True\n    else:\n        print(i)\n    i = i + 1\n```",
          "options": [
            {
              "text": "0 1 2 3 4 5 6 7 8 9",
              "correct": false,
              "feedback": "Erreur : dès que `fini` passe à `True`, la\ncondition `not fini` devient fausse et la\nboucle s'arrête, bien avant `i = 10`.\n"
            },
            {
              "text": "Aucune sortie",
              "correct": false,
              "feedback": "Erreur : au départ `fini` vaut `False`, donc\n`not fini` vaut `True` et la boucle démarre ;\nl'arrêt n'intervient qu'à i = 5.\n"
            },
            {
              "text": "0 1 2 3 4",
              "correct": true,
              "feedback": "Bonne réponse : on affiche 0 1 2 3 4 ; au tour\ni = 5, la branche `if` met `fini` à `True` sans\nafficher, puis la condition du `while` devient\nfausse.\n"
            },
            {
              "text": "0 1 2 3 4 5",
              "correct": false,
              "feedback": "Erreur : au tour i = 5, c'est la branche `if`\nqui s'exécute, pas le `else` : le 5 n'est pas\naffiché.\n"
            }
          ],
          "explanation": "Tracer pas à pas : à chaque tour avec i inférieur\nà 5, print(i) ; à i = 5, `fini`\npasse à `True`, puis le test `not fini` échoue."
        },
        {
          "statement": "Que produit ce code ?\n```python\ni = 0\nfini = False\nwhile i < 10 and not fini:\n    if i == 2:\n        fini = True\n    else:\n        print(i)\n    i = i + 1\n```",
          "options": [
            {
              "text": "0 1 2 3 4 5 6 7 8 9",
              "correct": false,
              "feedback": "Erreur : dès que `fini` passe à `True`, la\ncondition `not fini` devient fausse et la\nboucle s'arrête, bien avant `i = 10`.\n"
            },
            {
              "text": "Aucune sortie",
              "correct": false,
              "feedback": "Erreur : au départ `fini` vaut `False`, donc\n`not fini` vaut `True` et la boucle démarre ;\nl'arrêt n'intervient qu'à i = 2.\n"
            },
            {
              "text": "0 1",
              "correct": true,
              "feedback": "Bonne réponse : on affiche 0 1 ; au tour\ni = 2, la branche `if` met `fini` à `True` sans\nafficher, puis la condition du `while` devient\nfausse.\n"
            },
            {
              "text": "0 1 2",
              "correct": false,
              "feedback": "Erreur : au tour i = 2, c'est la branche `if`\nqui s'exécute, pas le `else` : le 2 n'est pas\naffiché.\n"
            }
          ],
          "explanation": "Tracer pas à pas : à chaque tour avec i inférieur\nà 2, print(i) ; à i = 2, `fini`\npasse à `True`, puis le test `not fini` échoue."
        }
      ]
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "for",
        "not",
        "modulo"
      ],
      "title": "Trace avec négation",
      "partie": "for",
      "statement": "Que produit ce code ?\n```python\nfor i in range(5):\n    if not (i % 2 == 0):\n        print(i)\n```",
      "options": [
        {
          "text": "Boucle infinie",
          "correct": false,
          "feedback": "Erreur : `for` sur un range est borné.\n"
        },
        {
          "text": "1 3 (les impairs)",
          "correct": true,
          "feedback": "Bonne réponse : pour i ∈ {0, 2, 4} (pairs),\n`i % 2 == 0` est vrai, donc sa négation est\nfausse et le `print` est sauté. Restent 1, 3.\n"
        },
        {
          "text": "0 1 2 3 4",
          "correct": false,
          "feedback": "Erreur : la condition filtre les valeurs ;\nl'affichage n'a lieu que pour les impairs.\n"
        },
        {
          "text": "0 2 4 (les pairs)",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse, le `not` renverse le\ntest de parité : on affiche ceux qui ne sont\n**pas** pairs.\n"
        }
      ],
      "explanation": "`not` inverse un booléen : `not (i % 2 == 0)`\nest vrai exactement quand `i` est impair.\nÉquivalent plus direct : `if i % 2 != 0:`.",
      "variants": [
        {
          "statement": "Que produit ce code ?\n```python\nfor i in range(5):\n    if not (i % 2 == 0):\n        print(i)\n```",
          "options": [
            {
              "text": "Boucle infinie",
              "correct": false,
              "feedback": "Erreur : `for` sur un range est borné.\n"
            },
            {
              "text": "1 3 (les impairs)",
              "correct": true,
              "feedback": "Bonne réponse : pour i ∈ {0, 2, 4} (pairs),\n`i % 2 == 0` est vrai, donc sa négation est\nfausse et le `print` est sauté. Restent 1, 3.\n"
            },
            {
              "text": "0 1 2 3 4",
              "correct": false,
              "feedback": "Erreur : la condition filtre les valeurs ;\nl'affichage n'a lieu que pour les impairs.\n"
            },
            {
              "text": "0 2 4 (les pairs)",
              "correct": false,
              "feedback": "Erreur : c'est l'inverse, le `not` renverse le\ntest de parité : on affiche ceux qui ne sont\n**pas** pairs.\n"
            }
          ],
          "explanation": "`not` inverse un booléen : `not (i % 2 == 0)`\nest vrai exactement quand `i` est impair.\nÉquivalent plus direct : `if i % 2 != 0:`."
        },
        {
          "statement": "Que produit ce code ?\n```python\nfor i in range(7):\n    if not (i % 2 == 0):\n        print(i)\n```",
          "options": [
            {
              "text": "Boucle infinie",
              "correct": false,
              "feedback": "Erreur : `for` sur un range est borné.\n"
            },
            {
              "text": "1 3 5 (les impairs)",
              "correct": true,
              "feedback": "Bonne réponse : pour i ∈ {0, 2, 4, 6} (pairs),\n`i % 2 == 0` est vrai, donc sa négation est\nfausse et le `print` est sauté. Restent 1, 3, 5.\n"
            },
            {
              "text": "0 1 2 3 4 5 6",
              "correct": false,
              "feedback": "Erreur : la condition filtre les valeurs ;\nl'affichage n'a lieu que pour les impairs.\n"
            },
            {
              "text": "0 2 4 6 (les pairs)",
              "correct": false,
              "feedback": "Erreur : c'est l'inverse, le `not` renverse le\ntest de parité : on affiche ceux qui ne sont\n**pas** pairs.\n"
            }
          ],
          "explanation": "`not` inverse un booléen : `not (i % 2 == 0)`\nest vrai exactement quand `i` est impair.\nÉquivalent plus direct : `if i % 2 != 0:`."
        },
        {
          "statement": "Que produit ce code ?\n```python\nfor i in range(10):\n    if not (i % 2 == 0):\n        print(i)\n```",
          "options": [
            {
              "text": "Boucle infinie",
              "correct": false,
              "feedback": "Erreur : `for` sur un range est borné.\n"
            },
            {
              "text": "1 3 5 7 9 (les impairs)",
              "correct": true,
              "feedback": "Bonne réponse : pour i ∈ {0, 2, 4, 6, 8} (pairs),\n`i % 2 == 0` est vrai, donc sa négation est\nfausse et le `print` est sauté. Restent 1, 3, 5, 7, 9.\n"
            },
            {
              "text": "0 1 2 3 4 5 6 7 8 9",
              "correct": false,
              "feedback": "Erreur : la condition filtre les valeurs ;\nl'affichage n'a lieu que pour les impairs.\n"
            },
            {
              "text": "0 2 4 6 8 (les pairs)",
              "correct": false,
              "feedback": "Erreur : c'est l'inverse, le `not` renverse le\ntest de parité : on affiche ceux qui ne sont\n**pas** pairs.\n"
            }
          ],
          "explanation": "`not` inverse un booléen : `not (i % 2 == 0)`\nest vrai exactement quand `i` est impair.\nÉquivalent plus direct : `if i % 2 != 0:`."
        }
      ]
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "while-condition"
      ],
      "title": "While et condition",
      "partie": "while",
      "statement": "Que fait ce code, en supposant la saisie d'un entier strictement positif ?\n```python\nn = int(input())\nwhile n != 1:\n    if n % 2 == 0:\n        n = n // 2\n    else:\n        n = 3 * n + 1\n    print(n)\n```",
      "options": [
        {
          "text": "Boucle sans fin dès que l'entier saisi est impair",
          "correct": false,
          "feedback": "Erreur : un entier impair devient pair par `3 * n + 1`, puis il est\ndivisé par $2$. Pour $n = 3$, le programme affiche $10$, $5$, $16$, $8$,\n$4$, $2$, $1$ puis s'arrête.\n"
        },
        {
          "text": "Affiche uniquement la valeur finale de `n`, une fois la boucle terminée",
          "correct": false,
          "feedback": "Erreur : `print(n)` est dans le corps de la boucle, donc exécuté à chaque\ntour. Le programme affiche toutes les valeurs prises par `n`, pas\nseulement la dernière.\n"
        },
        {
          "text": "Affiche les valeurs successives de `n`, tant que `n` ne vaut pas `1`",
          "correct": true,
          "feedback": "Bonne réponse : tant que `n` ne vaut pas `1`, la boucle calcule la valeur\nsuivante et l'affiche. Pour $n = 6$, on obtient $3$, $10$, $5$, $16$,\n$8$, $4$, $2$, $1$.\n"
        },
        {
          "text": "Divise `n` par `2` jusqu'à obtenir `1`, puis affiche le nombre de divisions",
          "correct": false,
          "feedback": "Erreur : quand `n` est impair, le code le remplace par `3 * n + 1` au lieu\nde le diviser, et aucun compteur n'est calculé : ce sont les valeurs de\n`n` qui s'affichent.\n"
        }
      ],
      "explanation": "Ce code calcule la suite de Syracuse : on divise $n$ par $2$ s'il est\npair, on le remplace par $3n + 1$ sinon. La conjecture de Syracuse\naffirme que cette suite atteint toujours $1$, quel que soit l'entier de\ndépart ; elle n'est pas démontrée, si bien que personne ne sait prouver\nque cette boucle s'arrête pour tout $n$."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "bug-condition"
      ],
      "title": "Bug d'imbrication",
      "partie": "conditions",
      "statement": "```python\nx = 5\nif x > 0:\n    if x > 10:\n        print(\"grand\")\n    else:\n        print(\"moyen\")\nelse:\n    print(\"négatif ou nul\")\n```\nQu'affiche le code ?",
      "options": [
        {
          "text": "Rien",
          "correct": false,
          "feedback": "Erreur : un cas est forcément exécuté.\n"
        },
        {
          "text": "`négatif ou nul`",
          "correct": false,
          "feedback": "Erreur : 5 est positif.\n"
        },
        {
          "text": "`moyen`",
          "correct": true,
          "feedback": "Bonne réponse : x = 5 entre dans le premier\n`if` (5 > 0) ; puis comme 5 ≤ 10, on\nexécute le `else` interne.\n"
        },
        {
          "text": "`grand`",
          "correct": false,
          "feedback": "Erreur : 5 n'est pas > 10.\n"
        }
      ],
      "explanation": "Important : l'`else` est toujours apparié au\n`if` du même niveau d'indentation. Lecture\nattentive nécessaire."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "chained"
      ],
      "title": "Chaînage",
      "partie": "conditions",
      "statement": "L'expression `0 < x < 100` en Python est\néquivalente à :",
      "options": [
        {
          "text": "`0 < x or x < 100`",
          "correct": false,
          "feedback": "Erreur : c'est `and` (et), pas `or` (ou).\n"
        },
        {
          "text": "`0 < x and x < 100`",
          "correct": true,
          "feedback": "Bonne réponse : Python autorise le\nchaînage. Idiomatique pour les bornes.\n"
        },
        {
          "text": "Une erreur de syntaxe",
          "correct": false,
          "feedback": "Erreur : la syntaxe est valide en Python.\n"
        },
        {
          "text": "`(0 < x) < 100`",
          "correct": false,
          "feedback": "Erreur : ce serait du C ou du JavaScript, pas\ndu Python.\n"
        }
      ],
      "explanation": "Cas pratique : `if 0 <= i < len(liste):` pour\nvérifier qu'un indice est valide. Bien plus\nlisible que `if i >= 0 and i < len(liste):`."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "parcours-liste"
      ],
      "title": "Parcours de liste",
      "partie": "for",
      "statement": "Quel code affiche **tous** les éléments d'une\nliste `liste`, un par ligne ?",
      "options": [
        {
          "text": "```python\nwhile i in liste:\n    print(i)\n```\n",
          "correct": false,
          "feedback": "Erreur : `i in liste` est un test, pas une\nitération. Et `i` n'est pas défini.\n"
        },
        {
          "text": "```python\nfor x in liste:\n    print(x)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : la boucle parcourt\ndirectement les éléments, du premier au\ndernier, sans manipuler d'indice.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste) - 1):\n    print(liste[i])\n```\n",
          "correct": false,
          "feedback": "Erreur : `range(len(liste) - 1)` s'arrête un\nindice trop tôt, donc le dernier élément\nn'est jamais affiché. Il faudrait\n`range(len(liste))`.\n"
        },
        {
          "text": "```python\ndo print(x) for x in liste\n```\n",
          "correct": false,
          "feedback": "Erreur : syntaxe invalide en Python (peut-\nêtre Ruby ou Perl).\n"
        }
      ],
      "explanation": "Bonne pratique : `for x in liste:` quand on\nn'a pas besoin de l'indice. Si l'indice est\nutile aussi, on parcourt les indices avec\n`for i in range(len(liste)):` et on lit\nl'élément par `liste[i]`."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "comprehension"
      ],
      "title": "Compréhension de liste",
      "partie": "for",
      "statement": "Quel est l'équivalent **pythonique** de :\n```python\ncarres = []\nfor i in range(10):\n    carres.append(i * i)\n```",
      "options": [
        {
          "text": "`carres = [i * i for i in range(10)]`",
          "correct": true,
          "feedback": "Bonne réponse : compréhension de liste,\nplus concise et idiomatique.\n"
        },
        {
          "text": "`carres = [10][i * i]`",
          "correct": false,
          "feedback": "Erreur : syntaxe invalide.\n"
        },
        {
          "text": "`carres = [i * 2 for i in range(10)]`",
          "correct": false,
          "feedback": "Erreur : `i * 2` calcule le **double**, pas le\ncarré : on obtient `[0, 2, 4, ..., 18]` au\nlieu de `[0, 1, 4, ..., 81]`.\n"
        },
        {
          "text": "`carres = range(10) ** 2`",
          "correct": false,
          "feedback": "Erreur : on ne peut pas élever un range au\ncarré.\n"
        }
      ],
      "explanation": "Compréhension de liste générale :\n`[expr for x in iterable if cond]`. Très\nutilisée en Python pour les transformations\nsimples."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "trace-boucle"
      ],
      "title": "Trace de while",
      "partie": "while",
      "statement": "Que vaut `n` à la fin de :\n```python\nn = 100\nwhile n > 1:\n    n = n // 2\n```",
      "options": [
        {
          "text": "Une infinité",
          "correct": false,
          "feedback": "Erreur : `n // 2` réduit n à chaque tour.\n"
        },
        {
          "text": "1",
          "correct": true,
          "feedback": "Bonne réponse : trace : 100 → 50 → 25 → 12\n→ 6 → 3 → 1. À ce moment, `1 > 1` est faux,\non sort.\n"
        },
        {
          "text": "0",
          "correct": false,
          "feedback": "Erreur : la boucle s'arrête quand n ≤ 1, donc\nn vaut au moins 1 à la fin.\n"
        },
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : 2 // 2 = 1, donc on continue.\n"
        }
      ],
      "explanation": "Cette boucle calcule combien de fois on peut\ndiviser n par 2. C'est le **logarithme entier**\nen base 2 (motif de complexité O(log n))."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "boucle-infinie-bug"
      ],
      "title": "Boucle infinie",
      "partie": "while",
      "statement": "Pourquoi cette boucle est-elle infinie ?\n```python\ni = 0\nwhile i < 10:\n    print(i)\n```",
      "options": [
        {
          "text": "Parce que `print` ne renvoie rien",
          "correct": false,
          "feedback": "Le fait que `print` ne\nrenvoie pas de valeur\n(autre que `None`) n'a\naucun lien avec la\nterminaison de la boucle.\nCe qui pose problème ici,\nc'est que `i` n'évolue\njamais.\n"
        },
        {
          "text": "Parce que `while` est toujours infini",
          "correct": false,
          "feedback": "Erreur : `while` peut très bien terminer.\n"
        },
        {
          "text": "Parce que l'initialisation `i = 0` serait fausse",
          "correct": false,
          "feedback": "Erreur : 0 < 10 est vrai.\n"
        },
        {
          "text": "Parce que `i` n'est jamais modifié dans la boucle",
          "correct": true,
          "feedback": "Bonne réponse : il faut ajouter `i += 1`\npour que la condition `i < 10` finisse par\ndevenir fausse.\n"
        }
      ],
      "explanation": "Bug ultra-classique chez les débutants. Toujours\nvérifier qu'un **variant** (ici, `i`) progresse\nvers la condition de sortie."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "range",
        "evaluation-unique"
      ],
      "title": "Borne d'un range modifiée en cours de boucle",
      "partie": "for",
      "statement": "Qu'affiche ce code ?\n```python\nn = 3\nfor i in range(n):\n    n = n + 1\nprint(i, n)\n```",
      "options": [
        {
          "text": "`2 6`, car `range(3)` est calculé une fois pour toutes au début de la boucle",
          "correct": true,
          "feedback": "Bonne réponse : `range(n)` est évalué une seule\nfois, avec `n` valant 3 ; la boucle fait donc\n3 tours (`i` vaut 0, 1 puis 2) et `n` est\nincrémenté 3 fois, de 3 à 6.\n"
        },
        {
          "text": "La boucle ne s'arrête jamais, car `n` augmente d'une unité à chaque tour",
          "correct": false,
          "feedback": "Erreur : la borne n'est pas relue à chaque tour.\nModifier `n` dans le corps ne change pas la\nsuite `range(3)` déjà construite, contrairement\nà une liste que l'on allonge en la parcourant.\n"
        },
        {
          "text": "`3 6`, car `i` termine à la valeur de la borne de départ, c'est-à-dire 3",
          "correct": false,
          "feedback": "Erreur : `range(3)` produit 0, 1 puis 2, jamais 3.\nLa dernière valeur prise par `i` est 2, car la\nborne d'un `range` est exclue.\n"
        },
        {
          "text": "Une erreur à l'exécution, car `n` ne peut pas être modifiée pendant le parcours de `range(n)`",
          "correct": false,
          "feedback": "Erreur : aucune règle n'interdit d'affecter `n`\ndans le corps de la boucle ; le code s'exécute\nsans erreur. Une fois la boucle lancée, `n` et\nla suite `range(3)` sont indépendants.\n"
        }
      ],
      "explanation": "`range(n)` est construit une seule fois, à l'entrée\ndans la boucle. Le nombre de tours est figé à ce\nmoment-là : modifier `n` ensuite n'allonge pas la\nboucle, à la différence d'une liste que l'on\nmodifie pendant son parcours.",
      "variants": [
        {
          "statement": "Qu'affiche ce code ?\n```python\nn = 3\nfor i in range(n):\n    n = n + 1\nprint(i, n)\n```",
          "options": [
            {
              "text": "`2 6`, car `range(3)` est calculé une fois pour toutes au début de la boucle",
              "correct": true,
              "feedback": "Bonne réponse : `range(n)` est évalué une seule\nfois, avec `n` valant 3 ; la boucle fait donc\n3 tours (`i` vaut 0, 1 puis 2) et `n` est\nincrémenté 3 fois, de 3 à 6.\n"
            },
            {
              "text": "La boucle ne s'arrête jamais, car `n` augmente d'une unité à chaque tour",
              "correct": false,
              "feedback": "Erreur : la borne n'est pas relue à chaque tour.\nModifier `n` dans le corps ne change pas la\nsuite `range(3)` déjà construite, contrairement\nà une liste que l'on allonge en la parcourant.\n"
            },
            {
              "text": "`3 6`, car `i` termine à la valeur de la borne de départ, c'est-à-dire 3",
              "correct": false,
              "feedback": "Erreur : `range(3)` produit 0, 1 puis 2, jamais 3.\nLa dernière valeur prise par `i` est 2, car la\nborne d'un `range` est exclue.\n"
            },
            {
              "text": "Une erreur à l'exécution, car `n` ne peut pas être modifiée pendant le parcours de `range(n)`",
              "correct": false,
              "feedback": "Erreur : aucune règle n'interdit d'affecter `n`\ndans le corps de la boucle ; le code s'exécute\nsans erreur. Une fois la boucle lancée, `n` et\nla suite `range(3)` sont indépendants.\n"
            }
          ],
          "explanation": "`range(n)` est construit une seule fois, à l'entrée\ndans la boucle. Le nombre de tours est figé à ce\nmoment-là : modifier `n` ensuite n'allonge pas la\nboucle, à la différence d'une liste que l'on\nmodifie pendant son parcours."
        },
        {
          "statement": "Qu'affiche ce code ?\n```python\nn = 5\nfor i in range(n):\n    n = n + 1\nprint(i, n)\n```",
          "options": [
            {
              "text": "`4 10`, car `range(5)` est calculé une fois pour toutes au début de la boucle",
              "correct": true,
              "feedback": "Bonne réponse : `range(n)` est évalué une seule\nfois, avec `n` valant 5 ; la boucle fait donc\n5 tours (`i` vaut 0, 1, 2, 3 puis 4) et `n` est\nincrémenté 5 fois, de 5 à 10.\n"
            },
            {
              "text": "La boucle ne s'arrête jamais, car `n` augmente d'une unité à chaque tour",
              "correct": false,
              "feedback": "Erreur : la borne n'est pas relue à chaque tour.\nModifier `n` dans le corps ne change pas la\nsuite `range(5)` déjà construite, contrairement\nà une liste que l'on allonge en la parcourant.\n"
            },
            {
              "text": "`5 10`, car `i` termine à la valeur de la borne de départ, c'est-à-dire 5",
              "correct": false,
              "feedback": "Erreur : `range(5)` produit 0, 1, 2, 3 puis 4, jamais 5.\nLa dernière valeur prise par `i` est 4, car la\nborne d'un `range` est exclue.\n"
            },
            {
              "text": "Une erreur à l'exécution, car `n` ne peut pas être modifiée pendant le parcours de `range(n)`",
              "correct": false,
              "feedback": "Erreur : aucune règle n'interdit d'affecter `n`\ndans le corps de la boucle ; le code s'exécute\nsans erreur. Une fois la boucle lancée, `n` et\nla suite `range(5)` sont indépendants.\n"
            }
          ],
          "explanation": "`range(n)` est construit une seule fois, à l'entrée\ndans la boucle. Le nombre de tours est figé à ce\nmoment-là : modifier `n` ensuite n'allonge pas la\nboucle, à la différence d'une liste que l'on\nmodifie pendant son parcours."
        },
        {
          "statement": "Qu'affiche ce code ?\n```python\nn = 4\nfor i in range(n):\n    n = n + 1\nprint(i, n)\n```",
          "options": [
            {
              "text": "`3 8`, car `range(4)` est calculé une fois pour toutes au début de la boucle",
              "correct": true,
              "feedback": "Bonne réponse : `range(n)` est évalué une seule\nfois, avec `n` valant 4 ; la boucle fait donc\n4 tours (`i` vaut 0, 1, 2 puis 3) et `n` est\nincrémenté 4 fois, de 4 à 8.\n"
            },
            {
              "text": "La boucle ne s'arrête jamais, car `n` augmente d'une unité à chaque tour",
              "correct": false,
              "feedback": "Erreur : la borne n'est pas relue à chaque tour.\nModifier `n` dans le corps ne change pas la\nsuite `range(4)` déjà construite, contrairement\nà une liste que l'on allonge en la parcourant.\n"
            },
            {
              "text": "`4 8`, car `i` termine à la valeur de la borne de départ, c'est-à-dire 4",
              "correct": false,
              "feedback": "Erreur : `range(4)` produit 0, 1, 2 puis 3, jamais 4.\nLa dernière valeur prise par `i` est 3, car la\nborne d'un `range` est exclue.\n"
            },
            {
              "text": "Une erreur à l'exécution, car `n` ne peut pas être modifiée pendant le parcours de `range(n)`",
              "correct": false,
              "feedback": "Erreur : aucune règle n'interdit d'affecter `n`\ndans le corps de la boucle ; le code s'exécute\nsans erreur. Une fois la boucle lancée, `n` et\nla suite `range(4)` sont indépendants.\n"
            }
          ],
          "explanation": "`range(n)` est construit une seule fois, à l'entrée\ndans la boucle. Le nombre de tours est figé à ce\nmoment-là : modifier `n` ensuite n'allonge pas la\nboucle, à la différence d'une liste que l'on\nmodifie pendant son parcours."
        }
      ]
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "while",
        "and",
        "trace"
      ],
      "title": "Deux conditions pour un même while",
      "partie": "while",
      "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 10 and total <= 12:\n    i = i + 1\n    if i % 2 == 0:\n        total = total + i\nprint(i, total)\n```",
      "options": [
        {
          "text": "`6 12`",
          "correct": false,
          "feedback": "Erreur : quand `total` vaut 12, la condition\n`total <= 12` est encore vraie, donc la\nboucle continue : `i` passe à 7 puis à 8, et\n`total` à 20.\n"
        },
        {
          "text": "`10 30`",
          "correct": false,
          "feedback": "Erreur : ce résultat ignore la seconde\ncondition. Le `and` exige les deux : dès que\n`total` dépasse 12, la boucle s'arrête, bien\navant que `i` atteigne 10.\n"
        },
        {
          "text": "`8 12`",
          "correct": false,
          "feedback": "Erreur : la condition n'est testée qu'au\ndébut de chaque tour, jamais au milieu. Le\ntour où `i` vaut 8 s'exécute en entier et\n`total` passe à 20 avant le test suivant.\n"
        },
        {
          "text": "`8 20`",
          "correct": true,
          "feedback": "Bonne réponse : `total` vaut 2, 6 puis 12\naprès les tours où `i` vaut 2, 4 et 6. Comme\n`12 <= 12` est vrai, la boucle continue ; au\ntour où `i` vaut 8, `total` passe à 20 et le\ntest suivant échoue.\n"
        }
      ],
      "explanation": "Une boucle `while` dont la condition relie\ndeux tests par `and` s'arrête dès que l'un\nd'eux devient faux. La condition n'est évaluée\nqu'au début de chaque tour : pour une trace, on\nla vérifie après chaque tour complet.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 10 and total <= 12:\n    i = i + 1\n    if i % 2 == 0:\n        total = total + i\nprint(i, total)\n```",
          "options": [
            {
              "text": "`6 12`",
              "correct": false,
              "feedback": "Erreur : quand `total` vaut 12, la condition\n`total <= 12` est encore vraie, donc la\nboucle continue : `i` passe à 7 puis à 8, et\n`total` à 20.\n"
            },
            {
              "text": "`10 30`",
              "correct": false,
              "feedback": "Erreur : ce résultat ignore la seconde\ncondition. Le `and` exige les deux : dès que\n`total` dépasse 12, la boucle s'arrête, bien\navant que `i` atteigne 10.\n"
            },
            {
              "text": "`8 12`",
              "correct": false,
              "feedback": "Erreur : la condition n'est testée qu'au\ndébut de chaque tour, jamais au milieu. Le\ntour où `i` vaut 8 s'exécute en entier et\n`total` passe à 20 avant le test suivant.\n"
            },
            {
              "text": "`8 20`",
              "correct": true,
              "feedback": "Bonne réponse : `total` vaut 2, 6 puis 12\naprès les tours où `i` vaut 2, 4 et 6. Comme\n`12 <= 12` est vrai, la boucle continue ; au\ntour où `i` vaut 8, `total` passe à 20 et le\ntest suivant échoue.\n"
            }
          ],
          "explanation": "Une boucle `while` dont la condition relie\ndeux tests par `and` s'arrête dès que l'un\nd'eux devient faux. La condition n'est évaluée\nqu'au début de chaque tour : pour une trace, on\nla vérifie après chaque tour complet."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 10 and total <= 6:\n    i = i + 1\n    if i % 2 == 0:\n        total = total + i\nprint(i, total)\n```",
          "options": [
            {
              "text": "`4 6`",
              "correct": false,
              "feedback": "Erreur : quand `total` vaut 6, la condition\n`total <= 6` est encore vraie, donc la\nboucle continue : `i` passe à 5 puis à 6, et\n`total` à 12.\n"
            },
            {
              "text": "`10 30`",
              "correct": false,
              "feedback": "Erreur : ce résultat ignore la seconde\ncondition. Le `and` exige les deux : dès que\n`total` dépasse 6, la boucle s'arrête, bien\navant que `i` atteigne 10.\n"
            },
            {
              "text": "`6 6`",
              "correct": false,
              "feedback": "Erreur : la condition n'est testée qu'au\ndébut de chaque tour, jamais au milieu. Le\ntour où `i` vaut 6 s'exécute en entier et\n`total` passe à 12 avant le test suivant.\n"
            },
            {
              "text": "`6 12`",
              "correct": true,
              "feedback": "Bonne réponse : `total` vaut 2 puis 6\naprès les tours où `i` vaut 2 et 4. Comme\n`6 <= 6` est vrai, la boucle continue ; au\ntour où `i` vaut 6, `total` passe à 12 et le\ntest suivant échoue.\n"
            }
          ],
          "explanation": "Une boucle `while` dont la condition relie\ndeux tests par `and` s'arrête dès que l'un\nd'eux devient faux. La condition n'est évaluée\nqu'au début de chaque tour : pour une trace, on\nla vérifie après chaque tour complet."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 10 and total <= 2:\n    i = i + 1\n    if i % 2 == 0:\n        total = total + i\nprint(i, total)\n```",
          "options": [
            {
              "text": "`2 2`",
              "correct": false,
              "feedback": "Erreur : quand `total` vaut 2, la condition\n`total <= 2` est encore vraie, donc la\nboucle continue : `i` passe à 3 puis à 4, et\n`total` à 6.\n"
            },
            {
              "text": "`10 30`",
              "correct": false,
              "feedback": "Erreur : ce résultat ignore la seconde\ncondition. Le `and` exige les deux : dès que\n`total` dépasse 2, la boucle s'arrête, bien\navant que `i` atteigne 10.\n"
            },
            {
              "text": "`4 2`",
              "correct": false,
              "feedback": "Erreur : la condition n'est testée qu'au\ndébut de chaque tour, jamais au milieu. Le\ntour où `i` vaut 4 s'exécute en entier et\n`total` passe à 6 avant le test suivant.\n"
            },
            {
              "text": "`4 6`",
              "correct": true,
              "feedback": "Bonne réponse : `total` vaut  puis 2\naprès les tours où `i` vaut  et 2. Comme\n`2 <= 2` est vrai, la boucle continue ; au\ntour où `i` vaut 4, `total` passe à 6 et le\ntest suivant échoue.\n"
            }
          ],
          "explanation": "Une boucle `while` dont la condition relie\ndeux tests par `and` s'arrête dès que l'un\nd'eux devient faux. La condition n'est évaluée\nqu'au début de chaque tour : pour une trace, on\nla vérifie après chaque tour complet."
        }
      ]
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "for",
        "elif",
        "compteur"
      ],
      "title": "Compteur avec if et elif",
      "partie": "for",
      "statement": "Qu'affiche ce code ?\n```python\nc = 0\nfor n in range(1, 16):\n    if n % 3 == 0:\n        c = c + 1\n    elif n % 5 == 0:\n        c = c + 2\nprint(c)\n```",
      "options": [
        {
          "text": "9",
          "correct": true,
          "feedback": "Bonne réponse : 5 multiples de 3 (3, 6, 9, 12, 15) ajoutent 1 chacun ; 5 et 10 ajoutent 2\nchacun ; 15, déjà pris par le `if`, n'entre\npas dans le `elif`. Total 5 + 4 = 9.\n"
        },
        {
          "text": "11",
          "correct": false,
          "feedback": "Erreur : 15 est compté deux fois, comme avec\ndeux `if` indépendants. Avec `elif`, la\nseconde condition n'est pas testée quand la\npremière est vraie.\n"
        },
        {
          "text": "5",
          "correct": false,
          "feedback": "Erreur : la branche `elif` est ignorée ; 5 et 10, qui ne sont pas multiples de 3, y entrent\net ajoutent 2 chacun.\n"
        },
        {
          "text": "7",
          "correct": false,
          "feedback": "Erreur : la branche `elif` ajoute 2, pas 1 ;\n5 et 10 apportent donc 4 au total, et non 2.\n"
        }
      ],
      "explanation": "Dans un `if`/`elif`, une seule branche s'exécute\npar tour. Il faut donc trier chaque valeur de\n`range(1, 16)` : multiples de 3 (5 valeurs),\npuis multiples de 5 restants (5 et 10).",
      "variants": [
        {
          "statement": "Qu'affiche ce code ?\n```python\nc = 0\nfor n in range(1, 16):\n    if n % 3 == 0:\n        c = c + 1\n    elif n % 5 == 0:\n        c = c + 2\nprint(c)\n```",
          "options": [
            {
              "text": "9",
              "correct": true,
              "feedback": "Bonne réponse : 5 multiples de 3 (3, 6, 9, 12, 15) ajoutent 1 chacun ; 5 et 10 ajoutent 2\nchacun ; 15, déjà pris par le `if`, n'entre\npas dans le `elif`. Total 5 + 4 = 9.\n"
            },
            {
              "text": "11",
              "correct": false,
              "feedback": "Erreur : 15 est compté deux fois, comme avec\ndeux `if` indépendants. Avec `elif`, la\nseconde condition n'est pas testée quand la\npremière est vraie.\n"
            },
            {
              "text": "5",
              "correct": false,
              "feedback": "Erreur : la branche `elif` est ignorée ; 5 et 10, qui ne sont pas multiples de 3, y entrent\net ajoutent 2 chacun.\n"
            },
            {
              "text": "7",
              "correct": false,
              "feedback": "Erreur : la branche `elif` ajoute 2, pas 1 ;\n5 et 10 apportent donc 4 au total, et non 2.\n"
            }
          ],
          "explanation": "Dans un `if`/`elif`, une seule branche s'exécute\npar tour. Il faut donc trier chaque valeur de\n`range(1, 16)` : multiples de 3 (5 valeurs),\npuis multiples de 5 restants (5 et 10)."
        },
        {
          "statement": "Qu'affiche ce code ?\n```python\nc = 0\nfor n in range(1, 21):\n    if n % 3 == 0:\n        c = c + 1\n    elif n % 5 == 0:\n        c = c + 2\nprint(c)\n```",
          "options": [
            {
              "text": "12",
              "correct": true,
              "feedback": "Bonne réponse : 6 multiples de 3 (3, 6, 9, 12, 15, 18) ajoutent 1 chacun ; 5 et 10 et 20 ajoutent 2\nchacun ; 15, déjà pris par le `if`, n'entre\npas dans le `elif`. Total 6 + 6 = 12.\n"
            },
            {
              "text": "14",
              "correct": false,
              "feedback": "Erreur : 15 est compté deux fois, comme avec\ndeux `if` indépendants. Avec `elif`, la\nseconde condition n'est pas testée quand la\npremière est vraie.\n"
            },
            {
              "text": "6",
              "correct": false,
              "feedback": "Erreur : la branche `elif` est ignorée ; 5 et 10 et 20, qui ne sont pas multiples de 3, y entrent\net ajoutent 2 chacun.\n"
            },
            {
              "text": "9",
              "correct": false,
              "feedback": "Erreur : la branche `elif` ajoute 2, pas 1 ;\n5 et 10 et 20 apportent donc 6 au total, et non 3.\n"
            }
          ],
          "explanation": "Dans un `if`/`elif`, une seule branche s'exécute\npar tour. Il faut donc trier chaque valeur de\n`range(1, 21)` : multiples de 3 (6 valeurs),\npuis multiples de 5 restants (5 et 10 et 20)."
        },
        {
          "statement": "Qu'affiche ce code ?\n```python\nc = 0\nfor n in range(1, 31):\n    if n % 3 == 0:\n        c = c + 1\n    elif n % 5 == 0:\n        c = c + 2\nprint(c)\n```",
          "options": [
            {
              "text": "18",
              "correct": true,
              "feedback": "Bonne réponse : 10 multiples de 3 (3, 6, 9, 12, 15, 18, 21, 24, 27, 30) ajoutent 1 chacun ; 5 et 10 et 20 et 25 ajoutent 2\nchacun ; 15 et 30, déjà pris par le `if`, n'entre\npas dans le `elif`. Total 10 + 8 = 18.\n"
            },
            {
              "text": "22",
              "correct": false,
              "feedback": "Erreur : 15 et 30 est compté deux fois, comme avec\ndeux `if` indépendants. Avec `elif`, la\nseconde condition n'est pas testée quand la\npremière est vraie.\n"
            },
            {
              "text": "10",
              "correct": false,
              "feedback": "Erreur : la branche `elif` est ignorée ; 5 et 10 et 20 et 25, qui ne sont pas multiples de 3, y entrent\net ajoutent 2 chacun.\n"
            },
            {
              "text": "14",
              "correct": false,
              "feedback": "Erreur : la branche `elif` ajoute 2, pas 1 ;\n5 et 10 et 20 et 25 apportent donc 8 au total, et non 4.\n"
            }
          ],
          "explanation": "Dans un `if`/`elif`, une seule branche s'exécute\npar tour. Il faut donc trier chaque valeur de\n`range(1, 31)` : multiples de 3 (10 valeurs),\npuis multiples de 5 restants (5 et 10 et 20 et 25)."
        }
      ]
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "conditions",
      "statement": "Parmi les affirmations suivantes, laquelle est\n**fausse** ?",
      "options": [
        {
          "text": "La condition d'un `while` n'est vérifiée qu'après chaque exécution du corps",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : la condition est\ntestée **avant** chaque tour, y compris le\npremier. Si elle est fausse d'emblée, le corps\nne s'exécute jamais.\n"
        },
        {
          "text": "La boucle `for` sur un `range` fini termine toujours sans intervention",
          "correct": false,
          "feedback": "Vrai : terminaison automatique.\n"
        },
        {
          "text": "Les opérateurs logiques `and`, `or`, `not` permettent de combiner des conditions",
          "correct": false,
          "feedback": "Vrai : ce sont les trois opérateurs\nbooléens.\n"
        },
        {
          "text": "La boucle `while` peut être infinie si la condition ne devient jamais fausse",
          "correct": false,
          "feedback": "Vrai : risque classique de bug.\n"
        }
      ],
      "explanation": "Le `while` teste d'abord, exécute ensuite : une\nboucle `while` peut ne faire aucun tour, alors\nqu'une boucle `for` sur un `range` fini termine\ntoujours."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "while",
        "saisie",
        "initialisation"
      ],
      "title": "Boucle de saisie validée",
      "partie": "while",
      "statement": "On veut que l'utilisateur saisisse un entier\nstrictement positif et redemander tant que la\nréponse n'est pas correcte. Quel code convient le\nmieux ?",
      "options": [
        {
          "text": "```python\nfor i in range(10):\n    n = int(input(\"n ? \"))\n```\n",
          "correct": false,
          "feedback": "Erreur : ce code demande **dix fois** la valeur,\nquelle que soit la qualité des saisies, et\nconserve la dernière. Il ne s'arrête pas dès\nqu'une bonne valeur est donnée.\n"
        },
        {
          "text": "```python\nn = 0\nwhile n <= 0:\n    n = int(input(\"n ? \"))\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : `n` est initialisé à une valeur\ninvalide (0 n'est pas strictement positif), ce\nqui garantit un premier passage ; la boucle\nredemande ensuite tant que la saisie reste\nnégative ou nulle.\n"
        },
        {
          "text": "```python\nn = -1\nwhile n > 0:\n    n = int(input(\"n ? \"))\n```\n",
          "correct": false,
          "feedback": "Erreur : la condition initiale `n > 0` est\nfausse (puisque `n = -1`), donc la boucle ne\ns'exécute **jamais**. Aucune saisie n'est\nréellement effectuée.\n"
        },
        {
          "text": "```python\nn = int(input(\"n ? \"))\nif n <= 0:\n    n = int(input(\"n ? \"))\n```\n",
          "correct": false,
          "feedback": "Erreur : ce code ne redemande qu'**une seule fois** la valeur. Si l'utilisateur entre encore\nune valeur invalide à la deuxième saisie, le\nprogramme continue avec cette valeur erronée.\n"
        }
      ],
      "explanation": "Schéma de validation de saisie : initialiser la\nvariable avec une valeur qui ne convient pas, pour\nforcer un premier tour, puis répéter tant que la\nvaleur ne convient pas. La condition du `while`\ndit exactement « tant que la saisie est mauvaise »."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "if",
        "elif",
        "structure"
      ],
      "title": "if/elif vs plusieurs if",
      "partie": "conditions",
      "statement": "Considérons les deux extraits suivants. En quoi\ndiffèrent-ils ?\n\n```python\n# Extrait A\nif x > 0:\n    y = 1\nelif x > 10:\n    y = 2\n\n# Extrait B\nif x > 0:\n    y = 1\nif x > 10:\n    y = 2\n```",
      "options": [
        {
          "text": "Ils produisent toujours le même résultat, quelle que soit la valeur de `x`",
          "correct": false,
          "feedback": "Erreur : ils diffèrent justement quand `x` est\nstrictement supérieur à $10$. L'extrait A donne\n$y = 1$ (la branche `elif` n'est pas testée\nparce que la première est vraie), l'extrait B\ndonne $y = 2$ (les deux conditions s'évaluent).\n"
        },
        {
          "text": "A n'exécute qu'une seule branche ; B peut exécuter les deux",
          "correct": true,
          "feedback": "Bonne réponse : `elif` indique que l'on\nn'évalue cette branche que si la précédente\nétait fausse. Avec deux `if` séparés, les deux\nconditions sont toujours vérifiées\nindépendamment, ce qui peut conduire à des\nréaffectations indésirables.\n"
        },
        {
          "text": "L'extrait A produit une erreur car `elif` ne fonctionne pas seul",
          "correct": false,
          "feedback": "Erreur : `elif` est un mot-clé valide en Python\ntant qu'il suit un `if`. Sa présence ne\nprovoque aucune erreur de syntaxe.\n"
        },
        {
          "text": "L'extrait B est plus rapide, car il évite le mot-clé `elif`",
          "correct": false,
          "feedback": "Erreur : la différence ne porte pas sur la\nvitesse mais sur la **logique**. L'extrait A\npeut au contraire être plus rapide, car il\nn'évalue pas la deuxième condition lorsque la\npremière est vraie.\n"
        }
      ],
      "explanation": "Règle pratique : utiliser `elif` lorsque les\nbranches s'excluent mutuellement (un seul cas est\nvrai à la fois). Utiliser des `if` séparés\nlorsqu'on veut **vraiment** que plusieurs\nconditions puissent déclencher chacune leur action."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "parcours-parallele",
        "indice"
      ],
      "title": "Parcours parallèle de deux listes",
      "partie": "for",
      "statement": "Deux listes `noms` et `notes` de même longueur\nrangent au même rang le nom d'un élève et sa note.\nQuel code affiche chaque nom suivi de sa note ?",
      "options": [
        {
          "text": "```python\nprint(noms + notes)\n```\n",
          "correct": false,
          "feedback": "Erreur : la concaténation `noms + notes` produit\nune seule liste juxtaposant les deux. Il n'y a\npas d'appariement, et tout est affiché en bloc.\n"
        },
        {
          "text": "```python\nfor i in range(len(noms)):\n    print(noms[i], notes[i])\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : l'indice `i` parcourt les\npositions de 0 à `len(noms) - 1` et sert à lire\nau même rang le nom et la note qui lui\ncorrespond.\n"
        },
        {
          "text": "```python\nfor nom in noms:\n    for note in notes:\n        print(nom, note)\n```\n",
          "correct": false,
          "feedback": "Erreur : ces boucles imbriquées affichent\n**toutes les paires** (nom, note), pas\nuniquement celles de même rang. Pour deux\nlistes de longueur $n$, on obtient $n^2$\nlignes au lieu de $n$.\n"
        },
        {
          "text": "```python\nfor nom in noms:\n    print(nom, notes[nom])\n```\n",
          "correct": false,
          "feedback": "Erreur : `nom` est une chaîne, pas un indice.\n`notes[nom]` lève `TypeError`, car une liste ne\ns'indexe qu'avec un entier. Pour retrouver le\nrang, il faut parcourir les indices.\n"
        }
      ],
      "explanation": "Le parcours par indice `for i in range(len(...))`\nest le moyen d'accéder au même rang dans deux\nlistes. L'hypothèse « de même longueur » garantit\nque `notes[i]` existe pour tout indice valide de\n`noms`."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "while",
        "racine-entiere"
      ],
      "title": "Calcul d'une racine entière",
      "partie": "while",
      "statement": "On exécute le code suivant :\n```python\nn = 30\nr = 0\nwhile r * r <= n:\n    r = r + 1\n```\nQuelle est la valeur de `r` à la sortie de la boucle ?",
      "options": [
        {
          "text": "$7$",
          "correct": false,
          "feedback": "Erreur : la boucle s'arrête dès que la condition\ndevient fausse, sans incrémenter une fois de\nplus. Pour $r = 6$, on a déjà $36 > 30$ : la\nboucle est sortie avec $r = 6$.\n"
        },
        {
          "text": "$6$",
          "correct": true,
          "feedback": "Bonne réponse : la boucle s'arrête dès que\n$r \\times r > n$. Pour $r = 5$, $25 \\leq 30$ :\non incrémente. Pour $r = 6$, $36 > 30$ : on\nsort. La valeur finale est donc $r = 6$.\n"
        },
        {
          "text": "$30$",
          "correct": false,
          "feedback": "Erreur : confusion entre la valeur cible $n$ et\nla variable `r`. La boucle calcule la **racine carrée entière** par majorant, pas la valeur de\n$n$.\n"
        },
        {
          "text": "$5$",
          "correct": false,
          "feedback": "Erreur : pour $r = 5$, on a $r \\times r = 25\n\\leq 30$, donc la condition est encore vraie.\nLa boucle fait au moins une itération de plus.\n"
        }
      ],
      "explanation": "Cet algorithme calcule le plus petit entier $r$\ntel que $r^2 > n$. À la sortie, $r - 1$ est la\npartie entière de $\\sqrt{n}$. Pour $n = 30$, on\nobtient $r = 6$, et $r - 1 = 5$, conforme à\n$\\lfloor\\sqrt{30}\\rfloor = 5$.",
      "variants": [
        {
          "statement": "On exécute le code suivant :\n```python\nn = 30\nr = 0\nwhile r * r <= n:\n    r = r + 1\n```\nQuelle est la valeur de `r` à la sortie de la boucle ?",
          "options": [
            {
              "text": "$7$",
              "correct": false,
              "feedback": "Erreur : la boucle s'arrête dès que la condition\ndevient fausse, sans incrémenter une fois de\nplus. Pour $r = 6$, on a déjà $36 > 30$ : la\nboucle est sortie avec $r = 6$.\n"
            },
            {
              "text": "$6$",
              "correct": true,
              "feedback": "Bonne réponse : la boucle s'arrête dès que\n$r \\times r > n$. Pour $r = 5$, $25 \\leq 30$ :\non incrémente. Pour $r = 6$, $36 > 30$ : on\nsort. La valeur finale est donc $r = 6$.\n"
            },
            {
              "text": "$30$",
              "correct": false,
              "feedback": "Erreur : confusion entre la valeur cible $n$ et\nla variable `r`. La boucle calcule la **racine carrée entière** par majorant, pas la valeur de\n$n$.\n"
            },
            {
              "text": "$5$",
              "correct": false,
              "feedback": "Erreur : pour $r = 5$, on a $r \\times r = 25\n\\leq 30$, donc la condition est encore vraie.\nLa boucle fait au moins une itération de plus.\n"
            }
          ],
          "explanation": "Cet algorithme calcule le plus petit entier $r$\ntel que $r^2 > n$. À la sortie, $r - 1$ est la\npartie entière de $\\sqrt{n}$. Pour $n = 30$, on\nobtient $r = 6$, et $r - 1 = 5$, conforme à\n$\\lfloor\\sqrt{30}\\rfloor = 5$."
        },
        {
          "statement": "On exécute le code suivant :\n```python\nn = 50\nr = 0\nwhile r * r <= n:\n    r = r + 1\n```\nQuelle est la valeur de `r` à la sortie de la boucle ?",
          "options": [
            {
              "text": "$9$",
              "correct": false,
              "feedback": "Erreur : la boucle s'arrête dès que la condition\ndevient fausse, sans incrémenter une fois de\nplus. Pour $r = 8$, on a déjà $64 > 50$ : la\nboucle est sortie avec $r = 8$.\n"
            },
            {
              "text": "$8$",
              "correct": true,
              "feedback": "Bonne réponse : la boucle s'arrête dès que\n$r \\times r > n$. Pour $r = 7$, $49 \\leq 50$ :\non incrémente. Pour $r = 8$, $64 > 50$ : on\nsort. La valeur finale est donc $r = 8$.\n"
            },
            {
              "text": "$50$",
              "correct": false,
              "feedback": "Erreur : confusion entre la valeur cible $n$ et\nla variable `r`. La boucle calcule la **racine carrée entière** par majorant, pas la valeur de\n$n$.\n"
            },
            {
              "text": "$7$",
              "correct": false,
              "feedback": "Erreur : pour $r = 7$, on a $r \\times r = 49\n\\leq 50$, donc la condition est encore vraie.\nLa boucle fait au moins une itération de plus.\n"
            }
          ],
          "explanation": "Cet algorithme calcule le plus petit entier $r$\ntel que $r^2 > n$. À la sortie, $r - 1$ est la\npartie entière de $\\sqrt{n}$. Pour $n = 50$, on\nobtient $r = 8$, et $r - 1 = 7$, conforme à\n$\\lfloor\\sqrt{50}\\rfloor = 7$."
        },
        {
          "statement": "On exécute le code suivant :\n```python\nn = 15\nr = 0\nwhile r * r <= n:\n    r = r + 1\n```\nQuelle est la valeur de `r` à la sortie de la boucle ?",
          "options": [
            {
              "text": "$5$",
              "correct": false,
              "feedback": "Erreur : la boucle s'arrête dès que la condition\ndevient fausse, sans incrémenter une fois de\nplus. Pour $r = 4$, on a déjà $16 > 15$ : la\nboucle est sortie avec $r = 4$.\n"
            },
            {
              "text": "$4$",
              "correct": true,
              "feedback": "Bonne réponse : la boucle s'arrête dès que\n$r \\times r > n$. Pour $r = 3$, $9 \\leq 15$ :\non incrémente. Pour $r = 4$, $16 > 15$ : on\nsort. La valeur finale est donc $r = 4$.\n"
            },
            {
              "text": "$15$",
              "correct": false,
              "feedback": "Erreur : confusion entre la valeur cible $n$ et\nla variable `r`. La boucle calcule la **racine carrée entière** par majorant, pas la valeur de\n$n$.\n"
            },
            {
              "text": "$3$",
              "correct": false,
              "feedback": "Erreur : pour $r = 3$, on a $r \\times r = 9\n\\leq 15$, donc la condition est encore vraie.\nLa boucle fait au moins une itération de plus.\n"
            }
          ],
          "explanation": "Cet algorithme calcule le plus petit entier $r$\ntel que $r^2 > n$. À la sortie, $r - 1$ est la\npartie entière de $\\sqrt{n}$. Pour $n = 15$, on\nobtient $r = 4$, et $r - 1 = 3$, conforme à\n$\\lfloor\\sqrt{15}\\rfloor = 3$."
        }
      ]
    },
    {
      "id": "q30",
      "difficulty": 1,
      "skills": [
        "if-elif-else",
        "branches"
      ],
      "title": "Combien de branches s'exécutent",
      "partie": "conditions",
      "statement": "Dans une structure `if ... elif ... elif ... else`, combien de\nbranches sont exécutées lors d'un passage ?",
      "options": [
        {
          "text": "Exactement une, la première vraie ou le `else`",
          "correct": true,
          "feedback": "Bonne réponse : les conditions sont testées dans l'ordre et la\npremière vraie l'emporte ; si aucune ne l'est, le `else`\ns'exécute.\n"
        },
        {
          "text": "Toutes celles dont la condition est vraie, dans l'ordre",
          "correct": false,
          "feedback": "Erreur : ce serait le comportement de plusieurs `if`\nindépendants, pas d'une chaîne `elif`.\n"
        },
        {
          "text": "Au moins deux, le `else` s'exécutant toujours",
          "correct": false,
          "feedback": "Erreur : le `else` n'est atteint que si toutes les conditions\nsont fausses.\n"
        },
        {
          "text": "Aucune, si plusieurs conditions sont vraies à la fois",
          "correct": false,
          "feedback": "Erreur : plusieurs conditions vraies ne bloquent rien ; la\npremière l'emporte.\n"
        }
      ],
      "explanation": "`elif` enchaîne des cas exclusifs : un seul bloc s'exécute, ce qui\ndistingue cette structure d'une suite de `if`."
    },
    {
      "id": "q31",
      "difficulty": 2,
      "skills": [
        "conditions-imbriquees",
        "trace"
      ],
      "title": "Trois issues pour un nombre",
      "partie": "conditions",
      "statement": "Qu'affiche ce programme ?\n```python\nx = 5\nif x > 0:\n    if x < 10:\n        print(\"A\")\n    else:\n        print(\"B\")\nelse:\n    print(\"C\")\n```",
      "options": [
        {
          "text": "`A`",
          "correct": true,
          "feedback": "Bonne réponse : le premier test sépare les positifs des autres,\nle second, imbriqué, sépare les positifs inférieurs à $10$ des\nautres. Pour $5$, c'est `A`.\n"
        },
        {
          "text": "`B`",
          "correct": false,
          "feedback": "Erreur : suis les tests dans l'ordre, en respectant\nl'indentation de chaque `else`.\n"
        },
        {
          "text": "`C`",
          "correct": false,
          "feedback": "Erreur : chaque `else` est rattaché au `if` de même niveau.\n"
        },
        {
          "text": "Rien, aucune branche ne convenant à $5$",
          "correct": false,
          "feedback": "Erreur : tout nombre tombe dans une des trois branches.\n"
        }
      ],
      "explanation": "Deux `if` imbriqués découpent les cas en arbre ; lire\nl'indentation dit quel `else` répond à quel `if`.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nx = 5\nif x > 0:\n    if x < 10:\n        print(\"A\")\n    else:\n        print(\"B\")\nelse:\n    print(\"C\")\n```",
          "options": [
            {
              "text": "`A`",
              "correct": true,
              "feedback": "Bonne réponse : le premier test sépare les positifs des autres,\nle second, imbriqué, sépare les positifs inférieurs à $10$ des\nautres. Pour $5$, c'est `A`.\n"
            },
            {
              "text": "`B`",
              "correct": false,
              "feedback": "Erreur : suis les tests dans l'ordre, en respectant\nl'indentation de chaque `else`.\n"
            },
            {
              "text": "`C`",
              "correct": false,
              "feedback": "Erreur : chaque `else` est rattaché au `if` de même niveau.\n"
            },
            {
              "text": "Rien, aucune branche ne convenant à $5$",
              "correct": false,
              "feedback": "Erreur : tout nombre tombe dans une des trois branches.\n"
            }
          ],
          "explanation": "Deux `if` imbriqués découpent les cas en arbre ; lire\nl'indentation dit quel `else` répond à quel `if`."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nx = 15\nif x > 0:\n    if x < 10:\n        print(\"A\")\n    else:\n        print(\"B\")\nelse:\n    print(\"C\")\n```",
          "options": [
            {
              "text": "`B`",
              "correct": true,
              "feedback": "Bonne réponse : le premier test sépare les positifs des autres,\nle second, imbriqué, sépare les positifs inférieurs à $10$ des\nautres. Pour $15$, c'est `B`.\n"
            },
            {
              "text": "`A`",
              "correct": false,
              "feedback": "Erreur : suis les tests dans l'ordre, en respectant\nl'indentation de chaque `else`.\n"
            },
            {
              "text": "`C`",
              "correct": false,
              "feedback": "Erreur : chaque `else` est rattaché au `if` de même niveau.\n"
            },
            {
              "text": "Rien, aucune branche ne convenant à $15$",
              "correct": false,
              "feedback": "Erreur : tout nombre tombe dans une des trois branches.\n"
            }
          ],
          "explanation": "Deux `if` imbriqués découpent les cas en arbre ; lire\nl'indentation dit quel `else` répond à quel `if`."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nx = -3\nif x > 0:\n    if x < 10:\n        print(\"A\")\n    else:\n        print(\"B\")\nelse:\n    print(\"C\")\n```",
          "options": [
            {
              "text": "`C`",
              "correct": true,
              "feedback": "Bonne réponse : le premier test sépare les positifs des autres,\nle second, imbriqué, sépare les positifs inférieurs à $10$ des\nautres. Pour $-3$, c'est `C`.\n"
            },
            {
              "text": "`A`",
              "correct": false,
              "feedback": "Erreur : suis les tests dans l'ordre, en respectant\nl'indentation de chaque `else`.\n"
            },
            {
              "text": "`B`",
              "correct": false,
              "feedback": "Erreur : chaque `else` est rattaché au `if` de même niveau.\n"
            },
            {
              "text": "Rien, aucune branche ne convenant à $-3$",
              "correct": false,
              "feedback": "Erreur : tout nombre tombe dans une des trois branches.\n"
            }
          ],
          "explanation": "Deux `if` imbriqués découpent les cas en arbre ; lire\nl'indentation dit quel `else` répond à quel `if`."
        }
      ]
    },
    {
      "id": "q32",
      "difficulty": 3,
      "skills": [
        "comparaison-chainee",
        "equivalence"
      ],
      "title": "Une double inégalité en Python",
      "partie": "conditions",
      "statement": "Python accepte l'écriture `0 < x < 10`. À quelle condition est-elle\néquivalente ?",
      "options": [
        {
          "text": "`0 < x and x < 10`",
          "correct": true,
          "feedback": "Bonne réponse : une comparaison chaînée vérifie chaque\ninégalité, reliées par un `and` implicite.\n"
        },
        {
          "text": "`0 < x or x < 10`",
          "correct": false,
          "feedback": "Erreur : avec `or`, tout nombre conviendrait.\n"
        },
        {
          "text": "`(0 < x) < 10`",
          "correct": false,
          "feedback": "Erreur : cette lecture comparerait un booléen à $10$, ce qui est\ntoujours vrai ; Python ne lit pas ainsi.\n"
        },
        {
          "text": "`0 < x < 10` n'est pas une expression valide",
          "correct": false,
          "feedback": "Erreur : l'écriture est valide et très lisible pour un\nintervalle.\n"
        }
      ],
      "explanation": "Les comparaisons chaînées traduisent directement les doubles\ninégalités mathématiques, avec la sémantique du `and`."
    },
    {
      "id": "q33",
      "difficulty": 2,
      "skills": [
        "while",
        "division-entiere",
        "tours"
      ],
      "title": "Diviser par deux jusqu'à un",
      "partie": "while",
      "statement": "Combien de tours effectue cette boucle ?\n```python\nn = 20\nwhile n > 1:\n    n = n // 2\n```",
      "options": [
        {
          "text": "`4`",
          "correct": true,
          "feedback": "Bonne réponse : `n` prend les valeurs 20, 10, 5, 2, 1 ; chaque division\nentière est un tour, et la boucle s'arrête dès que `n` vaut $1$.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : le tour qui fait passer `n` à $1$ compte aussi.\n"
        },
        {
          "text": "`5`",
          "correct": false,
          "feedback": "Erreur : dès que `n` vaut $1$, la condition `n > 1` est fausse\net aucun tour supplémentaire n'a lieu.\n"
        },
        {
          "text": "`20`",
          "correct": false,
          "feedback": "Erreur : `n` est divisé par deux à chaque tour, pas diminué de\nun.\n"
        }
      ],
      "explanation": "Une boucle qui divise par deux fait environ $\\log_2 n$ tours : c'est\nle schéma de la dichotomie.",
      "variants": [
        {
          "statement": "Combien de tours effectue cette boucle ?\n```python\nn = 20\nwhile n > 1:\n    n = n // 2\n```",
          "options": [
            {
              "text": "`4`",
              "correct": true,
              "feedback": "Bonne réponse : `n` prend les valeurs 20, 10, 5, 2, 1 ; chaque division\nentière est un tour, et la boucle s'arrête dès que `n` vaut $1$.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : le tour qui fait passer `n` à $1$ compte aussi.\n"
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : dès que `n` vaut $1$, la condition `n > 1` est fausse\net aucun tour supplémentaire n'a lieu.\n"
            },
            {
              "text": "`20`",
              "correct": false,
              "feedback": "Erreur : `n` est divisé par deux à chaque tour, pas diminué de\nun.\n"
            }
          ],
          "explanation": "Une boucle qui divise par deux fait environ $\\log_2 n$ tours : c'est\nle schéma de la dichotomie."
        },
        {
          "statement": "Combien de tours effectue cette boucle ?\n```python\nn = 100\nwhile n > 1:\n    n = n // 2\n```",
          "options": [
            {
              "text": "`6`",
              "correct": true,
              "feedback": "Bonne réponse : `n` prend les valeurs 100, 50, 25, 12, 6, 3, 1 ; chaque division\nentière est un tour, et la boucle s'arrête dès que `n` vaut $1$.\n"
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : le tour qui fait passer `n` à $1$ compte aussi.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : dès que `n` vaut $1$, la condition `n > 1` est fausse\net aucun tour supplémentaire n'a lieu.\n"
            },
            {
              "text": "`100`",
              "correct": false,
              "feedback": "Erreur : `n` est divisé par deux à chaque tour, pas diminué de\nun.\n"
            }
          ],
          "explanation": "Une boucle qui divise par deux fait environ $\\log_2 n$ tours : c'est\nle schéma de la dichotomie."
        },
        {
          "statement": "Combien de tours effectue cette boucle ?\n```python\nn = 7\nwhile n > 1:\n    n = n // 2\n```",
          "options": [
            {
              "text": "`2`",
              "correct": true,
              "feedback": "Bonne réponse : `n` prend les valeurs 7, 3, 1 ; chaque division\nentière est un tour, et la boucle s'arrête dès que `n` vaut $1$.\n"
            },
            {
              "text": "`1`",
              "correct": false,
              "feedback": "Erreur : le tour qui fait passer `n` à $1$ compte aussi.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : dès que `n` vaut $1$, la condition `n > 1` est fausse\net aucun tour supplémentaire n'a lieu.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : `n` est divisé par deux à chaque tour, pas diminué de\nun.\n"
            }
          ],
          "explanation": "Une boucle qui divise par deux fait environ $\\log_2 n$ tours : c'est\nle schéma de la dichotomie."
        }
      ]
    },
    {
      "id": "q34",
      "difficulty": 3,
      "skills": [
        "while-imbriques",
        "tours",
        "calcul"
      ],
      "title": "Deux boucles while imbriquées",
      "partie": "while",
      "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 4:\n    j = 0\n    while j < i:\n        total = total + 1\n        j = j + 1\n    i = i + 1\nprint(total)\n```",
      "options": [
        {
          "text": "`6`",
          "correct": true,
          "feedback": "Bonne réponse : la boucle intérieure fait `i` tours à chaque\npassage, soit $0$, puis $1$, jusqu'à $4 - 1$. Leur somme vaut\n6.\n"
        },
        {
          "text": "`16`",
          "correct": false,
          "feedback": "Erreur : la boucle intérieure ne fait pas toujours $4$ tours,\nelle en fait `i`.\n"
        },
        {
          "text": "`4`",
          "correct": false,
          "feedback": "Erreur : `total` est incrémenté dans la boucle intérieure, bien\nplus de $4$ fois au total.\n"
        },
        {
          "text": "`0`",
          "correct": false,
          "feedback": "Erreur : dès que `i` vaut $1$, la boucle intérieure tourne au\nmoins une fois.\n"
        }
      ],
      "explanation": "Deux boucles imbriquées se comptent en additionnant les tours de la\nboucle intérieure pour chaque valeur de la boucle extérieure.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 4:\n    j = 0\n    while j < i:\n        total = total + 1\n        j = j + 1\n    i = i + 1\nprint(total)\n```",
          "options": [
            {
              "text": "`6`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle intérieure fait `i` tours à chaque\npassage, soit $0$, puis $1$, jusqu'à $4 - 1$. Leur somme vaut\n6.\n"
            },
            {
              "text": "`16`",
              "correct": false,
              "feedback": "Erreur : la boucle intérieure ne fait pas toujours $4$ tours,\nelle en fait `i`.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : `total` est incrémenté dans la boucle intérieure, bien\nplus de $4$ fois au total.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : dès que `i` vaut $1$, la boucle intérieure tourne au\nmoins une fois.\n"
            }
          ],
          "explanation": "Deux boucles imbriquées se comptent en additionnant les tours de la\nboucle intérieure pour chaque valeur de la boucle extérieure."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 5:\n    j = 0\n    while j < i:\n        total = total + 1\n        j = j + 1\n    i = i + 1\nprint(total)\n```",
          "options": [
            {
              "text": "`10`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle intérieure fait `i` tours à chaque\npassage, soit $0$, puis $1$, jusqu'à $5 - 1$. Leur somme vaut\n10.\n"
            },
            {
              "text": "`25`",
              "correct": false,
              "feedback": "Erreur : la boucle intérieure ne fait pas toujours $5$ tours,\nelle en fait `i`.\n"
            },
            {
              "text": "`5`",
              "correct": false,
              "feedback": "Erreur : `total` est incrémenté dans la boucle intérieure, bien\nplus de $5$ fois au total.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : dès que `i` vaut $1$, la boucle intérieure tourne au\nmoins une fois.\n"
            }
          ],
          "explanation": "Deux boucles imbriquées se comptent en additionnant les tours de la\nboucle intérieure pour chaque valeur de la boucle extérieure."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ni = 0\ntotal = 0\nwhile i < 6:\n    j = 0\n    while j < i:\n        total = total + 1\n        j = j + 1\n    i = i + 1\nprint(total)\n```",
          "options": [
            {
              "text": "`15`",
              "correct": true,
              "feedback": "Bonne réponse : la boucle intérieure fait `i` tours à chaque\npassage, soit $0$, puis $1$, jusqu'à $6 - 1$. Leur somme vaut\n15.\n"
            },
            {
              "text": "`36`",
              "correct": false,
              "feedback": "Erreur : la boucle intérieure ne fait pas toujours $6$ tours,\nelle en fait `i`.\n"
            },
            {
              "text": "`6`",
              "correct": false,
              "feedback": "Erreur : `total` est incrémenté dans la boucle intérieure, bien\nplus de $6$ fois au total.\n"
            },
            {
              "text": "`0`",
              "correct": false,
              "feedback": "Erreur : dès que `i` vaut $1$, la boucle intérieure tourne au\nmoins une fois.\n"
            }
          ],
          "explanation": "Deux boucles imbriquées se comptent en additionnant les tours de la\nboucle intérieure pour chaque valeur de la boucle extérieure."
        }
      ]
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "while",
        "multiple",
        "trace"
      ],
      "title": "Le premier multiple qui dépasse",
      "partie": "while",
      "statement": "Qu'affiche ce programme ?\n```python\nk = 1\nwhile k * 7 < 50:\n    k = k + 1\nprint(k * 7)\n```",
      "options": [
        {
          "text": "`56`",
          "correct": true,
          "feedback": "Bonne réponse : `k` augmente tant que `k * 7` reste inférieur à\n$50$ ; à la sortie, `k * 7` est le premier multiple de $7$ qui\natteint ou dépasse $50$.\n"
        },
        {
          "text": "`49`",
          "correct": false,
          "feedback": "Erreur : $49$ est encore inférieur à $50$, la boucle fait\nun tour de plus.\n"
        },
        {
          "text": "`50`",
          "correct": false,
          "feedback": "Erreur : $50$ n'est pas un multiple de $7$, `k * 7` ne peut pas\nvaloir $50$.\n"
        },
        {
          "text": "`7`",
          "correct": false,
          "feedback": "Erreur : `7` est la valeur initiale de `k * 7`, et la condition\nest vraie au départ.\n"
        }
      ],
      "explanation": "La valeur affichée est celle qui rend la condition fausse pour la\npremière fois : un `while` s'arrête juste après avoir dépassé le\nseuil.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\nk = 1\nwhile k * 7 < 50:\n    k = k + 1\nprint(k * 7)\n```",
          "options": [
            {
              "text": "`56`",
              "correct": true,
              "feedback": "Bonne réponse : `k` augmente tant que `k * 7` reste inférieur à\n$50$ ; à la sortie, `k * 7` est le premier multiple de $7$ qui\natteint ou dépasse $50$.\n"
            },
            {
              "text": "`49`",
              "correct": false,
              "feedback": "Erreur : $49$ est encore inférieur à $50$, la boucle fait\nun tour de plus.\n"
            },
            {
              "text": "`50`",
              "correct": false,
              "feedback": "Erreur : $50$ n'est pas un multiple de $7$, `k * 7` ne peut pas\nvaloir $50$.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : `7` est la valeur initiale de `k * 7`, et la condition\nest vraie au départ.\n"
            }
          ],
          "explanation": "La valeur affichée est celle qui rend la condition fausse pour la\npremière fois : un `while` s'arrête juste après avoir dépassé le\nseuil."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nk = 1\nwhile k * 7 < 30:\n    k = k + 1\nprint(k * 7)\n```",
          "options": [
            {
              "text": "`35`",
              "correct": true,
              "feedback": "Bonne réponse : `k` augmente tant que `k * 7` reste inférieur à\n$30$ ; à la sortie, `k * 7` est le premier multiple de $7$ qui\natteint ou dépasse $30$.\n"
            },
            {
              "text": "`28`",
              "correct": false,
              "feedback": "Erreur : $28$ est encore inférieur à $30$, la boucle fait\nun tour de plus.\n"
            },
            {
              "text": "`30`",
              "correct": false,
              "feedback": "Erreur : $30$ n'est pas un multiple de $7$, `k * 7` ne peut pas\nvaloir $30$.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : `7` est la valeur initiale de `k * 7`, et la condition\nest vraie au départ.\n"
            }
          ],
          "explanation": "La valeur affichée est celle qui rend la condition fausse pour la\npremière fois : un `while` s'arrête juste après avoir dépassé le\nseuil."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\nk = 1\nwhile k * 7 < 100:\n    k = k + 1\nprint(k * 7)\n```",
          "options": [
            {
              "text": "`105`",
              "correct": true,
              "feedback": "Bonne réponse : `k` augmente tant que `k * 7` reste inférieur à\n$100$ ; à la sortie, `k * 7` est le premier multiple de $7$ qui\natteint ou dépasse $100$.\n"
            },
            {
              "text": "`98`",
              "correct": false,
              "feedback": "Erreur : $98$ est encore inférieur à $100$, la boucle fait\nun tour de plus.\n"
            },
            {
              "text": "`100`",
              "correct": false,
              "feedback": "Erreur : $100$ n'est pas un multiple de $7$, `k * 7` ne peut pas\nvaloir $100$.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : `7` est la valeur initiale de `k * 7`, et la condition\nest vraie au départ.\n"
            }
          ],
          "explanation": "La valeur affichée est celle qui rend la condition fausse pour la\npremière fois : un `while` s'arrête juste après avoir dépassé le\nseuil."
        }
      ]
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "for",
        "range",
        "pas-negatif"
      ],
      "title": "Un range qui descend par pas de trois",
      "partie": "for",
      "statement": "Qu'affiche ce programme, les valeurs étant lues ligne par ligne ?\n```python\nfor i in range(10, 0, -3):\n    print(i)\n```",
      "options": [
        {
          "text": "`10 7 4 1`",
          "correct": true,
          "feedback": "Bonne réponse : `i` part de $10$ et diminue de $3$ à chaque\ntour, tant qu'il reste strictement supérieur à $0$, borne\nexclue.\n"
        },
        {
          "text": "`10 7 4 1 -2`",
          "correct": false,
          "feedback": "Erreur : la borne $0$ est exclue, et aucune valeur négative n'est\nproduite.\n"
        },
        {
          "text": "`10 7 4`",
          "correct": false,
          "feedback": "Erreur : la dernière valeur positive est encore produite.\n"
        },
        {
          "text": "`1 4 7 10`",
          "correct": false,
          "feedback": "Erreur : un pas négatif parcourt les valeurs en descendant.\n"
        }
      ],
      "explanation": "`range(debut, fin, pas)` accepte un pas négatif : les valeurs\ndescendent de `debut` vers `fin`, exclue.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme, les valeurs étant lues ligne par ligne ?\n```python\nfor i in range(10, 0, -3):\n    print(i)\n```",
          "options": [
            {
              "text": "`10 7 4 1`",
              "correct": true,
              "feedback": "Bonne réponse : `i` part de $10$ et diminue de $3$ à chaque\ntour, tant qu'il reste strictement supérieur à $0$, borne\nexclue.\n"
            },
            {
              "text": "`10 7 4 1 -2`",
              "correct": false,
              "feedback": "Erreur : la borne $0$ est exclue, et aucune valeur négative n'est\nproduite.\n"
            },
            {
              "text": "`10 7 4`",
              "correct": false,
              "feedback": "Erreur : la dernière valeur positive est encore produite.\n"
            },
            {
              "text": "`1 4 7 10`",
              "correct": false,
              "feedback": "Erreur : un pas négatif parcourt les valeurs en descendant.\n"
            }
          ],
          "explanation": "`range(debut, fin, pas)` accepte un pas négatif : les valeurs\ndescendent de `debut` vers `fin`, exclue."
        },
        {
          "statement": "Qu'affiche ce programme, les valeurs étant lues ligne par ligne ?\n```python\nfor i in range(12, 0, -5):\n    print(i)\n```",
          "options": [
            {
              "text": "`12 7 2`",
              "correct": true,
              "feedback": "Bonne réponse : `i` part de $12$ et diminue de $5$ à chaque\ntour, tant qu'il reste strictement supérieur à $0$, borne\nexclue.\n"
            },
            {
              "text": "`12 7 2 -3`",
              "correct": false,
              "feedback": "Erreur : la borne $0$ est exclue, et aucune valeur négative n'est\nproduite.\n"
            },
            {
              "text": "`12 7`",
              "correct": false,
              "feedback": "Erreur : la dernière valeur positive est encore produite.\n"
            },
            {
              "text": "`2 7 12`",
              "correct": false,
              "feedback": "Erreur : un pas négatif parcourt les valeurs en descendant.\n"
            }
          ],
          "explanation": "`range(debut, fin, pas)` accepte un pas négatif : les valeurs\ndescendent de `debut` vers `fin`, exclue."
        },
        {
          "statement": "Qu'affiche ce programme, les valeurs étant lues ligne par ligne ?\n```python\nfor i in range(9, 0, -4):\n    print(i)\n```",
          "options": [
            {
              "text": "`9 5 1`",
              "correct": true,
              "feedback": "Bonne réponse : `i` part de $9$ et diminue de $4$ à chaque\ntour, tant qu'il reste strictement supérieur à $0$, borne\nexclue.\n"
            },
            {
              "text": "`9 5 1 -3`",
              "correct": false,
              "feedback": "Erreur : la borne $0$ est exclue, et aucune valeur négative n'est\nproduite.\n"
            },
            {
              "text": "`9 5`",
              "correct": false,
              "feedback": "Erreur : la dernière valeur positive est encore produite.\n"
            },
            {
              "text": "`1 5 9`",
              "correct": false,
              "feedback": "Erreur : un pas négatif parcourt les valeurs en descendant.\n"
            }
          ],
          "explanation": "`range(debut, fin, pas)` accepte un pas négatif : les valeurs\ndescendent de `debut` vers `fin`, exclue."
        }
      ]
    }
  ]
}