2026-10-08 02:01:58 +02:00
@ extends ( 'layouts.vitrine' )
@ section ( 'titre' , 'Générateur de TD' )
@ section ( 'contenu' )
2026-10-09 21:07:25 +02:00
@ include ( 'partials.apercu' )
2026-10-08 02:01:58 +02:00
< section class = " border-b border-[#e3e5e8] bg-white " >
< div class = " mx-auto max-w-7xl px-5 pb-12 pt-12 lg:px-10 " >
< p class = " inline-block border border-[#dadce0] rounded-lg bg-[#fce8e6] px-3 py-1 text-sm font-bold " > Générateur de TD </ p >
< div class = " min-w-0 flex-1 " >< p data - public = " P " class = " mb-2 inline-flex items-center gap-2 rounded-full border border-[#b8c1cf] bg-white px-3 py-1 text-sm font-bold text-[#1d3fa8] " > Pour les profs </ p >
< h1 class = " mt-6 max-w-5xl text-6xl font-extrabold leading-[1.05] tracking-tight sm:text-7xl " > Des exercices à l’ infini , corrigés tout de suite .</ h1 ></ div >
< 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 border-[#e3e5e8] rounded-2xl bg-[#e6f4ea] px-4 py-3 text-left font-extrabold 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 border-[#e3e5e8] rounded-2xl bg-[#fef7e0] px-4 py-3 text-left font-extrabold 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 border-[#e3e5e8] rounded-2xl bg-[#eef0fd] px-4 py-3 text-left font-extrabold 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 border-[#e3e5e8] rounded-2xl bg-[#fce8e6] px-4 py-3 text-left font-extrabold 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 border-[#e3e5e8] rounded-2xl bg-white px-4 py-3 text-left font-extrabold 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 border-[#e3e5e8] rounded-2xl 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 border-[#dadce0] rounded-lg 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 border-[#e3e5e8] rounded-2xl 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 border-[#e3e5e8] rounded-2xl bg-white p-7 " >
< p id = " g-titre " class = " font-mono text-sm font-semibold " ></ p >
< p id = " g-enonce " class = " mt-3 text-3xl font-extrabold 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 border-[#e3e5e8] rounded-2xl bg-black px-6 py-4 text-lg font-extrabold text-white transition-transform hover:bg-[#ec3013] " > Corriger </ button >< button type = " button " id = " g-new " class = " inline-flex items-center gap-3 border border-[#e3e5e8] rounded-2xl bg-white px-5 py-3 font-extrabold hover:bg-[#fef7e0] " > Nouvel exercice </ button ></ div >
< div id = " g-corr " hidden class = " mt-6 border border-[#e3e5e8] rounded-2xl p-5 " >< p id = " g-verdict " class = " text-xl font-extrabold " ></ 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 border-[#e3e5e8] rounded-2xl bg-[#fef7e0] p-7 " >< p class = " text-2xl font-extrabold " > 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 ( /^ 0 x / i , '' ), 16 ) === n && /^ ( 0 x ) ? [ 0 - 9 a - 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 border-[#e3e5e8] rounded-2xl bg-white px-4 py-3 text-lg font-semibold text-black placeholder:text-[#444141] focus:bg-[#f8f9fa] 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-[#e6f4ea]' , 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 border-[#e3e5e8] rounded-2xl p-5 ' + ( tout ? 'bg-[#e6f4ea]' : '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