{
  "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`), instructions `break` et\n`continue`.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "if"
      ],
      "title": "Syntaxe du if",
      "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",
      "statement": "À quoi sert le mot-clé `elif` en Python ?",
      "options": [
        {
          "text": "C'est un alias pour `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` (ou `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",
      "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": [
        "for-range"
      ],
      "title": "Boucle for range",
      "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 (0, 1,\n2, 3, 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]."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "for-range-bornes"
      ],
      "title": "range avec deux arguments",
      "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)."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "while"
      ],
      "title": "Boucle 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",
          "correct": false,
          "feedback": "Erreur : la boucle décrémente, donc affiche\nen ordre **décroissant**.\n"
        },
        {
          "text": "Boucle infinie",
          "correct": false,
          "feedback": "Erreur : `n -= 1` garantit la terminaison.\n"
        },
        {
          "text": "N'affiche rien",
          "correct": false,
          "feedback": "Erreur : `n = 10 > 0`, la boucle s'exécute\nau moins une fois.\n"
        },
        {
          "text": "Affiche les nombres de 10 à 1 (en décroissant)",
          "correct": true,
          "feedback": "Bonne réponse : à chaque tour, on affiche\n`n`, puis on le diminue de 1. Quand n\natteint 0, on 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": [
        "break"
      ],
      "title": "break",
      "statement": "Que fait l'instruction `break` dans une boucle ?",
      "options": [
        {
          "text": "Sort immédiatement de la boucle (la plus interne)",
          "correct": true,
          "feedback": "Bonne réponse : `break` quitte la boucle.\nL'exécution continue **après** la boucle.\n"
        },
        {
          "text": "Met la boucle en pause",
          "correct": false,
          "feedback": "Erreur : `break` ne met pas en pause, elle\n**interrompt**.\n"
        },
        {
          "text": "Recommence la boucle au début",
          "correct": false,
          "feedback": "Erreur : c'est `continue` qui passe au tour\nsuivant.\n"
        },
        {
          "text": "Termine le programme entier",
          "correct": false,
          "feedback": "Erreur : ce serait `exit()` ou `sys.exit()`.\n"
        }
      ],
      "explanation": "Utile pour sortir d'une recherche dès qu'on a\ntrouvé : `for x in liste: if x == cible: break`."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "continue"
      ],
      "title": "continue",
      "statement": "Que fait l'instruction `continue` dans une\nboucle ?",
      "options": [
        {
          "text": "Met la boucle en pause",
          "correct": false,
          "feedback": "Erreur : pas de pause.\n"
        },
        {
          "text": "Continue indéfiniment",
          "correct": false,
          "feedback": "Erreur : `continue` ne crée pas de boucle\ninfinie.\n"
        },
        {
          "text": "Saute le reste de l'itération courante et passe à la suivante",
          "correct": true,
          "feedback": "Bonne réponse : on ignore les instructions\nrestantes du tour, on passe directement à\nl'itération suivante.\n"
        },
        {
          "text": "Sort de la boucle",
          "correct": false,
          "feedback": "Erreur : c'est `break`.\n"
        }
      ],
      "explanation": "Exemple : `for x in liste: if x < 0: continue ;\nprint(x)` affiche tous les éléments\n**positifs** sans plonger dans une condition\nimbriquée."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "parite"
      ],
      "title": "Test de parité",
      "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": [
        "print-loop"
      ],
      "title": "Boucle simple",
      "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))`."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "imbrication"
      ],
      "title": "Boucles imbriquées",
      "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),\n(2,0), (2,1).\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",
      "statement": "Pourquoi `False and 1/0 == 0` ne lève-t-il pas\n`ZeroDivisionError` en Python ?",
      "options": [
        {
          "text": "Parce qu'il faut un `try/except`",
          "correct": false,
          "feedback": "Erreur : pas nécessaire ici.\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 opérande est `False`, le second n'est pas évalué",
          "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": [
        "break-trace"
      ],
      "title": "Trace avec break",
      "statement": "Que produit ce code ?\n```python\nfor i in range(10):\n    if i == 3:\n        break\n    print(i)\n```",
      "options": [
        {
          "text": "0 1 2 3 4 5 6 7 8 9",
          "correct": false,
          "feedback": "Erreur : `break` interrompt la boucle.\n"
        },
        {
          "text": "Aucune sortie",
          "correct": false,
          "feedback": "Erreur : le `break` n'intervient qu'à i = 3.\n"
        },
        {
          "text": "0 1 2",
          "correct": true,
          "feedback": "Bonne réponse : on affiche 0, 1, 2 ; au\ntour i = 3, le `break` interrompt la boucle\navant l'affichage.\n"
        },
        {
          "text": "0 1 2 3",
          "correct": false,
          "feedback": "Erreur : `break` se déclenche **avant**\n`print`. Le 3 n'est pas affiché.\n"
        }
      ],
      "explanation": "Tracer pas à pas : i = 0 → print(0) ; i = 1 →\nprint(1) ; i = 2 → print(2) ; i = 3 → break."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "continue-trace"
      ],
      "title": "Trace avec continue",
      "statement": "Que produit ce code ?\n```python\nfor i in range(5):\n    if i % 2 == 0:\n        continue\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),\nle `continue` saute le `print`. Restent\n1 et 3.\n"
        },
        {
          "text": "0 1 2 3 4",
          "correct": false,
          "feedback": "Erreur : `continue` saute l'affichage pour\nles pairs.\n"
        },
        {
          "text": "0 2 4 (les pairs)",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse, on saute les\npairs.\n"
        }
      ],
      "explanation": "`continue` est utile pour filtrer dans une\nboucle. Alternative : `if i % 2 != 0:\nprint(i)`."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "while-condition"
      ],
      "title": "While et condition",
      "statement": "Que fait ce code, supposant la saisie d'un\nentier non nul ?\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 infinie systématique",
          "correct": false,
          "feedback": "Erreur : pour tout n testé jusqu'à présent,\nla suite atteint 1.\n"
        },
        {
          "text": "Affiche la suite de Fibonacci",
          "correct": false,
          "feedback": "Erreur : aucune relation avec Fibonacci.\n"
        },
        {
          "text": "Calcule la suite de Syracuse (ou Collatz) jusqu'à atteindre 1",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'algorithme classique\ndont la terminaison est une **conjecture**\n(non démontrée, mais vérifiée pour de très\ngrandes valeurs).\n"
        },
        {
          "text": "Lève une exception",
          "correct": false,
          "feedback": "Erreur : code valide.\n"
        }
      ],
      "explanation": "La conjecture de Syracuse (1937) demeure ouverte\nen mathématiques : on ne sait pas prouver que la\nsuite atteint toujours 1 pour tout n entier\npositif."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "bug-condition"
      ],
      "title": "Bug d'imbrication",
      "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",
      "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",
      "statement": "Quelle boucle permet d'afficher les éléments\nd'une liste ?",
      "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 : forme idiomatique Python.\nPlus lisible et moins sujette aux erreurs\nd'indice.\n"
        },
        {
          "text": "```python\nfor i in range(len(liste)):\n    print(liste[i])\n```\n",
          "correct": false,
          "feedback": "Code correct mais peu pythonique. Préférer\nla boucle directe sur les éléments.\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 : `for i, x in enumerate(liste):`."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "comprehension"
      ],
      "title": "Compréhension de liste",
      "statement": "Quelle 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 = map(2, range(10))`",
          "correct": false,
          "feedback": "Erreur : `map` prend une **fonction** en\npremier argument, pas un nombre.\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",
      "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",
      "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 `i = 0` est faux",
          "correct": false,
          "feedback": "Erreur : 0 < 10 est vrai.\n"
        },
        {
          "text": "Parce que `i` n'est jamais modifié à l'intérieur de 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": [
        "enumerate"
      ],
      "title": "enumerate",
      "statement": "À quoi sert `enumerate` en Python ?",
      "options": [
        {
          "text": "À compter les éléments d'une liste",
          "correct": false,
          "feedback": "Erreur : c'est `len`.\n"
        },
        {
          "text": "À itérer en récupérant à la fois l'indice et la valeur de chaque élément",
          "correct": true,
          "feedback": "Bonne réponse : usage typique\n`for i, x in enumerate(liste):`. Plus\nlisible que `for i in range(len(liste)):`\n+ `liste[i]`.\n"
        },
        {
          "text": "À renvoyer une chaîne",
          "correct": false,
          "feedback": "Erreur : `enumerate` renvoie un itérateur.\n"
        },
        {
          "text": "À trier la liste",
          "correct": false,
          "feedback": "Erreur : c'est `sorted` ou `liste.sort()`.\n"
        }
      ],
      "explanation": "`enumerate(iterable, start=0)` produit des\npaires `(index, valeur)`. Avec `start=1` pour\ncommencer à 1."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "piege-augmenter-liste"
      ],
      "title": "Modifier en parcourant",
      "statement": "Pourquoi ce code est-il **buggué** ?\n```python\nliste = [1, 2, 3, 4, 5]\nfor x in liste:\n    if x > 2:\n        liste.remove(x)\n```",
      "options": [
        {
          "text": "Parce que `5 > 2` est faux",
          "correct": false,
          "feedback": "Erreur : 5 est bien > 2.\n"
        },
        {
          "text": "Parce que modifier la liste pendant qu'on l'itère provoque des éléments sautés ou des comportements imprévisibles",
          "correct": true,
          "feedback": "Bonne réponse : Python ne garantit pas le\ncomportement quand la liste est modifiée\npendant son parcours. Solution :\nconstruire une nouvelle liste filtrée\n(`[x for x in liste if x <= 2]`).\n"
        },
        {
          "text": "Parce que `remove` n'existe pas",
          "correct": false,
          "feedback": "Erreur : `remove` existe.\n"
        },
        {
          "text": "Parce que la boucle ne s'exécute jamais",
          "correct": false,
          "feedback": "Erreur : elle s'exécute, mais avec des bugs.\n"
        }
      ],
      "explanation": "Règle de sécurité : ne **jamais** modifier une\ncollection pendant qu'on la parcourt. Itérer\nsur une copie ou construire une nouvelle\ncollection."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "for-else"
      ],
      "title": "for / else",
      "statement": "Python autorise une clause `else` après un\n`for`. Quand est-elle exécutée ?",
      "options": [
        {
          "text": "Si la boucle se termine normalement (sans `break`)",
          "correct": true,
          "feedback": "Bonne réponse : trait peu connu mais\npuissant. Si la boucle se termine par\n`break`, l'`else` est sauté ; sinon, il est\nexécuté. Utile pour les recherches.\n"
        },
        {
          "text": "Toujours, comme un finally",
          "correct": false,
          "feedback": "Erreur : il y a une condition.\n"
        },
        {
          "text": "Si la boucle est interrompue par `break`",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse.\n"
        },
        {
          "text": "Jamais en Python",
          "correct": false,
          "feedback": "Erreur : la syntaxe existe.\n"
        }
      ],
      "explanation": "Schéma : recherche avec « else = pas\ntrouvé ».\n```python\nfor x in liste:\n    if x == cible:\n        print(\"trouvé\")\n        break\nelse:\n    print(\"absent\")\n```"
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes, laquelle est\n**fausse** ?",
      "options": [
        {
          "text": "`break` et `continue` ont le même effet",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : `break`\n**sort** de la boucle ; `continue` passe à\nl'itération **suivante**. Effets très\ndifférents.\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": "Mnémonique : « `break` ⇒ on casse (sort),\n`continue` ⇒ on continue (au tour suivant) »."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "while-true",
        "break",
        "saisie"
      ],
      "title": "Boucle de saisie validée",
      "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\nwhile True:\n    n = int(input(\"n ? \"))\n    if n > 0:\n        break\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : la boucle `while True` répète la\nsaisie indéfiniment ; on en sort dès que la\ncondition `n > 0` est satisfaite, grâce au\n`break`. C'est l'idiome standard pour la\nvalidation d'entrée utilisateur.\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\nfois** la valeur. Si l'utilisateur entre encore\nune valeur invalide à la deuxième saisie, le\nprogramme continue avec cette valeur erronée.\n"
        }
      ],
      "explanation": "Le schéma `while True` + `break` est très utilisé\npour les boucles dont la condition de sortie ne\ns'évalue qu'au milieu du corps. Il est plus clair\nqu'une variable booléenne intermédiaire dans\nbeaucoup de cas."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "if",
        "elif",
        "structure"
      ],
      "title": "if/elif vs plusieurs if",
      "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",
          "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": "L'extrait A teste les conditions de manière exclusive ; l'extrait B les évalue indépendamment, ce qui peut écraser la valeur de y",
          "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 que l'extrait A",
          "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": [
        "zip",
        "parcours-parallele"
      ],
      "title": "Parcours parallèle de deux listes",
      "statement": "Quelle est la façon la plus pythonique de parcourir\nen parallèle deux listes `noms` et `notes` de même\nlongueur, pour afficher chaque nom suivi de sa\nnote ?",
      "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": false,
          "feedback": "Code correct mais peu pythonique. Il manipule\ndes indices alors que l'on n'en a pas besoin.\nPréférer `zip`, qui exprime directement le\nparcours parallèle.\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, note in zip(noms, notes):\n    print(nom, note)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : `zip(noms, notes)` produit une\nséquence de couples `(nom, note)` que l'on\ndéballe directement dans la boucle. C'est\nl'expression idiomatique du parcours parallèle.\n"
        }
      ],
      "explanation": "Variantes utiles : `enumerate` quand on veut\nl'indice **et** l'élément ; `zip(*matrice)` pour\ntransposer une matrice ; `zip(a, b, c)` pour\nparcourir trois séquences en parallèle."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "while",
        "condition",
        "dichotomie"
      ],
      "title": "Calcul d'une racine entière",
      "statement": "On exécute le code suivant :\n\n```python\nn = 30\nr = 0\nwhile r * r <= n:\n    r = r + 1\n```\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\ncarré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$."
    }
  ]
}