/**
* 🧮 IHK SUBNETTING DRILL MASTER 🧮
* =================================
* Interaktiver Subnetting-Trainer für AP1 & AP2 (LF 3 & LF 9):
* - Endlose Zufallsaufgaben (FLSM & VLSM)
* - Magische Zahl (256 - Oktett) Erklärung
* - Automatische Validierung aller 6 Parameter
*/
class SubnettingDrill {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.currentTask = null;
this.stats = { total: 0, correct: 0, streak: 0 };
this.init();
}
init() {
if (!this.container) return;
this.render();
this.generateTask();
}
render() {
this.container.innerHTML = `
`;
document.getElementById("btn-check-drill").addEventListener("click", () => this.checkAnswer());
document.getElementById("btn-next-drill").addEventListener("click", () => this.generateTask());
document.getElementById("btn-magic-hint").addEventListener("click", () => this.toggleHint());
}
generateTask() {
// Generiere IP im privaten Bereich (Klasse A, B oder C)
const type = Math.floor(Math.random() * 3);
let ipParts = [];
let prefix = 24;
if (type === 0) {
ipParts = [192, 168, Math.floor(Math.random() * 100) + 1, Math.floor(Math.random() * 254) + 1];
prefix = Math.floor(Math.random() * 7) + 24; // /24 bis /30
} else if (type === 1) {
ipParts = [172, Math.floor(Math.random() * 16) + 16, Math.floor(Math.random() * 254) + 1, Math.floor(Math.random() * 254) + 1];
prefix = Math.floor(Math.random() * 9) + 20; // /20 bis /28
} else {
ipParts = [10, Math.floor(Math.random() * 254) + 1, Math.floor(Math.random() * 254) + 1, Math.floor(Math.random() * 254) + 1];
prefix = Math.floor(Math.random() * 12) + 18; // /18 bis /29
}
const ipInt = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];
const maskInt = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;
const netInt = (ipInt & maskInt) >>> 0;
const bcastInt = (netInt | ~maskInt) >>> 0;
const firstInt = (netInt + 1) >>> 0;
const lastInt = (bcastInt - 1) >>> 0;
const hostBits = 32 - prefix;
const usableHosts = Math.pow(2, hostBits) - 2;
const intToIp = (num) => [
(num >>> 24) & 255,
(num >>> 16) & 255,
(num >>> 8) & 255,
num & 255
].join(".");
this.currentTask = {
ip: ipParts.join("."),
prefix: prefix,
mask: intToIp(maskInt),
net: intToIp(netInt),
first: intToIp(firstInt),
last: intToIp(lastInt),
bcast: intToIp(bcastInt),
hosts: usableHosts,
magicNumber: 256 - ((maskInt >>> (8 * (3 - Math.floor(prefix / 8)))) & 255 || 256)
};
document.getElementById("drill-ip-display").textContent = `${this.currentTask.ip} / ${this.currentTask.prefix}`;
document.getElementById("ans-mask").value = "";
document.getElementById("ans-net").value = "";
document.getElementById("ans-first").value = "";
document.getElementById("ans-last").value = "";
document.getElementById("ans-bcast").value = "";
document.getElementById("ans-hosts").value = "";
const resBox = document.getElementById("drill-result");
resBox.classList.remove("active");
}
checkAnswer() {
if (!this.currentTask) return;
const ansMask = document.getElementById("ans-mask").value.trim();
const ansNet = document.getElementById("ans-net").value.trim();
const ansFirst = document.getElementById("ans-first").value.trim();
const ansLast = document.getElementById("ans-last").value.trim();
const ansBcast = document.getElementById("ans-bcast").value.trim();
const ansHosts = parseInt(document.getElementById("ans-hosts").value.trim(), 10);
const checks = [
{ name: "Subnetzmaske", correct: ansMask === this.currentTask.mask, expected: this.currentTask.mask, user: ansMask },
{ name: "Netzadresse", correct: ansNet === this.currentTask.net, expected: this.currentTask.net, user: ansNet },
{ name: "Erste Host-IP", correct: ansFirst === this.currentTask.first, expected: this.currentTask.first, user: ansFirst },
{ name: "Letzte Host-IP", correct: ansLast === this.currentTask.last, expected: this.currentTask.last, user: ansLast },
{ name: "Broadcast", correct: ansBcast === this.currentTask.bcast, expected: this.currentTask.bcast, user: ansBcast },
{ name: "Nutzbare Hosts", correct: ansHosts === this.currentTask.hosts, expected: this.currentTask.hosts, user: ansHosts }
];
const allCorrect = checks.every(c => c.correct);
this.stats.total++;
if (allCorrect) {
this.stats.correct++;
this.stats.streak++;
} else {
this.stats.streak = 0;
}
const pct = Math.round((this.stats.correct / this.stats.total) * 100);
document.getElementById("drill-stats").textContent = `${this.stats.correct}/${this.stats.total} gelöst (${pct}%)`;
document.getElementById("drill-streak").textContent = `🔥 Streak: ${this.stats.streak}`;
const resBox = document.getElementById("drill-result");
resBox.innerHTML = `
${allCorrect ? '🎉 Perfekt! Alle 6 Werte exakt berechnet.' : '❌ Noch nicht ganz richtig – überprüfe deine Werte:'}
| Parameter | Deine Eingabe | Musterlösung | Status |
${checks.map(c => `
| ${c.name} |
${c.user || 'leer'} |
${c.expected} |
${c.correct ? '✅ Richtig' : '❌ Falsch'} |
`).join('')}
`;
resBox.classList.add("active");
}
toggleHint() {
if (!this.currentTask) return;
const resBox = document.getElementById("drill-result");
const octetIndex = Math.floor(this.currentTask.prefix / 8);
const bitsInOctet = this.currentTask.prefix % 8;
const octetNum = octetIndex + (bitsInOctet > 0 ? 1 : 0);
resBox.innerHTML = `
💡 Das IHK-Geheimnis: Die „Magische Zahl“ (Schrittweite)
Für /${this.currentTask.prefix} liegt das interessante Oktett im ${octetNum}. Oktett.
1. Ermittle den Maskenwert: ${this.currentTask.mask}.
2. Die Schrittweite („Magische Zahl“) ist: 256 - Oktettwert.
3. Die Netzadressen steigen immer in Vielfachen dieser Schrittweite (0, Schrittweite, 2 × Schrittweite, ...) bis kurz vor deine gegebene IP.
4. Broadcast ist immer Nächste Netzadresse - 1.
`;
resBox.classList.add("active");
}
}
window.SubnettingDrill = SubnettingDrill;