/** * MINES - Casino Game * Elegant browser-based Mines game simulation */ class MinesGame { constructor(config = {}) { // Grid configuration (easily changeable) this.gridSize = config.gridSize || 5; this.totalTiles = this.gridSize * this.gridSize; // Game state this.balance = 1000; this.bet = 10; this.minesCount = 3; this.isPlaying = false; this.revealedCount = 0; this.currentMultiplier = 1.0; this.minePositions = []; this.history = []; // DOM Elements this.elements = { balance: document.getElementById('balance'), betInput: document.getElementById('bet-amount'), minesCount: document.getElementById('mines-count'), minesSlider: document.getElementById('mines-slider'), minesDecrease: document.getElementById('mines-decrease'), minesIncrease: document.getElementById('mines-increase'), tilesInfo: document.getElementById('tiles-info'), safeTiles: document.getElementById('safe-tiles'), nextMultiplier: document.getElementById('next-multiplier'), currentMultiplier: document.getElementById('current-multiplier'), potentialWin: document.getElementById('potential-win'), startBtn: document.getElementById('start-btn'), cashoutBtn: document.getElementById('cashout-btn'), gameGrid: document.getElementById('game-grid'), gridOverlay: document.getElementById('grid-overlay'), overlayIcon: document.getElementById('overlay-icon'), overlayText: document.getElementById('overlay-text'), revealedCount: document.getElementById('revealed-count'), totalSafe: document.getElementById('total-safe'), historyList: document.getElementById('history-list'), modal: document.getElementById('result-modal'), modalContent: document.getElementById('modal-content'), modalIcon: document.getElementById('modal-icon'), modalTitle: document.getElementById('modal-title'), modalAmount: document.getElementById('modal-amount'), modalSubtitle: document.getElementById('modal-subtitle'), modalClose: document.getElementById('modal-close') }; // Initialize this.init(); } init() { this.updateGridCSS(); this.createGrid(); this.bindEvents(); this.updateUI(); this.showOverlay('💎', 'Place your bet and start!'); } updateGridCSS() { document.documentElement.style.setProperty('--grid-size', this.gridSize); this.elements.tilesInfo.textContent = `${this.totalTiles} (${this.gridSize}×${this.gridSize})`; } createGrid() { this.elements.gameGrid.innerHTML = ''; for (let i = 0; i < this.totalTiles; i++) { const tile = document.createElement('div'); tile.className = 'tile disabled'; tile.dataset.index = i; tile.innerHTML = ''; this.elements.gameGrid.appendChild(tile); } } bindEvents() { // Bet input this.elements.betInput.addEventListener('input', (e) => { this.bet = Math.max(1, parseFloat(e.target.value) || 1); this.updateUI(); }); // Bet buttons (half/double) document.querySelectorAll('.bet-btn').forEach(btn => { btn.addEventListener('click', () => { const action = btn.dataset.action; if (action === 'half') { this.bet = Math.max(1, Math.floor(this.bet / 2)); } else if (action === 'double') { this.bet = Math.min(this.balance, this.bet * 2); } this.elements.betInput.value = this.bet; this.updateUI(); }); }); // Mines selector this.elements.minesDecrease.addEventListener('click', () => { this.minesCount = Math.max(1, this.minesCount - 1); this.elements.minesSlider.value = this.minesCount; this.updateUI(); }); this.elements.minesIncrease.addEventListener('click', () => { this.minesCount = Math.min(this.totalTiles - 1, this.minesCount + 1); this.elements.minesSlider.value = this.minesCount; this.updateUI(); }); this.elements.minesSlider.addEventListener('input', (e) => { this.minesCount = parseInt(e.target.value); this.updateUI(); }); // Update slider max based on grid size this.elements.minesSlider.max = this.totalTiles - 1; // Start button this.elements.startBtn.addEventListener('click', () => this.startRound()); // Cashout button this.elements.cashoutBtn.addEventListener('click', () => this.cashOut()); // Modal close this.elements.modalClose.addEventListener('click', () => this.hideModal()); // Tile clicks this.elements.gameGrid.addEventListener('click', (e) => { const tile = e.target.closest('.tile'); if (tile && this.isPlaying && !tile.classList.contains('revealed') && !tile.classList.contains('disabled')) { this.revealTile(parseInt(tile.dataset.index)); } }); } /** * Calculate multiplier based on mines count and revealed tiles * Formula: Each safe reveal increases multiplier based on probability * The fewer safe tiles remaining, the higher the reward */ calculateMultiplier(revealed) { if (revealed === 0) return 1.0; const safeTiles = this.totalTiles - this.minesCount; let multiplier = 1.0; // House edge (around 3%) const houseEdge = 0.97; for (let i = 0; i < revealed; i++) { const remainingTiles = this.totalTiles - i; const remainingSafe = safeTiles - i; // Fair odds * house edge const stepMultiplier = (remainingTiles / remainingSafe) * houseEdge; multiplier *= stepMultiplier; } return Math.round(multiplier * 100) / 100; } getNextMultiplier() { return this.calculateMultiplier(this.revealedCount + 1); } updateUI() { // Update mines count display this.elements.minesCount.textContent = this.minesCount; // Update safe tiles count const safeTiles = this.totalTiles - this.minesCount; this.elements.safeTiles.textContent = safeTiles; this.elements.totalSafe.textContent = safeTiles; // Update multipliers this.elements.currentMultiplier.textContent = `×${this.currentMultiplier.toFixed(2)}`; this.elements.nextMultiplier.textContent = `×${this.getNextMultiplier().toFixed(2)}`; // Update potential win const potentialWin = this.bet * this.currentMultiplier; this.elements.potentialWin.textContent = this.formatCurrency(potentialWin); // Update revealed count this.elements.revealedCount.textContent = this.revealedCount; // Update balance this.elements.balance.textContent = this.formatCurrency(this.balance); // Update start button state this.elements.startBtn.disabled = this.bet > this.balance || this.bet <= 0; } formatCurrency(amount) { return '$' + amount.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } showOverlay(icon, text) { this.elements.overlayIcon.textContent = icon; this.elements.overlayText.textContent = text; this.elements.gridOverlay.classList.remove('hidden'); } hideOverlay() { this.elements.gridOverlay.classList.add('hidden'); } generateMinePositions() { const positions = []; while (positions.length < this.minesCount) { const pos = Math.floor(Math.random() * this.totalTiles); if (!positions.includes(pos)) { positions.push(pos); } } return positions; } startRound() { if (this.bet > this.balance) return; // Deduct bet from balance this.balance -= this.bet; // Reset game state this.isPlaying = true; this.revealedCount = 0; this.currentMultiplier = 1.0; this.minePositions = this.generateMinePositions(); // Update UI this.hideOverlay(); this.elements.startBtn.classList.add('hidden'); this.elements.cashoutBtn.classList.remove('hidden'); // Reset all tiles document.querySelectorAll('.tile').forEach(tile => { tile.className = 'tile'; tile.querySelector('.tile-icon').textContent = ''; }); // Disable bet controls during game this.elements.betInput.disabled = true; this.elements.minesSlider.disabled = true; this.elements.minesDecrease.disabled = true; this.elements.minesIncrease.disabled = true; this.updateUI(); } revealTile(index) { const tile = document.querySelectorAll('.tile')[index]; const icon = tile.querySelector('.tile-icon'); if (this.minePositions.includes(index)) { // Hit a mine - GAME OVER tile.classList.add('revealed', 'mine'); icon.textContent = '💣'; this.endRound(false); } else { // Safe tile tile.classList.add('revealed', 'safe'); icon.textContent = '💎'; this.revealedCount++; this.currentMultiplier = this.calculateMultiplier(this.revealedCount); this.updateUI(); // Check if all safe tiles are revealed const safeTiles = this.totalTiles - this.minesCount; if (this.revealedCount >= safeTiles) { this.cashOut(); } } } cashOut() { if (!this.isPlaying) return; const winnings = this.bet * this.currentMultiplier; this.balance += winnings; this.endRound(true, winnings); } endRound(isWin, winnings = 0) { this.isPlaying = false; // Reveal all mines this.minePositions.forEach(pos => { const tile = document.querySelectorAll('.tile')[pos]; if (!tile.classList.contains('revealed')) { tile.classList.add('show-mine'); tile.querySelector('.tile-icon').textContent = '💣'; } }); // Disable all tiles document.querySelectorAll('.tile').forEach(tile => { tile.classList.add('disabled'); }); // Re-enable controls this.elements.betInput.disabled = false; this.elements.minesSlider.disabled = false; this.elements.minesDecrease.disabled = false; this.elements.minesIncrease.disabled = false; // Update buttons this.elements.cashoutBtn.classList.add('hidden'); this.elements.startBtn.classList.remove('hidden'); // Add to history this.addToHistory(isWin, winnings); // Show result modal this.showResultModal(isWin, winnings); this.updateUI(); } showResultModal(isWin, winnings) { this.elements.modalContent.className = 'modal-content ' + (isWin ? 'win' : 'loss'); if (isWin) { this.elements.modalIcon.textContent = '💎'; this.elements.modalTitle.textContent = 'You Won!'; this.elements.modalAmount.textContent = '+' + this.formatCurrency(winnings); this.elements.modalSubtitle.textContent = `×${this.currentMultiplier.toFixed(2)} multiplier • ${this.revealedCount} tiles revealed`; } else { this.elements.modalIcon.textContent = '💣'; this.elements.modalTitle.textContent = 'Mine Hit!'; this.elements.modalAmount.textContent = '-' + this.formatCurrency(this.bet); this.elements.modalSubtitle.textContent = `${this.revealedCount} tiles revealed before explosion`; } this.elements.modal.classList.remove('hidden'); } hideModal() { this.elements.modal.classList.add('hidden'); } addToHistory(isWin, winnings) { const historyItem = { isWin, bet: this.bet, winnings: isWin ? winnings : -this.bet, multiplier: this.currentMultiplier, mines: this.minesCount, revealed: this.revealedCount }; this.history.unshift(historyItem); if (this.history.length > 20) this.history.pop(); this.renderHistory(); } renderHistory() { if (this.history.length === 0) { this.elements.historyList.innerHTML = '
No rounds played yet
'; return; } this.elements.historyList.innerHTML = this.history.map(item => `
Bet: ${this.formatCurrency(item.bet)}
${item.isWin ? '+' : ''}${this.formatCurrency(item.winnings)}
×${item.multiplier.toFixed(2)} • ${item.mines} mines • ${item.revealed} revealed
`).join(''); } } // Initialize game when DOM is loaded document.addEventListener('DOMContentLoaded', () => { // You can change grid size here: 5, 6, 7, etc. window.minesGame = new MinesGame({ gridSize: 5 }); });