CoolFace
Apppublic

pruetpong/Stat-Random-Question-Generator

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
index.html351 linesDownload Raw Back to root
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>