1
0
mirror of https://github.com/tzuwei-huang/random-number-generator.git synced 2026-08-24 14:10:29 +09:00

305 lines
15 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fair Random Number Generator</title>
<style>
body { font-family: sans-serif; text-align: center; margin: 20px 0; background-color: #f4f7f6; font-size: 1.2em; }
.container { width: 98%; max-width: 98%; margin: 0 auto; padding: 40px 20px; background: white; border: 1px solid #eee; border-radius: 15px; box-shadow: 0 10px 25px rgba(0,0,0,0.05); position: relative; box-sizing: border-box; }
.lang-switch { position: absolute; top: 20px; right: 20px; }
.lang-link { text-decoration: none; color: #3498db; font-size: 1.2em; font-weight: bold; cursor: pointer; }
h1 { color: #2c3e50; margin-bottom: 30px; font-size: 3em; }
.result { font-size: 16em; font-weight: bold; color: #e74c3c; margin: 30px 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.1); }
.info { font-size: 2em; color: #7f8c8d; margin-bottom: 30px; }
button { padding: 20px 40px; font-size: 2em; cursor: pointer; transition: all 0.2s; }
button:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); }
button:active { transform: translateY(0); }
.reset-btn { background-color: #f8f9fa; border: 1px solid #dee2e6; color: #6c757d; text-decoration: none; padding: 15px 30px; border-radius: 6px; display: inline-block; margin-top: 20px; font-size: 1.3em; cursor: pointer; }
.reset-btn:hover { background-color: #e9ecef; }
.generate-btn { background-color: #2ecc71; border: none; color: white; border-radius: 6px; font-weight: bold; }
.generate-btn:hover { background-color: #27ae60; }
input[type="number"] { padding: 15px; border: 2px solid #edeff2; border-radius: 8px; width: 150px; outline: none; font-size: 2em; text-align: center; }
input[type="number"]:focus { border-color: #3498db; }
label { font-size: 2em; color: #34495e; margin-right: 10px; }
.drawn-list { margin-top: 40px; text-align: left; background: #fdfdfd; padding: 25px; border: 1px dashed #dcdde1; border-radius: 10px; }
.drawn-numbers { font-size: 3em; font-weight: bold; color: #34495e; word-wrap: break-word; line-height: 1.2; margin-top: 10px; }
.mode-container { margin: 10px 0; font-size: 1.2em; color: #7f8c8d; }
.mode-badge { padding: 5px 15px; border-radius: 20px; font-weight: bold; background-color: #ecf0f1; color: #34495e; margin: 0 10px; }
.mode-toggle-btn { background-color: #3498db; border: none; color: white; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 1em; text-decoration: none; display: inline-block; transition: background 0.2s; }
.mode-toggle-btn:hover { background-color: #2980b9; }
.repeat-badge { background-color: #ff9f43; color: white; padding: 5px 15px; border-radius: 50px; font-size: 0.15em; vertical-align: middle; margin-left: 10px; box-shadow: 0 4px 10px rgba(230, 126, 34, 0.3); animation: bounce 0.5s ease infinite alternate; }
@keyframes bounce { from { transform: translateY(0); } to { transform: translateY(-5px); } }
.hidden { display: none; }
</style>
</head>
<body>
<div class="container">
<a href="https://github.com/tzuwei-huang/random-number-generator" target="_blank" style="position: absolute; top: 15px; left: 20px;">
<svg width="36" height="36" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61-.546-1.387-1.333-1.756-1.333-1.756-1.09-.745.083-.73.083-.73 1.205.085 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 21.795 24 17.295 24 12c0-6.63-5.37-12-12-12z"/>
</svg>
</a>
<div class="lang-switch">
<a id="lang-toggle" class="lang-link">繁體中文</a>
</div>
<h1 id="title">🎲 Random Picker</h1>
<div id="setup-view">
<div class="mode-container">
<span id="mode-text">Mode:</span>
<span id="mode-badge" class="mode-badge">Fair (No Repeat)</span>
<button id="toggle-mode-setup" class="mode-toggle-btn">Switch Mode</button>
</div>
<p id="enter-range" style="font-size: 2em; color: #34495e;">Enter the range:</p>
<label id="label-min">Min:</label>
<input type="number" id="min-val" value="1" min="0">
<label id="label-max">Max:</label>
<input type="number" id="max-val" value="100" min="0">
<br><br>
<button id="start-btn" class="generate-btn">Start</button>
</div>
<div id="active-view" class="hidden">
<div class="mode-container">
<span id="mode-text-active">Mode:</span>
<span id="mode-badge-active" class="mode-badge">Fair (No Repeat)</span>
<button id="toggle-mode-active" class="mode-toggle-btn">Switch Mode</button>
</div>
<div class="info">
<span id="range-text">Range:</span> <span id="display-range"></span><br>
<span id="remaining-text">Remaining:</span> <span id="remaining-count"></span>
</div>
<div class="result">
<span id="result-display">?</span>
<span id="repeat-badge" class="repeat-badge hidden">Repeat!</span>
</div>
<div id="action-buttons">
<button id="pick-btn" class="generate-btn">Pick One</button>
<div id="all-picked-msg" class="hidden">
<p style="font-size: 2em; color: #2ecc71; font-weight: bold;" id="all-picked-text">🎉 All numbers picked!</p>
<button id="restart-btn" class="generate-btn">Restart</button>
</div>
</div>
<a id="reset-btn" class="reset-btn">Reset</a>
<div id="drawn-container" class="drawn-list hidden">
<strong id="drawn-title" style="font-size: 2em;">Drawn Numbers:</strong>
<div id="drawn-numbers" class="drawn-numbers"></div>
</div>
</div>
</div>
<script>
const TRANSLATIONS = {
"en": {
"title": "🎲 Random Picker",
"enter_range": "Enter the range:",
"min": "Min:",
"max": "Max:",
"start": "Start",
"range": "Range:",
"remaining": "Remaining:",
"pick_one": "Pick One",
"all_picked": "🎉 All numbers picked!",
"restart": "Restart",
"reset": "Reset",
"reset_range": "Reset Range",
"drawn_numbers": "Drawn Numbers:",
"lang_toggle": "繁體中文",
"mode": "Mode:",
"mode_fair": "Fair (No Repeat)",
"mode_random": "Pure Random",
"toggle_mode": "Switch Mode",
"repeat": "Repeat!"
},
"zh": {
"title": "🎲 隨機抽號器",
"enter_range": "輸入範圍:",
"min": "最小值:",
"max": "最大值:",
"start": "開始",
"range": "範圍:",
"remaining": "剩餘數量:",
"pick_one": "抽一個",
"all_picked": "🎉 全部抽完啦!",
"restart": "重新開始",
"reset": "重置",
"reset_range": "重新設定範圍",
"drawn_numbers": "已抽出的號碼:",
"lang_toggle": "English",
"mode": "模式:",
"mode_fair": "公平模式 (不重複)",
"mode_random": "純隨機模式",
"toggle_mode": "切換模式",
"repeat": "重複!"
}
};
let currentLang = 'zh';
let pool = [];
let drawn = [];
let currentRange = [1, 100];
let isActive = false;
let mode = 'fair';
let isRepeat = false;
function updateUI() {
const t = TRANSLATIONS[currentLang];
document.getElementById('lang-toggle').innerText = t.lang_toggle;
document.getElementById('title').innerText = t.title;
document.getElementById('enter-range').innerText = t.enter_range;
document.getElementById('label-min').innerText = t.min;
document.getElementById('label-max').innerText = t.max;
document.getElementById('start-btn').innerText = t.start;
document.getElementById('range-text').innerText = t.range;
document.getElementById('remaining-text').innerText = t.remaining;
document.getElementById('pick-btn').innerText = t.pick_one;
document.getElementById('all-picked-text').innerText = t.all_picked;
document.getElementById('restart-btn').innerText = t.restart;
document.getElementById('drawn-title').innerText = t.drawn_numbers;
const modeLabel = mode === 'fair' ? t.mode_fair : t.mode_random;
document.getElementById('mode-text').innerText = t.mode;
document.getElementById('mode-badge').innerText = modeLabel;
document.getElementById('toggle-mode-setup').innerText = t.toggle_mode;
document.getElementById('mode-text-active').innerText = t.mode;
document.getElementById('mode-badge-active').innerText = modeLabel;
document.getElementById('toggle-mode-active').innerText = t.toggle_mode;
document.getElementById('repeat-badge').innerText = t.repeat;
if (isActive) {
document.getElementById('setup-view').classList.add('hidden');
document.getElementById('active-view').classList.remove('hidden');
document.getElementById('display-range').innerText = `${currentRange[0]} ~ ${currentRange[1]}`;
document.getElementById('remaining-count').innerText = pool.length;
if (mode === 'fair') {
if (pool.length > 0) {
document.getElementById('pick-btn').classList.remove('hidden');
document.getElementById('all-picked-msg').classList.add('hidden');
document.getElementById('reset-btn').innerText = t.reset;
} else {
document.getElementById('pick-btn').classList.add('hidden');
document.getElementById('all-picked-msg').classList.remove('hidden');
document.getElementById('reset-btn').innerText = t.reset_range;
}
} else {
document.getElementById('pick-btn').classList.remove('hidden');
document.getElementById('all-picked-msg').classList.add('hidden');
document.getElementById('reset-btn').innerText = t.reset;
}
const padding = String(currentRange[1]).length;
const formattedDrawn = drawn.map(n => String(n).padStart(padding, '0'));
document.getElementById('drawn-numbers').innerText = formattedDrawn.join(', ');
if (drawn.length > 0) {
document.getElementById('drawn-container').classList.remove('hidden');
const lastNumber = drawn[0];
document.getElementById('result-display').innerText = String(lastNumber).padStart(padding, '0');
if (isRepeat) {
document.getElementById('repeat-badge').classList.remove('hidden');
} else {
document.getElementById('repeat-badge').classList.add('hidden');
}
} else {
document.getElementById('drawn-container').classList.add('hidden');
document.getElementById('result-display').innerText = '?';
document.getElementById('repeat-badge').classList.add('hidden');
}
} else {
document.getElementById('setup-view').classList.remove('hidden');
document.getElementById('active-view').classList.add('hidden');
}
}
function getRandomInt(min, max) {
const range = max - min + 1;
const array = new Uint32Array(1);
window.crypto.getRandomValues(array);
return (array[0] % range) + min;
}
function toggleMode() {
mode = mode === 'fair' ? 'random' : 'fair';
updateUI();
}
document.getElementById('toggle-mode-setup').onclick = toggleMode;
document.getElementById('toggle-mode-active').onclick = toggleMode;
document.getElementById('lang-toggle').onclick = () => {
currentLang = currentLang === 'en' ? 'zh' : 'en';
updateUI();
};
document.getElementById('start-btn').onclick = () => {
const min = parseInt(document.getElementById('min-val').value);
const max = parseInt(document.getElementById('max-val').value);
if (isNaN(min) || isNaN(max) || min > max) {
alert(currentLang === 'en' ? "Invalid range!" : "無效的範圍!");
return;
}
if (max - min + 1 > 10000) {
alert(currentLang === 'en' ? "Range too large (max 10000)!" : "範圍太大 (最大 10000)");
return;
}
currentRange = [min, max];
initPool(min, max);
isActive = true;
pickOne();
updateUI();
};
function initPool(min, max) {
pool = [];
for (let i = min; i <= max; i++) {
pool.push(i);
}
drawn = [];
isRepeat = false;
}
function pickOne() {
let picked;
if (mode === 'fair') {
if (pool.length === 0) return;
const idx = getRandomInt(0, pool.length - 1);
picked = pool.splice(idx, 1)[0];
isRepeat = false;
} else {
picked = getRandomInt(currentRange[0], currentRange[1]);
isRepeat = drawn.includes(picked);
const poolIdx = pool.indexOf(picked);
if (poolIdx !== -1) {
pool.splice(poolIdx, 1);
}
}
drawn.unshift(picked);
}
document.getElementById('pick-btn').onclick = () => {
pickOne();
updateUI();
};
document.getElementById('restart-btn').onclick = () => {
initPool(currentRange[0], currentRange[1]);
pickOne();
updateUI();
};
document.getElementById('reset-btn').onclick = () => {
isActive = false;
updateUI();
};
updateUI();
</script>
</body>
</html>