Created
July 23, 2025 11:17
-
-
Save senko/0914a98bd7b946a395a7b051d8798622 to your computer and use it in GitHub Desktop.
Qwen3-coder single-shotting Minesweeper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Minesweeper</title> | |
<style> | |
* { | |
box-sizing: border-box; | |
margin: 0; | |
padding: 0; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
} | |
body { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
min-height: 100vh; | |
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c); | |
padding: 20px; | |
} | |
.game-container { | |
background-color: #2c3e50; | |
border-radius: 10px; | |
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); | |
padding: 20px; | |
text-align: center; | |
max-width: 500px; | |
width: 100%; | |
} | |
h1 { | |
color: #ecf0f1; | |
margin-bottom: 15px; | |
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); | |
} | |
.stats { | |
display: flex; | |
justify-content: space-between; | |
background-color: #34495e; | |
padding: 12px 20px; | |
border-radius: 8px; | |
margin-bottom: 20px; | |
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.3); | |
} | |
.stat-box { | |
background-color: #2c3e50; | |
color: #ecf0f1; | |
padding: 8px 15px; | |
border-radius: 5px; | |
font-weight: bold; | |
min-width: 80px; | |
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); | |
} | |
.stat-value { | |
font-size: 24px; | |
color: #f1c40f; | |
} | |
.controls { | |
margin: 15px 0; | |
} | |
button { | |
background: linear-gradient(to bottom, #3498db, #2980b9); | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
border-radius: 5px; | |
font-size: 16px; | |
font-weight: bold; | |
cursor: pointer; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); | |
transition: all 0.2s; | |
} | |
button:hover { | |
background: linear-gradient(to bottom, #3cb0fd, #3498db); | |
transform: translateY(-2px); | |
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.3); | |
} | |
button:active { | |
transform: translateY(1px); | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); | |
} | |
.difficulty { | |
display: flex; | |
justify-content: center; | |
gap: 10px; | |
margin: 15px 0; | |
} | |
.difficulty button { | |
padding: 8px 15px; | |
font-size: 14px; | |
} | |
.board { | |
display: grid; | |
grid-template-columns: repeat(10, 1fr); | |
gap: 4px; | |
margin: 0 auto; | |
max-width: 400px; | |
} | |
.cell { | |
aspect-ratio: 1; | |
background: linear-gradient(to bottom right, #3498db, #2980b9); | |
border-radius: 3px; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-weight: bold; | |
font-size: 18px; | |
cursor: pointer; | |
user-select: none; | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); | |
transition: all 0.1s; | |
} | |
.cell:hover { | |
background: linear-gradient(to bottom right, #3cb0fd, #3498db); | |
} | |
.cell.revealed { | |
background: #bdc3c7; | |
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); | |
} | |
.cell.flagged::before { | |
content: "🚩"; | |
} | |
.cell.mine.revealed { | |
background: #e74c3c; | |
} | |
.cell.mine.revealed::before { | |
content: "💣"; | |
} | |
.number-1 { color: #3498db; } | |
.number-2 { color: #2ecc71; } | |
.number-3 { color: #e74c3c; } | |
.number-4 { color: #9b59b6; } | |
.number-5 { color: #e67e22; } | |
.number-6 { color: #1abc9c; } | |
.number-7 { color: #34495e; } | |
.number-8 { color: #7f8c8d; } | |
.instructions { | |
background-color: #34495e; | |
color: #ecf0f1; | |
padding: 15px; | |
border-radius: 8px; | |
margin-top: 20px; | |
text-align: left; | |
font-size: 14px; | |
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.3); | |
} | |
.instructions h3 { | |
margin-bottom: 10px; | |
color: #f1c40f; | |
} | |
.instructions ul { | |
padding-left: 20px; | |
} | |
.instructions li { | |
margin-bottom: 8px; | |
} | |
.game-over { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(0, 0, 0, 0.8); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
z-index: 100; | |
opacity: 0; | |
pointer-events: none; | |
transition: opacity 0.3s; | |
} | |
.game-over.show { | |
opacity: 1; | |
pointer-events: all; | |
} | |
.game-over-content { | |
background: #2c3e50; | |
padding: 30px; | |
border-radius: 10px; | |
text-align: center; | |
max-width: 90%; | |
width: 400px; | |
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); | |
} | |
.game-over h2 { | |
color: #ecf0f1; | |
margin-bottom: 20px; | |
} | |
.win { | |
color: #2ecc71; | |
} | |
.lose { | |
color: #e74c3c; | |
} | |
@media (max-width: 500px) { | |
.board { | |
grid-template-columns: repeat(10, 1fr); | |
gap: 2px; | |
} | |
.cell { | |
font-size: 14px; | |
} | |
.stat-box { | |
padding: 6px 10px; | |
min-width: 70px; | |
} | |
.stat-value { | |
font-size: 20px; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="game-container"> | |
<h1>MINESWEEPER</h1> | |
<div class="stats"> | |
<div class="stat-box"> | |
<div>MINES</div> | |
<div id="mine-count" class="stat-value">15</div> | |
</div> | |
<div class="stat-box"> | |
<div>FLAGS</div> | |
<div id="flag-count" class="stat-value">0</div> | |
</div> | |
<div class="stat-box"> | |
<div>TIME</div> | |
<div id="timer" class="stat-value">0</div> | |
</div> | |
</div> | |
<div class="controls"> | |
<button id="reset-btn">New Game</button> | |
</div> | |
<div class="difficulty"> | |
<button id="easy-btn">Easy</button> | |
<button id="medium-btn">Medium</button> | |
<button id="hard-btn">Hard</button> | |
</div> | |
<div id="board" class="board"></div> | |
<div class="instructions"> | |
<h3>How to Play:</h3> | |
<ul> | |
<li><strong>Left-click</strong> to reveal a cell</li> | |
<li><strong>Right-click</strong> to place/remove a flag</li> | |
<li>Numbers indicate how many mines are adjacent</li> | |
<li>Reveal all non-mine cells to win the game</li> | |
<li>Flag all mines to win faster!</li> | |
</ul> | |
</div> | |
</div> | |
<div id="game-over" class="game-over"> | |
<div class="game-over-content"> | |
<h2 id="game-result">Game Over</h2> | |
<p id="result-message">You clicked on a mine!</p> | |
<button id="play-again">Play Again</button> | |
</div> | |
</div> | |
<script> | |
// Game configuration | |
const config = { | |
easy: { rows: 10, cols: 10, mines: 15 }, | |
medium: { rows: 14, cols: 14, mines: 35 }, | |
hard: { rows: 18, cols: 18, mines: 70 } | |
}; | |
// Game state | |
let gameState = { | |
board: [], | |
minePositions: [], | |
revealedCount: 0, | |
flaggedCount: 0, | |
gameOver: false, | |
gameWon: false, | |
firstClick: true, | |
timer: 0, | |
timerInterval: null, | |
difficulty: 'easy' | |
}; | |
// DOM elements | |
const boardElement = document.getElementById('board'); | |
const mineCountElement = document.getElementById('mine-count'); | |
const flagCountElement = document.getElementById('flag-count'); | |
const timerElement = document.getElementById('timer'); | |
const resetButton = document.getElementById('reset-btn'); | |
const easyButton = document.getElementById('easy-btn'); | |
const mediumButton = document.getElementById('medium-btn'); | |
const hardButton = document.getElementById('hard-btn'); | |
const gameOverElement = document.getElementById('game-over'); | |
const gameResultElement = document.getElementById('game-result'); | |
const resultMessageElement = document.getElementById('result-message'); | |
const playAgainButton = document.getElementById('play-again'); | |
// Initialize the game | |
function initGame() { | |
const { rows, cols, mines } = config[gameState.difficulty]; | |
// Reset game state | |
gameState.board = []; | |
gameState.minePositions = []; | |
gameState.revealedCount = 0; | |
gameState.flaggedCount = 0; | |
gameState.gameOver = false; | |
gameState.gameWon = false; | |
gameState.firstClick = true; | |
gameState.timer = 0; | |
// Clear the board | |
boardElement.innerHTML = ''; | |
boardElement.style.gridTemplateColumns = `repeat(${cols}, 1fr)`; | |
// Update mine count display | |
mineCountElement.textContent = mines; | |
flagCountElement.textContent = '0'; | |
timerElement.textContent = '0'; | |
// Clear any existing timer | |
if (gameState.timerInterval) { | |
clearInterval(gameState.timerInterval); | |
gameState.timerInterval = null; | |
} | |
// Hide game over screen | |
gameOverElement.classList.remove('show'); | |
// Create the board | |
for (let row = 0; row < rows; row++) { | |
gameState.board[row] = []; | |
for (let col = 0; col < cols; col++) { | |
gameState.board[row][col] = { | |
isMine: false, | |
isRevealed: false, | |
isFlagged: false, | |
adjacentMines: 0 | |
}; | |
// Create cell element | |
const cell = document.createElement('div'); | |
cell.className = 'cell'; | |
cell.dataset.row = row; | |
cell.dataset.col = col; | |
// Add event listeners | |
cell.addEventListener('click', () => handleCellClick(row, col)); | |
cell.addEventListener('contextmenu', (e) => { | |
e.preventDefault(); | |
handleRightClick(row, col); | |
}); | |
boardElement.appendChild(cell); | |
} | |
} | |
} | |
// Place mines on the board (avoiding the first clicked cell) | |
function placeMines(firstRow, firstCol) { | |
const { rows, cols, mines } = config[gameState.difficulty]; | |
const totalCells = rows * cols; | |
const minePositions = []; | |
// Create array of all possible positions | |
const positions = []; | |
for (let row = 0; row < rows; row++) { | |
for (let col = 0; col < cols; col++) { | |
// Don't place mine on first clicked cell or adjacent cells | |
if (Math.abs(row - firstRow) <= 1 && Math.abs(col - firstCol) <= 1) { | |
continue; | |
} | |
positions.push({ row, col }); | |
} | |
} | |
// Shuffle positions and select mines | |
for (let i = positions.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[positions[i], positions[j]] = [positions[j], positions[i]]; | |
} | |
// Place mines | |
for (let i = 0; i < mines; i++) { | |
const { row, col } = positions[i]; | |
gameState.board[row][col].isMine = true; | |
minePositions.push({ row, col }); | |
} | |
gameState.minePositions = minePositions; | |
// Calculate adjacent mines for each cell | |
calculateAdjacentMines(); | |
} | |
// Calculate adjacent mines for each cell | |
function calculateAdjacentMines() { | |
const { rows, cols } = config[gameState.difficulty]; | |
for (let row = 0; row < rows; row++) { | |
for (let col = 0; col < cols; col++) { | |
if (gameState.board[row][col].isMine) continue; | |
let count = 0; | |
// Check all 8 adjacent cells | |
for (let r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) { | |
for (let c = Math.max(0, col - 1); c <= Math.min(cols - 1, col + 1); c++) { | |
if (gameState.board[r][c].isMine) { | |
count++; | |
} | |
} | |
} | |
gameState.board[row][col].adjacentMines = count; | |
} | |
} | |
} | |
// Handle cell click | |
function handleCellClick(row, col) { | |
// Ignore if game is over or cell is flagged | |
if (gameState.gameOver || gameState.board[row][col].isFlagged) return; | |
const cell = gameState.board[row][col]; | |
// First click - place mines and start timer | |
if (gameState.firstClick) { | |
gameState.firstClick = false; | |
placeMines(row, col); | |
startTimer(); | |
} | |
// If it's a mine, game over | |
if (cell.isMine) { | |
revealMines(); | |
endGame(false); | |
return; | |
} | |
// Reveal the cell | |
revealCell(row, col); | |
// Check for win | |
checkWin(); | |
} | |
// Handle right click (flag placement) | |
function handleRightClick(row, col) { | |
// Ignore if game is over or cell is revealed | |
if (gameState.gameOver || gameState.board[row][col].isRevealed) return; | |
const cell = gameState.board[row][col]; | |
const cellElement = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`); | |
// Toggle flag | |
if (cell.isFlagged) { | |
cell.isFlagged = false; | |
gameState.flaggedCount--; | |
cellElement.classList.remove('flagged'); | |
} else { | |
// Don't allow more flags than mines | |
if (gameState.flaggedCount >= config[gameState.difficulty].mines) return; | |
cell.isFlagged = true; | |
gameState.flaggedCount++; | |
cellElement.classList.add('flagged'); | |
} | |
// Update flag count display | |
flagCountElement.textContent = gameState.flaggedCount; | |
// Check for win | |
checkWin(); | |
} | |
// Reveal a cell and adjacent cells if empty | |
function revealCell(row, col) { | |
const { rows, cols } = config[gameState.difficulty]; | |
const cell = gameState.board[row][col]; | |
// Ignore if already revealed or flagged | |
if (cell.isRevealed || cell.isFlagged) return; | |
// Mark as revealed | |
cell.isRevealed = true; | |
gameState.revealedCount++; | |
// Update cell display | |
const cellElement = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`); | |
cellElement.classList.add('revealed'); | |
// If it's an empty cell, reveal adjacent cells | |
if (cell.adjacentMines === 0) { | |
cellElement.textContent = ''; | |
// Reveal all adjacent cells | |
for (let r = Math.max(0, row - 1); r <= Math.min(rows - 1, row + 1); r++) { | |
for (let c = Math.max(0, col - 1); c <= Math.min(cols - 1, col + 1); c++) { | |
if (r === row && c === col) continue; | |
revealCell(r, c); | |
} | |
} | |
} else { | |
// Show the number of adjacent mines | |
cellElement.textContent = cell.adjacentMines; | |
cellElement.classList.add(`number-${cell.adjacentMines}`); | |
} | |
} | |
// Reveal all mines (when game is lost) | |
function revealMines() { | |
gameState.minePositions.forEach(({ row, col }) => { | |
const cell = gameState.board[row][col]; | |
if (!cell.isRevealed) { | |
cell.isRevealed = true; | |
const cellElement = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`); | |
cellElement.classList.add('revealed', 'mine'); | |
} | |
}); | |
} | |
// Start the game timer | |
function startTimer() { | |
gameState.timer = 0; | |
timerElement.textContent = gameState.timer; | |
gameState.timerInterval = setInterval(() => { | |
gameState.timer++; | |
timerElement.textContent = gameState.timer; | |
}, 1000); | |
} | |
// Check if the player has won | |
function checkWin() { | |
const { rows, cols, mines } = config[gameState.difficulty]; | |
const totalCells = rows * cols; | |
// Win condition: all non-mine cells are revealed | |
if (gameState.revealedCount === totalCells - mines) { | |
endGame(true); | |
} | |
// Alternative win condition: all mines are flagged and all other cells are revealed | |
if (gameState.flaggedCount === mines) { | |
let allFlaggedAreMines = true; | |
let allNonMinesRevealed = true; | |
for (let row = 0; row < rows; row++) { | |
for (let col = 0; col < cols; col++) { | |
const cell = gameState.board[row][col]; | |
if (cell.isFlagged && !cell.isMine) { | |
allFlaggedAreMines = false; | |
} | |
if (!cell.isFlagged && !cell.isRevealed && !cell.isMine) { | |
allNonMinesRevealed = false; | |
} | |
} | |
} | |
if (allFlaggedAreMines && allNonMinesRevealed) { | |
endGame(true); | |
} | |
} | |
} | |
// End the game | |
function endGame(isWin) { | |
gameState.gameOver = true; | |
gameState.gameWon = isWin; | |
// Stop the timer | |
if (gameState.timerInterval) { | |
clearInterval(gameState.timerInterval); | |
gameState.timerInterval = null; | |
} | |
// Show game over screen | |
setTimeout(() => { | |
if (isWin) { | |
gameResultElement.textContent = 'You Win!'; | |
gameResultElement.className = 'win'; | |
resultMessageElement.textContent = `Congratulations! You found all mines in ${gameState.timer} seconds.`; | |
} else { | |
gameResultElement.textContent = 'Game Over!'; | |
gameResultElement.className = 'lose'; | |
resultMessageElement.textContent = 'You clicked on a mine!'; | |
} | |
gameOverElement.classList.add('show'); | |
}, 500); | |
} | |
// Set difficulty | |
function setDifficulty(difficulty) { | |
gameState.difficulty = difficulty; | |
initGame(); | |
} | |
// Event listeners | |
resetButton.addEventListener('click', () => initGame()); | |
easyButton.addEventListener('click', () => setDifficulty('easy')); | |
mediumButton.addEventListener('click', () => setDifficulty('medium')); | |
hardButton.addEventListener('click', () => setDifficulty('hard')); | |
playAgainButton.addEventListener('click', () => { | |
gameOverElement.classList.remove('show'); | |
initGame(); | |
}); | |
// Initialize the game | |
initGame(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment