ฝึกคำนวณIP

IP Subnetting Lab
IP

SUBNET LAB

NETWORK ENGINEER TRAINING SIMULATION v3.0

SYSTEM ONLINE
// OPERATOR INIT
ชื่อผู้ปฏิบัติการ
ระดับความยาก
🟢
EASY
/24 networks
🟡
MEDIUM
/22–/24
🔴
HARD
/16–/24
▶  เริ่มปฏิบัติการ
Operator—
Mission1/5
LevelEASY
Score0
MISSION PROGRESS20%
🧮  คำนวณ 🖥  กำหนด IP
// MISSION
—
Network
—
Subnets
—
Original Mask
—
// STEP 1 — ตารางค่า Bit
อ้างอิงค่าบิต (Octet สุดท้าย)
💡 ตารางนี้แสดงค่าของแต่ละบิตในออกเตตสุดท้าย ใช้อ้างอิงตลอดการคำนวณ
ถัดไป →
▶  โจทย์ถัดไป 🏆  ดูผลคะแนน
0
/ 500
—

// MISSION REPORT
📋  Export รายงาน ↺  เล่นใหม่
`} function showEl(id){document.getElementById(id).classList.remove('hidden')} function hideEl(id){document.getElementById(id).classList.add('hidden')} /* ════════════════════════════════════════ DIFF CARDS ════════════════════════════════════════ */ document.querySelectorAll('.diff-card').forEach(b=>{ b.addEventListener('click',()=>{ document.querySelectorAll('.diff-card').forEach(x=>x.classList.remove('sel')); b.classList.add('sel'); difficulty=b.dataset.d; }); }); /* ════════════════════════════════════════ GENERATE QUESTIONS ════════════════════════════════════════ */ function generateQuestions(){ const pools=[ {net:'192.168.1.0',cidr:24,subs:4,name:'Office Network'}, {net:'192.168.10.0',cidr:24,subs:8,name:'Department'}, {net:'10.0.0.0',cidr:24,subs:4,name:'Branch'}, {net:'172.16.0.0',cidr:24,subs:2,name:'Floor'}, {net:'192.168.100.0',cidr:24,subs:4,name:'Building'}, {net:'192.168.50.0',cidr:24,subs:8,name:'Campus Zone'}, {net:'10.10.0.0',cidr:22,subs:4,name:'Campus'}, {net:'172.20.0.0',cidr:22,subs:8,name:'Site HQ'}, {net:'192.168.200.0',cidr:24,subs:4,name:'Data Center'}, {net:'10.0.0.0',cidr:16,subs:4,name:'Region'}, {net:'172.30.0.0',cidr:22,subs:16,name:'Enterprise'}, {net:'10.20.0.0',cidr:24,subs:4,name:'Factory'}, ]; const pool=difficulty==='easy'?pools.slice(0,6):difficulty==='med'?pools.slice(3,9):pools.slice(6); questions=[]; const used=new Set(); while(questions.length<5){ let idx=ri(0,pool.length-1); if(used.has(idx)&&pool.length>5)continue; used.add(idx); const p=pool[idx]; const bitsNeeded=Math.ceil(Math.log2(p.subs)); const newCidr=p.cidr+bitsNeeded; const subSize=Math.pow(2,32-newCidr); const baseNum=ip2n(p.net); const subnets=[]; for(let s=0;ss.classList.remove('active'));document.getElementById(id).classList.add('active')} /* ════════════════════════════════════════ LOAD QUESTION ════════════════════════════════════════ */ function loadQuestion(){ const q=questions[currentQ]; currentStep=1;currentSubnetIdx=0;subnetAnswers=[];machineData={};hintUsed=false;questionDone=false; document.getElementById('hud-q').textContent=`${currentQ+1}/5`; document.getElementById('prog-fill').style.width=((currentQ+1)/5*100)+'%'; document.getElementById('prog-pct').textContent=((currentQ+1)/5*100).toFixed(0)+'%'; document.getElementById('q-title').textContent=q.title; document.getElementById('q-network').textContent=q.network+'/'+q.cidr; document.getElementById('q-subs').textContent=q.subsNeeded+' subnets'; document.getElementById('q-origmask').textContent=q.mask; document.getElementById('q-tags').innerHTML=`${q.bitsNeeded} bits ยืม ${q.subnets[0].hosts} hosts/subnet`; renderNetVis(q); renderAssignMachines(q); buildStepTrail(); setupStep1(q); ['step2','step3','step4','step5','step6','step6-done'].forEach(id=>document.getElementById(id).classList.add('hidden')); showEl('step1'); ['s2-fb','s3-fb','s4-fb','s5-fb','s6-fb'].forEach(id=>document.getElementById(id).innerHTML=''); document.getElementById('assign-fb').innerHTML=''; hideEl('btn-next');hideEl('btn-finish'); switchTab('calc'); buildStepTrail(); } /* ════════════════════════════════════════ NET VIS ════════════════════════════════════════ */ function renderNetVis(q){ const vis=document.getElementById('net-vis');vis.innerHTML=''; q.subnets.forEach((s,i)=>{ const c=SUB_COLORS[i%SUB_COLORS.length]; const d=document.createElement('div');d.className='sub-blk pending';d.id='sblk-'+i; d.style.cssText=`background:${c.bg};border-color:${c.border};color:${c.text}`; d.textContent=`แผนก${i+1}: ${s.network}/${s.cidr}`;d.title=`${s.first}–${s.last}`; vis.appendChild(d); }); } /* ════════════════════════════════════════ STEP TRAIL ════════════════════════════════════════ */ const stepLabels=['ตาราง Bit','Binary Mask','หา n','CIDR ใหม่','Hosts','IP Range']; function buildStepTrail(){ const trail=document.getElementById('step-trail');trail.innerHTML=''; stepLabels.forEach((lbl,i)=>{ const sn=i+1; const node=document.createElement('div');node.className='step-node'; const circ=document.createElement('div');circ.className='step-circle';circ.id='sc-'+sn;circ.textContent=sn; if(sn{ const sn=i+1; const circ=document.getElementById('sc-'+sn);const line=document.getElementById('sl-'+sn); if(!circ)return; circ.className='step-circle'; if(sn{ const th=document.createElement('th');th.textContent=v;head.appendChild(th); const td=document.createElement('td');td.innerHTML=`—`;body.appendChild(td); }); // Show which bits are 1 for the last octet of the ORIGINAL mask const lastOctetDec=parseInt(q.mask.split('.')[3]); const bin=toBin(lastOctetDec); Array.from(body.children).forEach((td,i)=>{td.querySelector('.bit-cell').textContent=bin[i];td.querySelector('.bit-cell').style.color=bin[i]==='1'?'var(--cyan)':'var(--text3)';}); } function goStep(n){ currentStep=n; for(let i=1;i<=6;i++) document.getElementById('step'+i)?.classList.add('hidden'); hideEl('step6-done'); showEl('step'+n); updateStepTrail(); document.getElementById('step'+n)?.scrollIntoView({behavior:'smooth',block:'nearest'}); } /* ════════════════════════════════════════ STEP 2 — Binary of original mask ════════════════════════════════════════ */ function setupStep2(){ const q=questions[currentQ]; document.getElementById('s2-cidr-show').textContent='/'+q.cidr; document.getElementById('s2-mask-show').textContent=q.mask; const lastOctetDec=parseInt(q.mask.split('.')[3]); const prefix=q.mask.split('.').slice(0,3).map(x=>toBin(parseInt(x))).join('.')+'.'; document.getElementById('s2-prefix').textContent=prefix; document.getElementById('s2-last-octet').value=''; document.getElementById('s2-fb').innerHTML=''; } function checkStep2(){ const q=questions[currentQ]; const lastOctetDec=parseInt(q.mask.split('.')[3]); const correct=toBin(lastOctetDec); const got=document.getElementById('s2-last-octet').value.trim(); if(got===correct){ setFb('s2-fb','ok',`✔ ถูกต้อง! ${correct} = ${lastOctetDec} ทศนิยม`); score+=10;document.getElementById('hud-score').textContent=score; setTimeout(()=>goStep(3),700); } else { setFb('s2-fb','err',`✘ ไม่ถูกต้อง กรอก: "${got}" — ถูกต้องคือ "${correct}"\n💡 ${lastOctetDec}₁₀ = ${correct}₂`); } } /* ════════════════════════════════════════ STEP 3 — Bits to borrow (n) ════════════════════════════════════════ */ function setupStep3(){ const q=questions[currentQ]; document.getElementById('s3-subs-needed').textContent=q.subsNeeded; document.getElementById('s3-n-answer').textContent=q.bitsNeeded; const opts=document.getElementById('s3-options');opts.innerHTML=''; // generate wrong options const correct=q.bitsNeeded; const choices=new Set([correct]); while(choices.size<4){const w=ri(Math.max(1,correct-2),correct+2);choices.add(w);} const arr=Array.from(choices).sort((a,b)=>a-b); arr.forEach(v=>{ const opt=document.createElement('label');opt.className='radio-opt'; opt.innerHTML=` n = ${v}  (2${v} = ${Math.pow(2,v)})`; opt.addEventListener('click',()=>{document.querySelectorAll('.radio-opt').forEach(x=>x.classList.remove('selected'));opt.classList.add('selected');checkStep3(v);}); opts.appendChild(opt); }); document.getElementById('s3-fb').innerHTML=''; hideEl('s3-next'); } function checkStep3(val){ const q=questions[currentQ]; if(parseInt(val)===q.bitsNeeded){ setFb('s3-fb','ok',`✔ ถูกต้อง! 2${q.bitsNeeded} = ${Math.pow(2,q.bitsNeeded)} ≥ ${q.subsNeeded} subnets`); score+=15;document.getElementById('hud-score').textContent=score; showEl('s3-next'); } else { setFb('s3-fb','err',`✘ ไม่ถูกต้อง 2${val} = ${Math.pow(2,val)} — ต้องการ ≥ ${q.subsNeeded}`); } } /* ════════════════════════════════════════ STEP 4 — New CIDR & Mask ════════════════════════════════════════ */ function setupStep4(){ const q=questions[currentQ]; document.getElementById('s4-orig-cidr').textContent=q.cidr; document.getElementById('s4-new-cidr').textContent=q.newCidr; const newLastDec=parseInt(cidrMask(q.newCidr).split('.')[3]); const prefix=cidrMask(q.newCidr).split('.').slice(0,3).map(x=>toBin(parseInt(x))).join('.')+'.'; document.getElementById('s4-prefix').textContent=prefix; ['s4-cidr-input','s4-mask-input','s4-last-octet'].forEach(id=>document.getElementById(id).value=''); document.getElementById('s4-fb').innerHTML=''; } function checkStep4(){ const q=questions[currentQ]; const cidrIn=document.getElementById('s4-cidr-input').value.trim().replace('/',''); const maskIn=document.getElementById('s4-mask-input').value.trim(); const binIn=document.getElementById('s4-last-octet').value.trim(); const newLastDec=parseInt(cidrMask(q.newCidr).split('.')[3]); const correctBin=toBin(newLastDec); const cidrOk=parseInt(cidrIn)===q.newCidr||cidrIn===('/'+q.newCidr); const maskOk=maskIn===cidrMask(q.newCidr); const binOk=binIn===correctBin; const lines=[]; if(cidrOk)lines.push(`✔ CIDR: /${q.newCidr}`); else lines.push(`✘ CIDR: กรอก "${cidrIn}" → ถูก: /${q.newCidr}`); if(maskOk)lines.push(`✔ Mask: ${cidrMask(q.newCidr)}`); else lines.push(`✘ Mask: กรอก "${maskIn}" → ถูก: ${cidrMask(q.newCidr)}`); if(binOk)lines.push(`✔ Binary: ${correctBin}`); else lines.push(`✘ Binary: กรอก "${binIn}" → ถูก: ${correctBin} (=${newLastDec})`); const allOk=cidrOk&&maskOk&&binOk; setFb('s4-fb',allOk?'ok':'err',lines.join('
')); if(allOk){score+=20;document.getElementById('hud-score').textContent=score;setTimeout(()=>goStep(5),700);} } /* ════════════════════════════════════════ STEP 5 — Hosts ════════════════════════════════════════ */ function setupStep5(){ const q=questions[currentQ]; document.getElementById('s5-h').textContent=q.hostBits; document.getElementById('s5-hosts').textContent=q.subnets[0].hosts; ['s5-h-input','s5-hosts-input'].forEach(id=>document.getElementById(id).value=''); document.getElementById('s5-fb').innerHTML=''; } function checkStep5(){ const q=questions[currentQ]; const hIn=document.getElementById('s5-h-input').value.trim(); const hostsIn=document.getElementById('s5-hosts-input').value.trim(); const hOk=parseInt(hIn)===q.hostBits; const hostsOk=parseInt(hostsIn)===q.subnets[0].hosts; const lines=[]; if(hOk)lines.push(`✔ h = ${q.hostBits} (host bits)`); else lines.push(`✘ h: กรอก "${hIn}" → ถูก: ${q.hostBits}`); if(hostsOk)lines.push(`✔ Hosts = 2${q.hostBits} − 2 = ${q.subnets[0].hosts}`); else lines.push(`✘ Hosts: กรอก "${hostsIn}" → ถูก: ${q.subnets[0].hosts}`); setFb('s5-fb',hOk&&hostsOk?'ok':'err',lines.join('
')); if(hOk&&hostsOk){score+=15;document.getElementById('hud-score').textContent=score;setTimeout(()=>goStep(6),700);} } /* ════════════════════════════════════════ STEP 6 — Subnet Ranges ════════════════════════════════════════ */ function setupStep6(){ const q=questions[currentQ]; document.getElementById('s6-current-num').textContent=currentSubnetIdx+1; document.getElementById('s6-total-num').textContent=q.subnets.length; document.getElementById('s6-block').textContent=q.subnets[0].blockSize; document.getElementById('s6-mask-ref').textContent=cidrMask(q.newCidr)+'/'+q.newCidr; ['s6-net','s6-first','s6-last','s6-bcast','s6-smask'].forEach(id=>document.getElementById(id).value=''); document.getElementById('s6-fb').innerHTML=''; hideEl('s6-hint-box'); hintUsed=false; updateProgressTags(); } function updateProgressTags(){ const q=questions[currentQ]; const cont=document.getElementById('s6-progress-tags');cont.innerHTML=''; q.subnets.forEach((_,i)=>{ const span=document.createElement('span');span.className='tag '+(icurrentSubnetIdx?'0.3':'1'; span.textContent=`แผนก${i+1}`;cont.appendChild(span); }); } function showHint6(){ hintUsed=true; const s=questions[currentQ].subnets[currentSubnetIdx]; const hb=document.getElementById('s6-hint-box'); hb.innerHTML=`⚡ คำใบ้: Network=${s.network} | First=${s.first} | Last=${s.last} | Broadcast=${s.broadcast} (−50% คะแนน)`; showEl('s6-hint-box'); } function checkStep6(){ const q=questions[currentQ]; const s=q.subnets[currentSubnetIdx]; const net=document.getElementById('s6-net').value.trim(); const first=document.getElementById('s6-first').value.trim(); const last=document.getElementById('s6-last').value.trim(); const bcast=document.getElementById('s6-bcast').value.trim(); const smask=document.getElementById('s6-smask').value.trim(); let pts=0;const lines=[]; const chk=(got,correct,label)=>{ if(got===correct){pts+=8;lines.push(`✔ ${label}: ${got}`);} else lines.push(`✘ ${label}: "${got}" → ถูก: ${correct}`); }; chk(net,s.network,'Network Address'); chk(first,s.first,'First Usable IP'); chk(last,s.last,'Last Usable IP'); chk(bcast,s.broadcast,'Broadcast'); chk(smask,s.mask,'Subnet Mask'); const earned=hintUsed?Math.floor(pts*.5):pts; score+=earned;document.getElementById('hud-score').textContent=score; const ok=pts===40; setFb('s6-fb',ok?'ok':pts>0?'info':'err',lines.join('
')+`
+${earned} pts${hintUsed?' (hint −50%)':''}`); // Mark vis block const blk=document.getElementById('sblk-'+currentSubnetIdx); if(blk){blk.classList.remove('pending');blk.classList.add('done');} subnetAnswers.push({idx:currentSubnetIdx,ok,pts:earned,s}); currentSubnetIdx++; if(currentSubnetIdx>=q.subnets.length){ setTimeout(showStep6Done,800); } else { document.getElementById('s6-current-num').textContent=currentSubnetIdx+1; setTimeout(()=>{setupStep6();},800); } } function skipSubnet6(){ const q=questions[currentQ]; const s=q.subnets[currentSubnetIdx]; subnetAnswers.push({idx:currentSubnetIdx,ok:false,pts:0,s}); const blk=document.getElementById('sblk-'+currentSubnetIdx);if(blk)blk.classList.add('done'); currentSubnetIdx++; if(currentSubnetIdx>=q.subnets.length) showStep6Done(); else{document.getElementById('s6-current-num').textContent=currentSubnetIdx+1;setupStep6();} } function showStep6Done(){ hideEl('step6'); const q=questions[currentQ]; let html=''; subnetAnswers.forEach((a,i)=>{ const c=SUB_COLORS[i%SUB_COLORS.length]; html+=`
แผนกที่ ${i+1}
${a.pts} pts
Network Address${a.s.network}
First Usable IP${a.s.first}
Last Usable IP${a.s.last}
Broadcast Address${a.s.broadcast}
Subnet Mask${a.s.mask}/${a.s.cidr}
`; }); document.getElementById('s6-summary').innerHTML=html; showEl('step6-done'); } /* ════════════════════════════════════════ SHOW NEXT BTN (after subnetting done) ════════════════════════════════════════ */ function showNextBtn(){ allResults.push({q:questions[currentQ],answers:subnetAnswers,machines:JSON.parse(JSON.stringify(machineData))}); if(currentQ<4)showEl('btn-next'); else showEl('btn-finish'); } /* ════════════════════════════════════════ ASSIGN MACHINES ════════════════════════════════════════ */ function renderAssignMachines(q){ const cont=document.getElementById('assign-machines');cont.innerHTML=''; q.subnets.forEach((s,i)=>{ const mac1=genMac(),mac2=genMac(); machineData['server_'+i]={mac:mac1};machineData['client_'+i]={mac:mac2}; const c=SUB_COLORS[i%SUB_COLORS.length]; const wrap=document.createElement('div');wrap.className='machine-wrap'; wrap.style.borderColor=c.border; wrap.innerHTML=`
SUBNET แผนก ${i+1}
${s.network}/${s.cidr} | ${s.first}–${s.last} | Hosts: ${s.hosts}
SERVER
MAC: ${mac1}
IP Address
Subnet Mask
Default Gateway
CLIENT PC
MAC: ${mac2}
IP Address
Subnet Mask
Default Gateway
`; cont.appendChild(wrap); }); } function checkAssign(){ const q=questions[currentQ];let totalPts=0; q.subnets.forEach((s,i)=>{ const srvIp=document.getElementById(`srv-ip-${i}`)?.value.trim(); const srvMask=document.getElementById(`srv-mask-${i}`)?.value.trim(); const srvGw=document.getElementById(`srv-gw-${i}`)?.value.trim(); const cliIp=document.getElementById(`cli-ip-${i}`)?.value.trim(); const cliMask=document.getElementById(`cli-mask-${i}`)?.value.trim(); const srvNum=ip2n(srvIp||'0.0.0.0'),cliNum=ip2n(cliIp||'0.0.0.0'); const netNum=ip2n(s.network),bcastNum=ip2n(s.broadcast); const srvOk=srvNum>netNum&&srvNumnetNum&&cliNumแผนก${i+1}: Server ${srvOk?'✔':'✘'} | Client ${cliOk?'✔':'✘'} | Mask ${maskOk1&&maskOk2?'✔':'✘'} → +${pts} pts`; }); score+=totalPts;document.getElementById('hud-score').textContent=score; document.getElementById('assign-fb').innerHTML=`
+${totalPts} pts จากการกำหนด IP
`; // update last result's machines if(allResults.length>0)allResults[allResults.length-1].machines=JSON.parse(JSON.stringify(machineData)); } /* ══════_
แชร์ LINE