Skip to content

Instantly share code, notes, and snippets.

@mhz-tamb
Created July 22, 2025 14:08
Show Gist options
  • Save mhz-tamb/ba2419e20cd276225375ca420bcca2db to your computer and use it in GitHub Desktop.
Save mhz-tamb/ba2419e20cd276225375ca420bcca2db to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Лотерея: классика и быстрая</title>
<style>
:root {
--bg-light: #eef2f7;
--bg-dark: #222;
--card-light: #fff;
--card-dark: #333;
--text-light: #000;
--text-dark: #eee;
--navbar-light: #007bff;
--navbar-dark: #444;
--btn-primary-light: #007bff;
--btn-primary-dark: #555;
--btn-secondary-light: #6c757d;
--btn-secondary-dark: #666;
}
/* Основные стили */
body {
margin: 0;
padding-top: 80px;
font-family: Arial, sans-serif;
background-color: var(--bg-light);
color: var(--text-light);
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
font-size: 1rem;
}
body.dark-theme {
background-color: var(--bg-dark);
color: var(--text-dark);
}
/* Навбар */
#navbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
display: flex;
justify-content: space-around;
align-items: center;
background-color: var(--navbar-light);
color: var(--text-light);
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
z-index: 1000;
font-size: 1.1rem;
}
body.dark-theme #navbar {
background-color: var(--navbar-dark);
color: var(--text-dark);
}
#navbar .stat {
flex: 1;
text-align: center;
}
#themeToggle {
background: none;
border: none;
font-size: 1.4rem;
cursor: pointer;
color: inherit;
}
/* Меню выбора */
#menu {
margin-bottom: 20px;
font-size: 1.1rem;
}
#lotterySelect {
padding: 8px;
font-size: 1rem;
border-radius: 4px;
border: 1px solid #ccc;
margin-left: 8px;
}
/* Контейнер игры */
.lottery-container {
background-color: var(--card-light);
border-radius: 12px;
box-shadow: 0 6px 18px rgba(0,0,0,0.1);
width: 500px;
max-width: 90%;
padding: 24px;
text-align: center;
margin-bottom: 40px;
}
body.dark-theme .lottery-container {
background-color: var(--card-dark);
}
.lottery-container h2 {
margin-bottom: 16px;
font-size: 1.3rem;
}
/* Поля ввода: общий стиль */
.numbers {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 16px;
justify-items: stretch;
}
.numbers input {
width: 85%;
padding: 14px;
font-size: 1.2rem;
border: 1px solid #ccc;
border-radius: 6px;
text-align: center;
}
/* Метка и bias */
.lottery-container label {
display: flex;
align-items: center;
justify-content: center;
margin: 12px 0;
font-size: 1.1rem;
}
.lottery-container label input {
width: 80px;
margin-left: 8px;
padding: 6px;
font-size: 1rem;
}
/* Кнопки */
.btn {
width: 100%;
padding: 14px;
border: none;
border-radius: 6px;
font-weight: bold;
color: #fff;
cursor: pointer;
transition: filter 0.2s;
margin: 12px 0;
font-size: 1.1rem;
}
.btn-primary {
background-color: var(--btn-primary-light);
}
.btn-primary:hover {
filter: brightness(90%);
}
body.dark-theme .btn-primary {
background-color: var(--btn-primary-dark);
}
body.dark-theme .btn-primary:hover {
filter: brightness(110%);
}
.btn-secondary {
background-color: var(--btn-secondary-light);
}
.btn-secondary:hover {
filter: brightness(90%);
}
body.dark-theme .btn-secondary {
background-color: var(--btn-secondary-dark);
}
body.dark-theme .btn-secondary:hover {
filter: brightness(110%);
}
.result {
margin-top: 20px;
font-size: 1.2rem;
line-height: 1.4;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/confetti.browser.min.js"></script>
</head>
<body>
<header id="navbar">
<div class="stat">Игр сыграно: <span id="gamesPlayed">0</span></div>
<div class="stat">Выигрышей: <span id="wins">0</span></div>
<div class="stat">Баланс: <span id="balance">$100</span></div>
<button id="themeToggle" aria-label="Переключить тему">🌙</button>
</header>
<div id="menu">
Выберите лотерею:
<select id="lotterySelect">
<option value="classic">Классическая (6/49)</option>
<option value="quick">Быстрая (3/20)</option>
</select>
</div>
<!-- Классическая лотерея -->
<div id="classicLottery" class="lottery-container">
<h2>Классическая лотерея</h2>
<div id="classicNumbers" class="numbers">
<input type="number" min="1" max="49" id="c1" placeholder="1">
<input type="number" min="1" max="49" id="c2" placeholder="2">
<input type="number" min="1" max="49" id="c3" placeholder="3">
<input type="number" min="1" max="49" id="c4" placeholder="4">
<input type="number" min="1" max="49" id="c5" placeholder="5">
<input type="number" min="1" max="49" id="c6" placeholder="6">
</div>
<button id="autoFillClassic" class="btn btn-secondary">Автозаполнить</button>
<label>Шанс выпадения (%): <input type="number" id="biasClassic" min="0" max="100"></label>
<button id="drawClassic" class="btn btn-primary">Сыграть</button>
<div id="resultClassic" class="result"></div>
</div>
<!-- Быстрая лотерея -->
<div id="quickLottery" class="lottery-container" style="display:none;">
<h2>Быстрая лотерея</h2>
<div id="quickNumbers" class="numbers">
<input type="number" min="1" max="20" id="q1" placeholder="1">
<input type="number" min="1" max="20" id="q2" placeholder="2">
<input type="number" min="1" max="20" id="q3" placeholder="3">
</div>
<button id="autoFillQuick" class="btn btn-secondary">Автозаполнить</button>
<label>Шанс выпадения (%): <input type="number" id="biasQuick" min="0" max="100"></label>
<button id="drawQuick" class="btn btn-primary">Сыграть</button>
<div id="resultQuick" class="result"></div>
</div>
<script>
'use strict';
/**
* Lottery Application
* Manages classic (6/49) and quick (3/20) lotteries,
* persists state to localStorage, and supports theme toggling.
*/
document.addEventListener('DOMContentLoaded', () => {
// Состояние приложения
const state = {
gamesPlayed: parseInt(localStorage.getItem('gamesPlayed'), 10) || 0,
wins: parseInt(localStorage.getItem('wins'), 10) || 0,
balance: parseInt(localStorage.getItem('balance'), 10) || 100,
lastLottery: localStorage.getItem('lastLottery') || 'classic',
biasClassic: localStorage.getItem('biasClassic') || '70',
biasQuick: localStorage.getItem('biasQuick') || '70',
theme: localStorage.getItem('theme') || 'light'
};
// DOM элементы
const dom = {
stats: {
gamesPlayed: document.getElementById('gamesPlayed'),
wins: document.getElementById('wins'),
balance: document.getElementById('balance')
},
lotterySelect: document.getElementById('lotterySelect'),
classicContainer: document.getElementById('classicLottery'),
quickContainer: document.getElementById('quickLottery'),
biasClassic: document.getElementById('biasClassic'),
biasQuick: document.getElementById('biasQuick'),
themeToggle: document.getElementById('themeToggle'),
autoFillClassic: document.getElementById('autoFillClassic'),
drawClassic: document.getElementById('drawClassic'),
autoFillQuick: document.getElementById('autoFillQuick'),
drawQuick: document.getElementById('drawQuick'),
resultClassic: document.getElementById('resultClassic'),
resultQuick: document.getElementById('resultQuick')
};
/** Обновление статистики и сохранение состояния */
const updateStats = () => {
dom.stats.gamesPlayed.textContent = state.gamesPlayed;
dom.stats.wins.textContent = state.wins;
dom.stats.balance.textContent = `$${state.balance}`;
localStorage.setItem('gamesPlayed', state.gamesPlayed);
localStorage.setItem('wins', state.wins);
localStorage.setItem('balance', state.balance);
};
/** Применение темы и сохранение */
const applyTheme = (theme) => {
document.body.classList.toggle('dark-theme', theme === 'dark');
dom.themeToggle.textContent = theme === 'dark' ? '☀️' : '🌙';
localStorage.setItem('theme', theme);
};
/** Отображение выбранной лотереи и сохранение */
const showLottery = (type) => {
dom.classicContainer.style.display = type === 'classic' ? 'block' : 'none';
dom.quickContainer.style.display = type === 'quick' ? 'block' : 'none';
localStorage.setItem('lastLottery', type);
};
/** Генерация уникальных случайных чисел */
const getRandomNumbers = (count, min, max) => {
const set = new Set();
while (set.size < count) {
set.add(Math.floor(Math.random() * (max - min + 1)) + min);
}
return Array.from(set);
};
/**
* Основная функция розыгрыша
* @param {object} config параметры розыгрыша
*/
const playLottery = ({ count, maxNum, inputPrefix, biasDom, resultDom, winReward, lossPenalty, confettiOpts }) => {
state.gamesPlayed++;
const userNums = [];
// Считывание и проверка ввода
for (let i = 1; i <= count; i++) {
const val = parseInt(document.getElementById(`${inputPrefix}${i}`).value, 10);
if (isNaN(val) || val < 1 || val > maxNum) {
alert(`Введите ${count} уникальных чисел от 1 до ${maxNum}!`);
state.gamesPlayed--;
return;
}
userNums.push(val);
}
if (new Set(userNums).size !== count) {
alert('Числа не должны повторяться!');
state.gamesPlayed--;
return;
}
// Получение bias
const biasVal = parseInt(biasDom.value, 10);
if (isNaN(biasVal) || biasVal < 0 || biasVal > 100) {
alert('Bias должен быть между 0 и 100!');
state.gamesPlayed--;
return;
}
localStorage.setItem(biasDom === dom.biasClassic ? 'biasClassic' : 'biasQuick', biasVal);
const bias = biasVal / 100;
// Подготовка пула чисел
const pool = [];
for (let i = 1; i <= maxNum; i++) {
if (!userNums.includes(i)) pool.push(i);
}
// Розыгрыш
const drawSet = new Set();
while (drawSet.size < count) {
const pick = Math.random() < bias
? userNums[Math.floor(Math.random() * userNums.length)]
: pool[Math.floor(Math.random() * pool.length)];
drawSet.add(pick);
}
const draw = Array.from(drawSet).sort((a, b) => a - b);
const matches = userNums.filter(n => draw.includes(n));
// Отображение результата
resultDom.innerHTML =
`<strong>Выпало:</strong> ${draw.join(', ')}<br>` +
`<strong>Совпадений:</strong> ${matches.length}` +
(matches.length === count ? ' (Полное совпадение!)' : '');
// Обновление баланса и выигрышей
if (matches.length === count) {
state.wins++;
state.balance += winReward;
confetti(confettiOpts);
} else {
state.balance -= lossPenalty;
}
updateStats();
};
// Инициализация интерфейса
dom.lotterySelect.value = state.lastLottery;
dom.biasClassic.value = state.biasClassic;
dom.biasQuick.value = state.biasQuick;
applyTheme(state.theme);
updateStats();
showLottery(state.lastLottery);
// Обработчики событий
dom.themeToggle.addEventListener('click', () => applyTheme(document.body.classList.contains('dark-theme') ? 'light' : 'dark'));
dom.lotterySelect.addEventListener('change', (e) => showLottery(e.target.value));
dom.autoFillClassic.addEventListener('click', () => {
getRandomNumbers(6, 1, 49)
.sort((a, b) => a - b)
.forEach((num, idx) => document.getElementById(`c${idx+1}`).value = num);
});
dom.drawClassic.addEventListener('click', () => playLottery({
count: 6,
maxNum: 49,
inputPrefix: 'c',
biasDom: dom.biasClassic,
resultDom: dom.resultClassic,
winReward: 100,
lossPenalty: 10,
confettiOpts: { particleCount: 200, spread: 100, origin: { y: 0.5 } }
}));
dom.autoFillQuick.addEventListener('click', () => {
getRandomNumbers(3,1,20)
.sort((a,b) => a - b)
.forEach((num, idx) => document.getElementById(`q${idx+1}`).value = num);
});
dom.drawQuick.addEventListener('click', () => playLottery({
count: 3,
maxNum: 20,
inputPrefix: 'q',
biasDom: dom.biasQuick,
resultDom: dom.resultQuick,
winReward: 50,
lossPenalty: 5,
confettiOpts: { particleCount: 150, spread: 90, origin: { y: 0.5 } }
}));
});
</script>
</body>
</html>
@chekmari
Copy link

О, в полку смузихлебного софта от токсичного сообщества прибыло!

Буду первым, так сказать, начинайте переубеждать меня.

@subs1stem
Copy link

Что то этих лотереек развелось... А ни одной нормально работающей оконной лотерейки ещё нету. Видимо слишком сложно.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment