Paillasse/design/_backup-ludique/resources/views/generateur.blade.php
2026-10-06 23:28:18 +02:00

104 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('layouts.vitrine')
@section('titre', 'Générateur de TD')
@section('contenu')
<section class="border-b-4 border-black bg-white">
<div class="mx-auto max-w-7xl px-5 pb-12 pt-12 lg:px-10">
<p class="inline-block -rotate-2 border-2 border-black bg-[#FF6FB5] px-3 py-1 text-sm font-extrabold uppercase tracking-wider shadow-[4px_4px_0px_#000000]">Générateur de TD</p>
<h1 class="mt-6 max-w-5xl text-6xl font-black leading-[0.9] tracking-tighter sm:text-7xl">Des exercices à l’infini, corrigés tout de suite.</h1>
<p class="mt-6 max-w-3xl text-xl font-medium leading-relaxed">Chaque clic tire un nouvel exercice au hasard. La correction montre le calcul, étape par étape. En mode examen : 10 questions, chronométrées, notées sur 20.</p>
</div>
</section>
<div class="mx-auto grid max-w-7xl gap-10 px-5 py-12 lg:grid-cols-12 lg:px-10">
<aside class="grid content-start gap-4 lg:col-span-3">
<p class="font-extrabold">Type d’exercice</p>
<div class="grid gap-2" role="group" aria-label="Type d’exercice">
<button type="button" data-type="reseau" aria-pressed="true" class="border-4 border-black bg-[#7CF2C0] px-4 py-3 text-left font-extrabold shadow-[4px_4px_0px_#000000] aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-2 aria-pressed:outline-black">Sous-réseaux</button>
<button type="button" data-type="binaire" aria-pressed="false" class="border-4 border-black bg-[#FFD23F] px-4 py-3 text-left font-extrabold shadow-[4px_4px_0px_#000000] aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-2 aria-pressed:outline-black">Décimal → binaire</button>
<button type="button" data-type="hexa" aria-pressed="false" class="border-4 border-black bg-[#C4B5FD] px-4 py-3 text-left font-extrabold shadow-[4px_4px_0px_#000000] aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-2 aria-pressed:outline-black">Décimal → hexadécimal</button>
<button type="button" data-type="c2" aria-pressed="false" class="border-4 border-black bg-[#FF6FB5] px-4 py-3 text-left font-extrabold shadow-[4px_4px_0px_#000000] aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-2 aria-pressed:outline-black">Complément à 2</button>
<button type="button" data-type="chmod" aria-pressed="false" class="border-4 border-black bg-white px-4 py-3 text-left font-extrabold shadow-[4px_4px_0px_#000000] aria-pressed:outline aria-pressed:outline-4 aria-pressed:outline-offset-2 aria-pressed:outline-black">Droits chmod</button>
</div>
<div class="mt-4 border-4 border-black bg-black p-4 text-white">
<p class="font-extrabold">Mode examen</p>
<p class="mt-1 text-sm font-semibold">10 questions du type choisi, chrono, note sur 20.</p>
<button type="button" id="g-exam" class="mt-3 border-2 border-white bg-[#ec3013] px-3 py-1 font-extrabold">Lancer l’examen</button>
</div>
<p class="border-2 border-black bg-white p-3 font-mono text-sm font-semibold">Série : <span id="g-serie">0</span> · Réussis : <span id="g-ok">0</span> / <span id="g-tot">0</span></p>
</aside>
<section class="lg:col-span-9" aria-live="polite">
<div id="g-barre" hidden class="mb-5 flex flex-wrap items-center gap-4 border-4 border-black bg-[#ec3013] px-5 py-3 font-extrabold text-white"><span>MODE EXAMEN</span><span>Question <span id="g-qn">1</span> / 10</span><span class="ml-auto font-mono text-xl" id="g-chrono">00:00</span></div>
<form id="g-form" class="border-4 border-black bg-white p-7 shadow-[10px_10px_0px_#000000]">
<p id="g-titre" class="font-mono text-sm font-semibold"></p>
<p id="g-enonce" class="mt-3 text-3xl font-black leading-tight"></p>
<div id="g-champs" class="mt-6 grid gap-4 sm:grid-cols-3"></div>
<div class="mt-6 flex flex-wrap gap-4"><button type="submit" class="inline-flex items-center gap-4 border-4 border-black bg-black px-6 py-4 text-lg font-extrabold text-white shadow-[6px_6px_0px_#ec3013] transition-transform hover:-translate-x-0.5 hover:-translate-y-0.5 hover:bg-[#ec3013] hover:shadow-[8px_8px_0px_#000000]">Corriger</button><button type="button" id="g-new" class="inline-flex items-center gap-3 border-4 border-black bg-white px-5 py-3 font-extrabold shadow-[4px_4px_0px_#000000] hover:bg-[#FFD23F]">Nouvel exercice</button></div>
<div id="g-corr" hidden class="mt-6 border-4 border-black p-5"><p id="g-verdict" class="text-xl font-black"></p><pre id="g-etapes" class="mt-3 whitespace-pre-wrap font-mono text-sm font-semibold"></pre></div>
</form>
<div id="g-fin" hidden class="mt-8 border-4 border-black bg-[#FFD23F] p-7 shadow-[10px_10px_0px_#000000]"><p class="text-2xl font-black">Examen terminé</p><p class="mt-2 font-mono text-7xl font-semibold"><span id="g-note">0</span> / 20</p><p id="g-temps" class="mt-2 text-lg font-bold"></p></div>
</section>
</div>
<script>
(function () {
var $ = function (id) { return document.getElementById(id); };
var type = 'reseau', cur = null, serie = 0, ok = 0, tot = 0, exam = null;
var r = function (a, b) { return a + Math.floor(Math.random() * (b - a + 1)); };
var ip = function (n) { return [n >>> 24, (n >>> 16) & 255, (n >>> 8) & 255, n & 255].join('.'); };
var b8 = function (n) { return (n & 255).toString(2).padStart(8, '0'); };
var G = {
reseau: function () {
var pref = [[10, r(0, 255)], [172, r(16, 31)], [192, 168]][r(0, 2)], a = ((pref[0] << 24) | (pref[1] << 16) | (r(0, 255) << 8) | r(1, 254)) >>> 0, c = r(20, 30);
var m = c === 0 ? 0 : (0xFFFFFFFF << (32 - c)) >>> 0, net = (a & m) >>> 0, bc = (net | (~m >>> 0)) >>> 0, h = Math.pow(2, 32 - c) - 2;
return { titre: 'Sous-réseaux · IPv4', enonce: ip(a) + '/' + c, champs: [['Adresse réseau', ip(net)], ['Diffusion', ip(bc)], ['Nombre d’hôtes', String(h)]],
etapes: 'Masque /' + c + ' = ' + ip(m) + '\nAdresse ET masque = ' + ip(net) + '\nBits d’hôte à 1 → diffusion ' + ip(bc) + '\nHôtes = 2^' + (32 - c) + ' − 2 = ' + h };
},
binaire: function () { var n = r(0, 255); return { titre: 'Conversion · décimal vers binaire (8 bits)', enonce: String(n), champs: [['En binaire', b8(n), function (v) { return /^[01]{1,8}$/.test(v) && parseInt(v, 2) === n; }]], etapes: [128, 64, 32, 16, 8, 4, 2, 1].map(function (p) { return p + ' → ' + ((n & p) ? '1' : '0'); }).join(' ') + '\n' + n + ' = ' + b8(n) }; },
hexa: function () { var n = r(0, 255), hx = n.toString(16).toUpperCase().padStart(2, '0'); return { titre: 'Conversion · décimal vers hexadécimal', enonce: String(n), champs: [['En hexadécimal', hx, function (v) { return parseInt(v.replace(/^0x/i, ''), 16) === n && /^(0x)?[0-9a-f]{1,2}$/i.test(v); }]], etapes: n + ' = ' + Math.floor(n / 16) + ' × 16 + ' + (n % 16) + '\n' + Math.floor(n / 16) + ' → ' + (Math.floor(n / 16)).toString(16).toUpperCase() + ', ' + (n % 16) + ' → ' + (n % 16).toString(16).toUpperCase() + '\nRésultat : ' + hx }; },
c2: function () { var n = r(-128, -1); return { titre: 'Complément à 2 · sur 8 bits', enonce: String(n), champs: [['Sur 8 bits', b8(n)]], etapes: '|' + n + '| = ' + (-n) + ' = ' + b8(-n) + '\nOn inverse les bits : ' + b8(~(-n)) + '\nOn ajoute 1 : ' + b8(n) }; },
chmod: function () {
var d = [r(4, 7), r(0, 7), r(0, 7)], s = function (x) { return (x & 4 ? 'r' : '-') + (x & 2 ? 'w' : '-') + (x & 1 ? 'x' : '-'); }, sym = d.map(s).join(''), oct = d.join('');
return Math.random() < .5
? { titre: 'Droits Linux · octal vers symbolique', enonce: 'chmod ' + oct, champs: [['Droits (ex. rwxr-x---)', sym]], etapes: d.map(function (x, i) { return ['propriétaire', 'groupe', 'autres'][i] + ' : ' + x + ' = ' + s(x); }).join('\n') + '\nr = 4, w = 2, x = 1' }
: { titre: 'Droits Linux · symbolique vers octal', enonce: sym, champs: [['En octal (ex. 750)', oct]], etapes: d.map(function (x, i) { return s(x) + ' = ' + x; }).join('\n') + '\nr = 4, w = 2, x = 1' };
}
};
function tirer() {
cur = G[type]();
$('g-titre').textContent = cur.titre; $('g-enonce').textContent = cur.enonce; $('g-corr').hidden = true;
$('g-champs').innerHTML = cur.champs.map(function (c, i) { return '<label class="grid gap-2 font-extrabold">' + c[0] + '<input data-i="' + i + '" autocomplete="off" class="w-full border-4 border-black bg-white px-4 py-3 text-lg font-semibold text-black placeholder:text-[#444141] focus:bg-[#FFFBEA] focus:shadow-[4px_4px_0px_#000000] focus:outline-none font-mono"></label>'; }).join('');
var f = $('g-champs').querySelector('input'); if (f) f.focus();
if (exam) $('g-qn').textContent = exam.n + 1;
}
$('g-form').addEventListener('submit', function (e) {
e.preventDefault();
if (!$('g-corr').hidden) { if (exam) suivante(); return; }
var bons = 0;
cur.champs.forEach(function (c, i) {
var inp = $('g-champs').querySelector('[data-i="' + i + '"]'), v = inp.value.trim().replace(/\s/g, ''), juste = c[2] ? c[2](v) : v.toLowerCase() === c[1].toLowerCase();
if (juste) bons++; inp.classList.toggle('bg-[#7CF2C0]', juste); inp.classList.toggle('bg-[#FFD9E8]', !juste);
});
var tout = bons === cur.champs.length; tot++; if (tout) { ok++; serie++; } else serie = 0;
if (exam) exam.pts += bons / cur.champs.length;
$('g-serie').textContent = serie; $('g-ok').textContent = ok; $('g-tot').textContent = tot;
var cr = $('g-corr'); cr.hidden = false; cr.className = 'mt-6 border-4 border-black p-5 ' + (tout ? 'bg-[#7CF2C0]' : 'bg-[#FFD9E8]');
$('g-verdict').textContent = (tout ? 'Juste !' : 'Pas tout à fait.') + ' Réponse : ' + cur.champs.map(function (c) { return c[1]; }).join(' · ');
$('g-etapes').textContent = cur.etapes;
if (exam) $('g-new').textContent = exam.n === 9 ? 'Voir ma note' : 'Question suivante';
});
function suivante() { exam.n++; if (exam.n >= 10) return finir(); tirer(); }
function finir() {
clearInterval(exam.t); $('g-form').hidden = true; $('g-fin').hidden = false;
$('g-note').textContent = (Math.round(exam.pts * 2 * 10) / 10).toString().replace('.', ',');
$('g-temps').textContent = 'Temps : ' + $('g-chrono').textContent; exam = null; $('g-new').textContent = 'Nouvel exercice';
}
$('g-new').addEventListener('click', function () { if (exam) { if ($('g-corr').hidden) return; suivante(); } else tirer(); });
document.querySelectorAll('[data-type]').forEach(function (b) { b.addEventListener('click', function () { if (exam) return; type = b.dataset.type; document.querySelectorAll('[data-type]').forEach(function (x) { x.setAttribute('aria-pressed', x === b); }); tirer(); }); });
$('g-exam').addEventListener('click', function () {
var d = Date.now(); exam = { n: 0, pts: 0, t: setInterval(function () { var s = Math.floor((Date.now() - d) / 1000); $('g-chrono').textContent = String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0'); }, 1000) };
$('g-barre').hidden = false; $('g-fin').hidden = true; $('g-form').hidden = false; tirer();
});
tirer();
})();
</script>
@endsection