-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
254 lines (223 loc) · 11.3 KB
/
Copy pathscript.js
File metadata and controls
254 lines (223 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// ============================================================================
// QUIZ : Algorithmique & Python - Module 3 (Tronc Commun)
// Créé par Hicham Baaki
// ============================================================================
const quizData = [
{ id: 1, type: 'vf', diff: 'easy', q: 'Une variable peut changer de valeur pendant l\'exécution d\'un programme.', ans: true, fb: '✅ Correct ! Contrairement à une constante, une variable est un espace mémoire modifiable via l\'affectation.' },
{ id: 2, type: 'qcm', diff: 'easy', q: 'En Python, quel symbole utilise-t-on pour l\'affectation ?', opts: ['==', '=', '←', ':='], ans: 1, fb: '✅ Correct ! `=` attribue une valeur en Python. `==` teste l\'égalité, et `←` est réservé au pseudo-code algorithmique.' },
{ id: 3, type: 'fill', diff: 'easy', q: 'Pour afficher un résultat à l\'écran en Python, on utilise la fonction ______.', ans: ['print()'], fb: '✅ Correct ! `print()` est l\'équivalent Python de l\'instruction algorithmique `Écrire`.' },
{ id: 4, type: 'qcm', diff: 'easy', q: 'Quel mot-clé Python correspond au `Sinon` algorithmique ?', opts: ['elseif', 'otherwise', 'else', 'elif'], ans: 2, fb: '✅ Correct ! `else` gère le cas par défaut. `elif` équivaut à `Sinon Si`.' },
{ id: 5, type: 'fill', diff: 'easy', q: 'Pour calculer la moyenne exacte de 3 notes en Python : `moy = (n1 + n2 + n3) ______ 3`', ans: ['/'], fb: '✅ Correct ! `/` effectue une division standard (retourne un float). `//` tronquerait le résultat.' },
{ id: 6, type: 'qcm', diff: 'medium', q: 'Quelle structure permet d\’exécuter les instructions une par une, dans l\’ordre où elles sont écrites ?', opts: ['Sélective (if/else)', 'Séquentielle', 'Itérative (boucle)', 'Fonction'], ans: 1, fb: '✅ Correct ! La structure séquentielle suit un flux linéaire, sans saut ni condition.' },
{ id: 7, type: 'vf', diff: 'medium', q: 'Si la condition d\'un `if` est fausse, le programme s\'arrête immédiatement.', ans: false, fb: '❌ Faux. Le bloc `if` est ignoré, mais l\'exécution continue avec les instructions suivantes (ou le `else`).' },
{ id: 8, type: 'qcm', diff: 'medium', q: 'Que retourne `10 // 3` en Python ?', opts: ['3.33', '3', '1', 'Erreur'], ans: 1, fb: '✅ Correct ! `//` effectue une division entière (quotient). `/` donnerait un flottant.' },
{ id: 9, type: 'fill', diff: 'medium', q: 'Pour convertir une saisie texte en nombre entier, on utilise : `nombre = ______input()`', ans: ['int()'], fb: '✅ Correct ! `input()` retourne toujours du texte. `int()` le convertit pour les calculs mathématiques.' },
{ id: 10, type: 'qcm', diff: 'hard', q: 'Trace : `x=5` → `if x>10: print("A")` → `elif x>3: print("B")` → `else: print("C")`. Sortie ?', opts: ['A', 'B', 'C', 'A et B'], ans: 1, fb: '✅ Correct ! `x>10` est faux. Python teste `x>3` (vrai) → affiche `B` et ignore le reste.' },
{ id: 11, type: 'vf', diff: 'hard', q: 'En Python, on peut échanger deux variables sans variable temporaire avec : `a, b = b, a`', ans: true, fb: '✅ Correct ! Python évalue le côté droit avant l\'assignation, permettant un échange simultané sécurisé.' },
];
let score = 0, answered = 0;
const container = document.getElementById('quiz-container');
const scoreText = document.getElementById('score-text');
const progressFill = document.getElementById('progress-fill');
const confettiCanvas = document.getElementById('confetti-canvas');
const ctx = confettiCanvas.getContext('2d');
// ============================================================================
// EFFET CONFETTIS (léger, performant, sans librairie externe)
// ============================================================================
function resizeCanvas() {
confettiCanvas.width = window.innerWidth;
confettiCanvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
function launchConfetti() {
confettiCanvas.style.display = 'block';
const particles = [];
const colors = ['#16a34a', '#22c55e', '#4ade80', '#86efac', '#fbbf24', '#f59e0b'];
for (let i = 0; i < 50; i++) {
particles.push({
x: Math.random() * confettiCanvas.width,
y: -10,
size: Math.random() * 8 + 4,
color: colors[Math.floor(Math.random() * colors.length)],
speed: Math.random() * 3 + 2,
rotation: Math.random() * 360,
rotationSpeed: (Math.random() - 0.5) * 10
});
}
let animationFrame;
function animate() {
ctx.clearRect(0, 0, confettiCanvas.width, confettiCanvas.height);
let active = false;
particles.forEach(p => {
if (p.y < confettiCanvas.height + 20) {
active = true;
p.y += p.speed;
p.x += Math.sin(p.y * 0.02) * 0.5;
p.rotation += p.rotationSpeed;
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate((p.rotation * Math.PI) / 180);
ctx.fillStyle = p.color;
ctx.fillRect(-p.size/2, -p.size/2, p.size, p.size);
ctx.restore();
}
});
if (active) {
animationFrame = requestAnimationFrame(animate);
} else {
ctx.clearRect(0, 0, confettiCanvas.width, confettiCanvas.height);
confettiCanvas.style.display = 'none';
cancelAnimationFrame(animationFrame);
}
}
animate();
}
// ============================================================================
// INITIALISATION DU QUIZ
// ============================================================================
function init() {
quizData.forEach((q, i) => {
const card = document.createElement('div');
card.className = 'card';
card.id = `card-${i}`;
card.innerHTML = `
<span class="badge ${q.diff}">${q.diff}</span>
<h3 class="q-title">Q${i+1}. ${q.q}</h3>
<div class="icon-effect" id="icon-${i}"></div>
<div id="opts-${i}" class="options"></div>
<div class="feedback" id="fb-${i}"></div>
`;
container.appendChild(card);
if (q.type === 'qcm' || q.type === 'vf') {
const options = q.type === 'vf' ? ['Vrai', 'Faux'] : q.opts;
options.forEach((opt, j) => {
const btn = document.createElement('button');
btn.className = 'btn-opt';
btn.textContent = opt;
// Closure sécurisée avec i et q.ans
btn.onclick = () => handleAnswer(i, j, q.ans, q.fb, q.type);
document.getElementById(`opts-${i}`).appendChild(btn);
});
} else if (q.type === 'fill') {
const div = document.createElement('div');
div.className = 'input-group';
div.innerHTML = `
<input type="text" id="input-${i}" placeholder="Votre réponse..." autocomplete="off">
<button class="validate-btn" id="btn-${i}">Valider</button>
`;
document.getElementById(`opts-${i}`).appendChild(div);
const inputEl = document.getElementById(`input-${i}`);
const btnEl = document.getElementById(`btn-${i}`);
// Gestion du clic et de la touche Entrée
btnEl.onclick = () => handleFill(i, q.ans[0], q.fb);
inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter') handleFill(i, q.ans[0], q.fb);
});
}
});
}
// ============================================================================
// GESTION DES RÉPONSES QCM / VRAI-FAUX
// ============================================================================
function handleAnswer(idx, selected, correct, fb, qtype) {
const optsContainer = document.getElementById(`opts-${idx}`);
const btns = optsContainer.querySelectorAll('.btn-opt');
// Empêcher les clics multiples
btns.forEach(b => b.disabled = true);
const card = document.getElementById(`card-${idx}`);
const icon = document.getElementById(`icon-${idx}`);
const feedbackEl = document.getElementById(`fb-${idx}`);
// Déterminer si la réponse est correcte
let isCorrect = false;
if (qtype === 'vf') {
// Pour Vrai/Faux : selected=0 → Vrai, selected=1 → Faux
isCorrect = (correct === true && selected === 0) || (correct === false && selected === 1);
} else {
// Pour QCM : comparaison directe des indices
isCorrect = (selected === correct);
}
if (isCorrect) {
score++;
btns[selected].classList.add('correct');
card.classList.add('pulse-success');
icon.textContent = '🎉';
// Lancer les confettis + effet sonore optionnel
launchConfetti();
feedbackEl.className = 'feedback show success';
feedbackEl.innerHTML = fb;
} else {
// Trouver l'index de la bonne réponse pour l'afficher
let correctIdx = correct;
if (qtype === 'vf') {
correctIdx = correct === true ? 0 : 1;
}
btns[selected].classList.add('incorrect');
btns[correctIdx].classList.add('correct');
card.classList.add('shake');
icon.textContent = '❌';
feedbackEl.className = 'feedback show error';
feedbackEl.innerHTML = `❌ Incorrect. ${fb}`;
}
// Afficher l'icône avec animation
icon.classList.add('show');
updateProgress();
}
// ============================================================================
// GESTION DES RÉPONSES TEXTE À TROUS
// ============================================================================
function handleFill(idx, correctVal, fb) {
const input = document.getElementById(`input-${idx}`);
const btn = document.getElementById(`btn-${idx}`);
// Vérifier si déjà répondu
if (btn.disabled) return;
const val = input.value.trim().toLowerCase();
const card = document.getElementById(`card-${idx}`);
const icon = document.getElementById(`icon-${idx}`);
const feedbackEl = document.getElementById(`fb-${idx}`);
if (val === correctVal.toLowerCase()) {
score++;
input.style.borderColor = 'var(--success)';
input.style.background = '#dcfce7';
card.classList.add('pulse-success');
icon.textContent = '🎉';
// Lancer les confettis
launchConfetti();
feedbackEl.className = 'feedback show success';
feedbackEl.innerHTML = fb;
} else {
input.style.borderColor = 'var(--error)';
input.style.background = '#fee2e2';
card.classList.add('shake');
icon.textContent = '❌';
feedbackEl.className = 'feedback show error';
feedbackEl.innerHTML = `❌ Incorrect. Réponse attendue : <b>${correctVal}</b>. ${fb}`;
}
// Désactiver les champs après réponse
input.disabled = true;
btn.disabled = true;
icon.classList.add('show');
updateProgress();
}
// ============================================================================
// MISE À JOUR DE LA PROGRESSION ET SCORE FINAL
// ============================================================================
function updateProgress() {
answered++;
scoreText.textContent = `${score} / ${quizData.length}`;
progressFill.style.width = `${(answered / quizData.length) * 100}%`;
if (answered === quizData.length) {
setTimeout(() => {
const msg = score >= 9
? '🌟 Excellent ! Maîtrise complète de Python & Algorithmique.'
: score >= 6
? '👍 Bien ! Révisez les types et la logique conditionnelle.'
: '📚 À retravailler : relisez variables, `input/print`, et `if/else`.';
alert(`Quiz terminé !\nScore : ${score}/${quizData.length}\n${msg}`);
}, 500);
}
}
// ============================================================================
// LANCEMENT AU CHARGEMENT DU DOM
// ============================================================================
document.addEventListener('DOMContentLoaded', init);