Skip to content

Instantly share code, notes, and snippets.

@senko
Created April 22, 2026 19:03
Show Gist options
  • Select an option

  • Save senko/b7a291c277ad1f48b84d0900f483cf83 to your computer and use it in GitHub Desktop.

Select an option

Save senko/b7a291c277ad1f48b84d0900f483cf83 to your computer and use it in GitHub Desktop.
Qwen 3.6-27B Q4 zero-shotting Minesweeper
<!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>
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg: #1a1a2e;
--surface: #16213e;
--surface2: #0f3460;
--accent: #e94560;
--accent2: #ff6b6b;
--text: #eee;
--text-dim: #8892b0;
--cell-size: 36px;
--cell-hidden: #2a3a5c;
--cell-hidden-hover: #3a4f7a;
--cell-revealed: #1a2540;
--cell-border-light: #3a4f7a;
--cell-border-dark: #1a2540;
--gold: #f0c040;
--green: #4ecdc4;
}
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow-x: hidden;
user-select: none;
}
h1 {
font-size: 2.2rem;
font-weight: 800;
letter-spacing: 4px;
text-transform: uppercase;
margin-bottom: 20px;
background: linear-gradient(135deg, var(--accent), var(--accent2), var(--gold));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: none;
filter: drop-shadow(0 2px 8px rgba(233, 69, 96, 0.3));
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 16px;
flex-wrap: wrap;
justify-content: center;
}
.controls button {
padding: 8px 20px;
border: 2px solid var(--surface2);
border-radius: 8px;
background: var(--surface);
color: var(--text-dim);
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
letter-spacing: 1px;
text-transform: uppercase;
}
.controls button:hover {
border-color: var(--accent);
color: var(--text);
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(233, 69, 96, 0.2);
}
.controls button.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
box-shadow: 0 4px 20px rgba(233, 69, 96, 0.4);
}
.game-container {
background: var(--surface);
border-radius: 16px;
padding: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255,255,255,0.05);
position: relative;
}
.status-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
gap: 12px;
}
.counter {
background: #0a0f1e;
border: 2px solid #1a2540;
border-radius: 8px;
padding: 6px 14px;
font-family: 'Courier New', monospace;
font-size: 1.5rem;
font-weight: 700;
color: var(--accent);
min-width: 70px;
text-align: center;
letter-spacing: 2px;
box-shadow: inset 0 2px 8px rgba(0,0,0,0.5);
}
.face-btn {
width: 48px;
height: 48px;
border-radius: 50%;
border: 3px solid var(--surface2);
background: linear-gradient(145deg, #2a3a5c, #1a2540);
cursor: pointer;
font-size: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.face-btn:hover {
transform: scale(1.1);
border-color: var(--accent);
}
.face-btn:active {
transform: scale(0.95);
}
.board {
display: inline-grid;
gap: 2px;
background: #0a0f1e;
border-radius: 8px;
padding: 2px;
border: 2px solid #1a2540;
}
.cell {
width: var(--cell-size);
height: var(--cell-size);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.95rem;
font-weight: 800;
cursor: pointer;
border-radius: 4px;
transition: all 0.1s ease;
position: relative;
overflow: hidden;
}
.cell.hidden {
background: linear-gradient(145deg, #2e4068, #243356);
border: 1px solid #3a5080;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.08), 0 1px 3px rgba(0,0,0,0.2);
}
.cell.hidden:hover {
background: linear-gradient(145deg, #3a5080, #2e4068);
border-color: #4a6090;
transform: scale(1.05);
z-index: 1;
}
.cell.hidden:active {
transform: scale(0.95);
box-shadow: inset 0 2px 6px rgba(0,0,0,0.4);
}
.cell.revealed {
background: var(--cell-revealed);
border: 1px solid rgba(255,255,255,0.03);
cursor: default;
}
.cell.flagged {
background: linear-gradient(145deg, #2e4068, #243356);
border: 1px solid #3a5080;
animation: flagPop 0.3s ease;
}
.cell.mine-hit {
background: var(--accent) !important;
border-color: var(--accent) !important;
animation: explode 0.4s ease;
}
.cell.mine-revealed {
background: #1a1a2e;
border: 1px solid rgba(233, 69, 96, 0.3);
}
.cell.wrong-flag {
background: #2a1a1a;
border: 1px solid rgba(233, 69, 96, 0.5);
}
@keyframes flagPop {
0% { transform: scale(0.5) rotate(-20deg); opacity: 0; }
60% { transform: scale(1.2) rotate(5deg); }
100% { transform: scale(1) rotate(0deg); opacity: 1; }
}
@keyframes explode {
0% { transform: scale(1); }
30% { transform: scale(1.3); }
100% { transform: scale(1); }
}
@keyframes revealCell {
0% { transform: scale(0.8); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
.cell.revealed {
animation: revealCell 0.15s ease;
}
.cell .flag-icon {
font-size: 1.1rem;
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5));
}
.cell .mine-icon {
font-size: 1.1rem;
}
.n1 { color: #4fc3f7; }
.n2 { color: #66bb6a; }
.n3 { color: #ef5350; }
.n4 { color: #ab47bc; }
.n5 { color: #ff7043; }
.n6 { color: #26c6da; }
.n7 { color: #ec407a; }
.n8 { color: #bdbdbd; }
.overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(10, 15, 30, 0.85);
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
backdrop-filter: blur(4px);
opacity: 0;
pointer-events: none;
transition: opacity 0.4s ease;
}
.overlay.show {
opacity: 1;
pointer-events: all;
}
.overlay-title {
font-size: 2rem;
font-weight: 800;
margin-bottom: 8px;
letter-spacing: 2px;
}
.overlay-title.win {
background: linear-gradient(135deg, var(--gold), var(--green));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.overlay-title.lose {
color: var(--accent);
}
.overlay-sub {
color: var(--text-dim);
font-size: 0.95rem;
margin-bottom: 20px;
}
.overlay button {
padding: 10px 30px;
border-radius: 8px;
border: none;
background: var(--accent);
color: #fff;
font-size: 1rem;
font-weight: 700;
cursor: pointer;
transition: all 0.2s ease;
letter-spacing: 1px;
text-transform: uppercase;
}
.overlay button:hover {
background: var(--accent2);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(233, 69, 96, 0.4);
}
.info-bar {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 14px;
color: var(--text-dim);
font-size: 0.8rem;
letter-spacing: 1px;
}
.info-bar span {
display: flex;
align-items: center;
gap: 5px;
}
.confetti-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 100;
overflow: hidden;
}
.confetti {
position: absolute;
width: 10px;
height: 10px;
top: -10px;
animation: confettiFall linear forwards;
}
@keyframes confettiFall {
0% { transform: translateY(0) rotate(0deg); opacity: 1; }
100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }
}
@media (max-width: 600px) {
:root { --cell-size: 28px; }
h1 { font-size: 1.5rem; }
.game-container { padding: 12px; }
.counter { font-size: 1.2rem; min-width: 55px; padding: 4px 10px; }
.face-btn { width: 40px; height: 40px; font-size: 1.2rem; }
}
</style>
</head>
<body>
<h1>πŸ’£ Minesweeper</h1>
<div class="controls">
<button data-difficulty="beginner" class="active">Beginner</button>
<button data-difficulty="intermediate">Intermediate</button>
<button data-difficulty="expert">Expert</button>
</div>
<div class="game-container">
<div class="status-bar">
<div class="counter" id="mineCounter">000</div>
<button class="face-btn" id="faceBtn">😊</button>
<div class="counter" id="timer">000</div>
</div>
<div class="board" id="board"></div>
<div class="overlay" id="overlay">
<div class="overlay-title" id="overlayTitle"></div>
<div class="overlay-sub" id="overlaySub"></div>
<button id="overlayBtn">Play Again</button>
</div>
</div>
<div class="info-bar">
<span>πŸ–±οΈ Left click: Reveal</span>
<span>🚩 Right click: Flag</span>
<span>πŸ‘† Middle: Chord</span>
</div>
<div class="confetti-container" id="confettiContainer"></div>
<script>
const DIFFICULTIES = {
beginner: { rows: 9, cols: 9, mines: 10 },
intermediate: { rows: 16, cols: 16, mines: 40 },
expert: { rows: 16, cols: 30, mines: 99 }
};
let difficulty = 'beginner';
let rows, cols, totalMines;
let board = []; // 2D array of cell data
let revealed = [];
let flagged = [];
let mineMap = [];
let gameOver = false;
let gameStarted = false;
let firstClick = true;
let flagCount = 0;
let timerInterval = null;
let seconds = 0;
let revealedCount = 0;
let cellsRevealed = 0;
const boardEl = document.getElementById('board');
const mineCounterEl = document.getElementById('mineCounter');
const timerEl = document.getElementById('timer');
const faceBtn = document.getElementById('faceBtn');
const overlay = document.getElementById('overlay');
const overlayTitle = document.getElementById('overlayTitle');
const overlaySub = document.getElementById('overlaySub');
const overlayBtn = document.getElementById('overlayBtn');
const confettiContainer = document.getElementById('confettiContainer');
// Difficulty buttons
document.querySelectorAll('.controls button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelector('.controls button.active').classList.remove('active');
btn.classList.add('active');
difficulty = btn.dataset.difficulty;
initGame();
});
});
faceBtn.addEventListener('click', initGame);
overlayBtn.addEventListener('click', initGame);
function initGame() {
const cfg = DIFFICULTIES[difficulty];
rows = cfg.rows;
cols = cfg.cols;
totalMines = cfg.mines;
board = [];
revealed = [];
flagged = [];
mineMap = [];
gameOver = false;
gameStarted = false;
firstClick = true;
flagCount = 0;
revealedCount = 0;
cellsRevealed = 0;
seconds = 0;
clearInterval(timerInterval);
timerInterval = null;
faceBtn.textContent = '😊';
overlay.classList.remove('show');
confettiContainer.innerHTML = '';
for (let r = 0; r < rows; r++) {
board[r] = [];
revealed[r] = [];
flagged[r] = [];
mineMap[r] = [];
for (let c = 0; c < cols; c++) {
board[r][c] = 0;
revealed[r][c] = false;
flagged[r][c] = false;
mineMap[r][c] = false;
}
}
updateMineCounter();
timerEl.textContent = '000';
renderBoard();
}
function placeMines(safeRow, safeCol) {
// Create safe zone around first click
const safeZone = new Set();
for (let dr = -1; dr <= 1; dr++) {
for (let dc = -1; dc <= 1; dc++) {
const nr = safeRow + dr;
const nc = safeCol + dc;
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
safeZone.add(nr * cols + nc);
}
}
}
let placed = 0;
while (placed < totalMines) {
const r = Math.floor(Math.random() * rows);
const c = Math.floor(Math.random() * cols);
if (!mineMap[r][c] && !safeZone.has(r * cols + c)) {
mineMap[r][c] = true;
placed++;
}
}
// Calculate numbers
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (mineMap[r][c]) {
board[r][c] = -1;
} else {
let count = 0;
forNeighbors(r, c, (nr, nc) => {
if (mineMap[nr][nc]) count++;
});
board[r][c] = count;
}
}
}
}
function forNeighbors(r, c, fn) {
for (let dr = -1; dr <= 1; dr++) {
for (let dc = -1; dc <= 1; dc++) {
if (dr === 0 && dc === 0) continue;
const nr = r + dr;
const nc = c + dc;
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
fn(nr, nc);
}
}
}
}
function renderBoard() {
boardEl.innerHTML = '';
boardEl.style.gridTemplateColumns = `repeat(${cols}, var(--cell-size))`;
boardEl.style.gridTemplateRows = `repeat(${rows}, var(--cell-size))`;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const cell = document.createElement('div');
cell.className = 'cell hidden';
cell.dataset.row = r;
cell.dataset.col = c;
cell.addEventListener('mousedown', onCellMouseDown);
cell.addEventListener('contextmenu', e => e.preventDefault());
cell.addEventListener('touchstart', onTouchStart, { passive: false });
cell.addEventListener('touchend', onTouchEnd, { passive: false });
cell.addEventListener('touchmove', onTouchMove, { passive: false });
boardEl.appendChild(cell);
}
}
}
let touchTimer = null;
let touchMoved = false;
let touchStartX, touchStartY;
function onTouchStart(e) {
if (gameOver) return;
const touch = e.touches[0];
touchStartX = touch.clientX;
touchStartY = touch.clientY;
touchMoved = false;
touchTimer = setTimeout(() => {
const r = parseInt(e.target.dataset.row);
const c = parseInt(e.target.dataset.col);
toggleFlag(r, c);
touchTimer = null;
}, 400);
}
function onTouchMove(e) {
touchMoved = true;
if (touchTimer) {
clearTimeout(touchTimer);
touchTimer = null;
}
}
function onTouchEnd(e) {
if (touchMoved) return;
e.preventDefault();
if (touchTimer) {
clearTimeout(touchTimer);
touchTimer = null;
return;
}
const r = parseInt(e.target.dataset.row);
const c = parseInt(e.target.dataset.col);
if (r !== undefined && !isNaN(r)) {
revealCell(r, c);
}
}
function onCellMouseDown(e) {
e.preventDefault();
if (gameOver) return;
const r = parseInt(e.target.dataset.row);
const c = parseInt(e.target.dataset.col);
if (e.button === 0) {
// Left click
if (revealed[r][c] && !flagged[r][c]) {
// Chord: if cell is revealed and neighbors flagged == number, reveal rest
chordReveal(r, c);
} else if (!flagged[r][c]) {
revealCell(r, c);
}
} else if (e.button === 2) {
// Right click
toggleFlag(r, c);
} else if (e.button === 1) {
// Middle click = chord
chordReveal(r, c);
}
}
function toggleFlag(r, c) {
if (gameOver || revealed[r][c]) return;
const cell = getCellEl(r, c);
if (flagged[r][c]) {
flagged[r][c] = false;
flagCount--;
cell.className = 'cell hidden';
cell.innerHTML = '';
} else {
flagged[r][c] = true;
flagCount++;
cell.className = 'cell hidden flagged';
cell.innerHTML = '<span class="flag-icon">🚩</span>';
}
updateMineCounter();
}
function chordReveal(r, c) {
if (!revealed[r][c] || board[r][c] <= 0) return;
let adjFlags = 0;
forNeighbors(r, c, (nr, nc) => {
if (flagged[nr][nc]) adjFlags++;
});
if (adjFlags === board[r][c]) {
forNeighbors(r, c, (nr, nc) => {
if (!revealed[nr][nc] && !flagged[nr][nc]) {
revealCell(nr, nc);
}
});
}
}
function revealCell(r, c) {
if (gameOver || revealed[r][c] || flagged[r][c]) return;
if (firstClick) {
firstClick = false;
placeMines(r, c);
startTimer();
}
revealed[r][c] = true;
cellsRevealed++;
const cell = getCellEl(r, c);
if (mineMap[r][c]) {
// Hit a mine
gameOver = true;
cell.className = 'cell revealed mine-hit';
cell.innerHTML = '<span class="mine-icon">πŸ’£</span>';
faceBtn.textContent = '😡';
revealAllMines(r, c);
stopTimer();
setTimeout(() => showOverlay(false), 600);
return;
}
cell.className = 'cell revealed';
if (board[r][c] > 0) {
cell.textContent = board[r][c];
cell.classList.add('n' + board[r][c]);
} else {
// Flood fill for empty cells
cell.textContent = '';
forNeighbors(r, c, (nr, nc) => {
if (!revealed[nr][nc] && !flagged[nr][nc]) {
revealCell(nr, nc);
}
});
}
// Check win
if (cellsRevealed === rows * cols - totalMines) {
gameOver = true;
faceBtn.textContent = '😎';
stopTimer();
autoFlagRemaining();
setTimeout(() => showOverlay(true), 400);
}
}
function revealAllMines(hitR, hitC) {
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const cell = getCellEl(r, c);
if (mineMap[r][c] && !flagged[r][c]) {
if (r === hitR && c === hitC) {
// Already shown
} else {
setTimeout(() => {
cell.className = 'cell revealed mine-revealed';
cell.innerHTML = '<span class="mine-icon">πŸ’£</span>';
}, Math.random() * 500);
}
} else if (flagged[r][c] && !mineMap[r][c]) {
setTimeout(() => {
cell.className = 'cell revealed wrong-flag';
cell.innerHTML = '<span style="font-size:1.1rem">❌</span>';
}, Math.random() * 500);
}
}
}
}
function autoFlagRemaining() {
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (mineMap[r][c] && !flagged[r][c]) {
flagged[r][c] = true;
flagCount++;
const cell = getCellEl(r, c);
cell.className = 'cell hidden flagged';
cell.innerHTML = '<span class="flag-icon">🚩</span>';
}
}
}
updateMineCounter();
}
function getCellEl(r, c) {
return boardEl.children[r * cols + c];
}
function updateMineCounter() {
const val = totalMines - flagCount;
mineCounterEl.textContent = String(Math.max(val, 0)).padStart(3, '0');
if (val < 0) mineCounterEl.textContent = '-' + String(Math.abs(val)).padStart(2, '0');
}
function startTimer() {
if (timerInterval) return;
gameStarted = true;
timerInterval = setInterval(() => {
seconds++;
if (seconds > 999) seconds = 999;
timerEl.textContent = String(seconds).padStart(3, '0');
}, 1000);
}
function stopTimer() {
clearInterval(timerInterval);
timerInterval = null;
}
function showOverlay(won) {
overlayTitle.className = 'overlay-title ' + (won ? 'win' : 'lose');
overlayTitle.textContent = won ? 'πŸŽ‰ You Win!' : 'πŸ’₯ Game Over';
overlaySub.textContent = won
? `Cleared in ${seconds} second${seconds !== 1 ? 's' : ''}`
: 'Better luck next time!';
overlay.classList.add('show');
if (won) {
launchConfetti();
}
}
function launchConfetti() {
const colors = ['#e94560', '#f0c040', '#4ecdc4', '#4fc3f7', '#ab47bc', '#ff7043', '#66bb6a'];
for (let i = 0; i < 80; i++) {
setTimeout(() => {
const confetti = document.createElement('div');
confetti.className = 'confetti';
confetti.style.left = Math.random() * 100 + '%';
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
confetti.style.width = (Math.random() * 8 + 4) + 'px';
confetti.style.height = (Math.random() * 8 + 4) + 'px';
confetti.style.borderRadius = Math.random() > 0.5 ? '50%' : '2px';
confetti.style.animationDuration = (Math.random() * 2 + 1.5) + 's';
confetti.style.animationDelay = '0s';
confettiContainer.appendChild(confetti);
setTimeout(() => confetti.remove(), 4000);
}, i * 30);
}
}
// Prevent context menu on board
boardEl.addEventListener('contextmenu', e => e.preventDefault());
// Init
initGame();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment