pruetpong/Stat-Random-Question-Generator
0
1<!DOCTYPE html>2<html lang="th">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>โปรแกรมสุ่มข้อมูลนักเรียน (ส่วนสูง/น้ำหนัก)</title>7 <script src="https://cdn.tailwindcss.com"></script>8 <link href="https://fonts.googleapis.com/css2?family=Prompt:wght@300;400;600;700&display=swap" rel="stylesheet">9 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />10 <style>11 body { font-family: 'Prompt', sans-serif; background-color: #f0f9ff; }12 13 /* Animation Classes */14 .fade-in { animation: fadeIn 0.5s ease-in-out; }15 @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }16 17 .pop-in { animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); }18 @keyframes popIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }19 20 /* Card Interactions */21 .menu-card { transition: all 0.3s ease; cursor: pointer; }22 .menu-card:hover { transform: translateY(-5px); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); }23 24 .data-card { transition: all 0.3s ease; }25 .data-card:hover { transform: translateY(-2px); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }26 27 /* Dynamic Theme Classes */28 .theme-height-bg { background: linear-gradient(135deg, #2563eb 0%, #4f46e5 100%); } /* Blue/Indigo */29 .theme-height-text { color: #4f46e5; }30 .theme-height-border { border-color: #e0e7ff; }31 .theme-height-btn { background-color: #4f46e5; }32 .theme-height-btn:hover { background-color: #4338ca; }33 34 .theme-weight-bg { background: linear-gradient(135deg, #ea580c 0%, #dc2626 100%); } /* Orange/Red */35 .theme-weight-text { color: #c2410c; }36 .theme-weight-border { border-color: #ffedd5; }37 .theme-weight-btn { background-color: #ea580c; }38 .theme-weight-btn:hover { background-color: #c2410c; }39 </style>40</head>41<body class="min-h-screen flex flex-col items-center justify-center p-4 bg-gray-50">42 43 <!-- Main Container -->44 <div class="bg-white rounded-3xl shadow-2xl w-full max-w-3xl overflow-hidden border border-gray-100 relative min-h-[600px] flex flex-col">45 46 <!-- Header (Dynamic Color) -->47 <div id="mainHeader" class="p-6 text-center text-white relative shadow-lg transition-colors duration-500 theme-height-bg">48 <!-- Back Button (Hidden by default) -->49 <button id="backBtn" onclick="goBack()" class="hidden absolute top-6 left-6 text-white/80 hover:text-white hover:bg-white/20 px-3 py-1 rounded-full transition-all text-sm font-semibold">50 <i class="fa-solid fa-arrow-left mr-2"></i>เมนูหลัก51 </button>52 53 <h1 id="appTitle" class="text-3xl font-bold mb-2 tracking-wide mt-2">ระบบข้อมูลจำลองเพื่อการศึกษา</h1>54 <p id="appSubtitle" class="text-white/80 text-sm font-light">เลือกประเภทข้อมูลที่ต้องการสุ่ม</p>55 </div>56 57 <!-- Content Area -->58 <div class="flex-grow p-8 relative">59 60 <!-- 1. MENU SECTION -->61 <div id="menuSection" class="grid grid-cols-1 md:grid-cols-2 gap-6 h-full items-center fade-in">62 <!-- Height Menu -->63 <div onclick="selectMode('height')" class="menu-card bg-blue-50 border-2 border-blue-100 rounded-2xl p-8 text-center group">64 <div class="w-20 h-20 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-6 group-hover:bg-blue-200 transition-colors">65 <i class="fa-solid fa-ruler-vertical text-4xl text-blue-600"></i>66 </div>67 <h3 class="text-2xl font-bold text-gray-800 mb-2">ข้อมูลส่วนสูง</h3>68 <p class="text-gray-500 text-sm">ข้อมูลจำลอง 25 ชุด</p>69 </div>70 71 <!-- Weight Menu -->72 <div onclick="selectMode('weight')" class="menu-card bg-orange-50 border-2 border-orange-100 rounded-2xl p-8 text-center group">73 <div class="w-20 h-20 bg-orange-100 rounded-full flex items-center justify-center mx-auto mb-6 group-hover:bg-orange-200 transition-colors">74 <i class="fa-solid fa-weight-scale text-4xl text-orange-600"></i>75 </div>76 <h3 class="text-2xl font-bold text-gray-800 mb-2">ข้อมูลน้ำหนัก</h3>77 <p class="text-gray-500 text-sm">ข้อมูลจำลอง 30 ชุด</p>78 </div>79 </div>80 81 <!-- 2. GENERATOR SECTION (Hidden initially) -->82 <div id="generatorSection" class="hidden flex flex-col items-center w-full">83 84 <!-- Random Button -->85 <button onclick="randomizeAll()" id="randomBtn" class="group relative inline-flex items-center justify-center px-10 py-4 text-xl font-bold text-white transition-all duration-200 rounded-full shadow-lg hover:scale-105 active:scale-95 w-full md:w-auto mb-8 theme-height-btn">86 <span class="absolute top-0 right-0 w-4 h-4 -mt-1 -mr-1 bg-white rounded-full animate-ping opacity-75"></span>87 <i class="fa-solid fa-shuffle text-2xl mr-3 group-hover:rotate-180 transition-transform duration-500"></i>88 <span id="btnText">สุ่มห้องเรียน</span>89 </button>90 91 <!-- Loading -->92 <div id="loading" class="hidden text-gray-400 font-semibold mt-4">93 <i class="fa-solid fa-circle-notch fa-spin mr-2"></i>กำลังประมวลผล...94 </div>95 96 <!-- Result Display -->97 <div id="resultDisplay" class="hidden w-full fade-in">98 <!-- Room Badge -->99 <div class="flex justify-between items-end border-b pb-4 mb-4 border-gray-100">100 <div>101 <p class="text-gray-400 text-xs uppercase tracking-wider font-semibold">ห้องเรียน</p>102 <h2 id="roomResultText" class="text-4xl font-bold text-gray-800 leading-none mt-1">ม.1/1</h2>103 </div>104 <div class="text-right">105 <p class="text-gray-400 text-xs uppercase tracking-wider font-semibold">จำนวนข้อมูล</p>106 <p id="dataCountText" class="text-2xl font-bold text-gray-600">25</p>107 </div>108 </div>109 110 <!-- Data Grid -->111 <div class="relative">112 <p class="text-gray-400 text-xs mb-3 text-center w-full bg-gray-50 py-1 rounded">113 <span id="unitText">หน่วย: เซนติเมตร</span>114 </p>115 <div id="dataGrid" class="grid grid-cols-5 md:grid-cols-6 gap-3 text-center">116 <!-- Items generated by JS -->117 </div>118 </div>119 </div>120 </div>121 122 </div>123 </div>124 125 <script>126 // --- Configuration ---127 const config = {128 'm1': 4, 'm2': 4, 'm3': 4, 'm4': 6, 'm5': 5, 'm6': 5129 };130 131 // CONFIG: HEIGHT (Do not change - Legacy Compatibility)132 const HEIGHT_CONFIG = {133 count: 25,134 min: 125,135 max: 195136 };137 138 // CONFIG: WEIGHT (New Requirement)139 const WEIGHT_CONFIG = {140 count: 30,141 globalMin: 41,142 globalMax: 99,143 // Guaranteed intervals to ensure distribution144 intervals: [145 [41, 50], [51, 60], [61, 70], [71, 80], [81, 90], [91, 99]146 ]147 };148 149 let currentMode = null; // 'height' or 'weight'150 151 // --- RNG Core (Mulberry32) ---152 function mulberry32(a) {153 return function() {154 var t = a += 0x6D2B79F5;155 t = Math.imul(t ^ t >>> 15, t | 1);156 t ^= t + Math.imul(t ^ t >>> 7, t | 61);157 return ((t ^ t >>> 14) >>> 0) / 4294967296;158 }159 }160 161 function stringToSeed(str) {162 let hash = 0;163 for (let i = 0; i < str.length; i++) {164 let char = str.charCodeAt(i);165 hash = ((hash << 5) - hash) + char;166 hash = hash & hash;167 }168 return Math.abs(hash);169 }170 171 function shuffleArray(array) {172 let currentIndex = array.length, randomIndex;173 while (currentIndex != 0) {174 randomIndex = Math.floor(Math.random() * currentIndex);175 currentIndex--;176 [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];177 }178 return array;179 }180 181 // --- Data Generation Logic ---182 183 // 1. HEIGHT GENERATOR (Original Logic)184 function generateHeightData(rng) {185 let data = [];186 for (let i = 0; i < HEIGHT_CONFIG.count; i++) {187 const val = Math.floor(rng() * (HEIGHT_CONFIG.max - HEIGHT_CONFIG.min + 1)) + HEIGHT_CONFIG.min;188 data.push(val);189 }190 return data;191 }192 193 // 2. WEIGHT GENERATOR (New Distributed Logic)194 function generateWeightData(rng) {195 let data = [];196 const intervals = WEIGHT_CONFIG.intervals;197 198 // Step A: Guarantee at least one value in each decade (41-50, 51-60...91-99)199 // This ensures the "distribution" requirement is met.200 intervals.forEach(interval => {201 const min = interval[0];202 const max = interval[1];203 const val = Math.floor(rng() * (max - min + 1)) + min;204 data.push(val);205 });206 207 // Step B: Fill the rest (Count - 6) randomly across the whole range logic208 // To maintain distribution vibes, we pick a random interval first, then a number in it.209 const remainingCount = WEIGHT_CONFIG.count - intervals.length;210 211 for (let i = 0; i < remainingCount; i++) {212 // Pick a random interval index (0 to 5)213 const intervalIndex = Math.floor(rng() * intervals.length);214 const interval = intervals[intervalIndex];215 216 // Generate number in that interval217 const val = Math.floor(rng() * (interval[1] - interval[0] + 1)) + interval[0];218 data.push(val);219 }220 221 return data;222 }223 224 function getRoomData(level, roomNumber, mode) {225 // Seed string includes mode to ensure different sets for height/weight226 // But keeps consistent for the same room+mode227 let seedString = `${level}-${roomNumber}-biology-2024`;228 if (mode === 'weight') {229 seedString += "-weight-dist"; 230 }231 232 const seed = stringToSeed(seedString);233 const rng = mulberry32(seed);234 235 if (mode === 'height') {236 return generateHeightData(rng);237 } else {238 return generateWeightData(rng);239 }240 }241 242 // --- UI Functions ---243 244 function selectMode(mode) {245 currentMode = mode;246 247 // UI Toggle248 document.getElementById('menuSection').classList.add('hidden');249 document.getElementById('generatorSection').classList.remove('hidden');250 document.getElementById('backBtn').classList.remove('hidden');251 252 // Theme Setting253 const header = document.getElementById('mainHeader');254 const btn = document.getElementById('randomBtn');255 const title = document.getElementById('appTitle');256 const subtitle = document.getElementById('appSubtitle');257 258 // Reset Classes259 header.className = "p-6 text-center text-white relative shadow-lg transition-colors duration-500";260 btn.className = "group relative inline-flex items-center justify-center px-10 py-4 text-xl font-bold text-white transition-all duration-200 rounded-full shadow-lg hover:scale-105 active:scale-95 w-full md:w-auto mb-8";261 262 if (mode === 'height') {263 header.classList.add('theme-height-bg');264 btn.classList.add('theme-height-btn');265 title.innerHTML = '<i class="fa-solid fa-ruler-vertical mr-2"></i>สุ่มข้อมูลส่วนสูง';266 subtitle.innerText = "แบบจำลองข้อมูล 25 คน";267 document.getElementById('btnText').innerText = "สุ่มห้องเรียน (ส่วนสูง)";268 } else {269 header.classList.add('theme-weight-bg');270 btn.classList.add('theme-weight-btn');271 title.innerHTML = '<i class="fa-solid fa-weight-scale mr-2"></i>สุ่มข้อมูลน้ำหนัก';272 subtitle.innerText = "แบบจำลองข้อมูล 30 คน";273 document.getElementById('btnText').innerText = "สุ่มห้องเรียน (น้ำหนัก)";274 }275 }276 277 function goBack() {278 // Hide Generator, Show Menu279 document.getElementById('generatorSection').classList.add('hidden');280 document.getElementById('menuSection').classList.remove('hidden');281 document.getElementById('backBtn').classList.add('hidden');282 283 // Reset Results284 document.getElementById('resultDisplay').classList.add('hidden');285 document.getElementById('randomBtn').disabled = false;286 document.getElementById('randomBtn').classList.remove('opacity-50');287 288 // Reset Header to Neutral or keep last state (Keeping last state looks fine)289 document.getElementById('appTitle').innerText = "ระบบข้อมูลจำลองเพื่อการศึกษา";290 document.getElementById('appSubtitle').innerText = "เลือกประเภทข้อมูลที่ต้องการสุ่ม";291 }292 293 function randomizeAll() {294 const btn = document.getElementById('randomBtn');295 const resultDisplay = document.getElementById('resultDisplay');296 const loading = document.getElementById('loading');297 298 // Loading State299 btn.disabled = true;300 btn.classList.add('opacity-50', 'cursor-not-allowed');301 resultDisplay.classList.add('hidden');302 loading.classList.remove('hidden');303 304 setTimeout(() => {305 performRandomization();306 loading.classList.add('hidden');307 resultDisplay.classList.remove('hidden');308 btn.disabled = false;309 btn.classList.remove('opacity-50', 'cursor-not-allowed');310 }, 500);311 }312 313 function performRandomization() {314 // 1. Random Room315 const levels = Object.keys(config);316 const randomLevelKey = levels[Math.floor(Math.random() * levels.length)];317 const maxRoom = config[randomLevelKey];318 const randomRoomNum = Math.floor(Math.random() * maxRoom) + 1;319 320 // 2. Get Data based on Mode321 let rawData = getRoomData(randomLevelKey, randomRoomNum, currentMode);322 323 // 3. Shuffle for display324 let shuffledData = shuffleArray([...rawData]);325 326 // 4. Render327 const levelText = randomLevelKey.replace('m', 'ม.');328 document.getElementById('roomResultText').innerText = `${levelText}/${randomRoomNum}`;329 document.getElementById('dataCountText').innerText = shuffledData.length;330 331 const grid = document.getElementById('dataGrid');332 grid.innerHTML = '';333 334 // Theme classes for cards335 let cardClass = currentMode === 'height' 336 ? 'bg-white text-indigo-700 border-indigo-100 theme-height-border' 337 : 'bg-white text-orange-700 border-orange-100 theme-weight-border';338 339 document.getElementById('unitText').innerText = currentMode === 'height' ? 'หน่วย: เซนติเมตร (ซม.)' : 'หน่วย: กิโลกรัม (กก.)';340 341 shuffledData.forEach((val, index) => {342 const div = document.createElement('div');343 div.className = `data-card p-3 rounded-xl border-2 font-bold shadow-sm pop-in flex items-center justify-center h-14 text-xl ${cardClass}`;344 div.style.animationDelay = `${index * 20}ms`;345 div.innerText = val;346 grid.appendChild(div);347 });348 }349 </script>350</body>351</html>