<style>
.minesweeper-game {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 50%, #93c5fd 100%);
padding: 20px;
margin: 0 -10px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
max-width: 768px;
margin-left: auto;
margin-right: auto;
}
.minesweeper-title {
text-align: center;
font-size: 2.5rem;
color: #1e40af;
margin-bottom: 10px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.minesweeper-title-line {
width: 100px;
height: 3px;
background: linear-gradient(to right, #60a5fa, #2563eb);
margin: 0 auto 20px;
border-radius: 2px;
}
.minesweeper-controls {
background: rgba(255, 255, 255, 0.8);
border-radius: 15px;
padding: 20px;
margin-bottom: 20px;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
flex-wrap: wrap;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.minesweeper-difficulty {
display: flex;
flex-direction: column;
align-items: center;
}
.minesweeper-difficulty-label {
color: #1d4ed8;
font-weight: 600;
margin-bottom: 8px;
}
.minesweeper-select {
padding: 8px 15px;
border: 2px solid #93c5fd;
border-radius: 8px;
background: white;
color: #1d4ed8;
font-weight: 500;
outline: none;
cursor: pointer;
}
.minesweeper-select:focus {
border-color: #3b82f6;
}
.minesweeper-info {
display: flex;
gap: 15px;
align-items: center;
}
.minesweeper-info-item {
background: #dbeafe;
padding: 12px 16px;
border-radius: 10px;
text-align: center;
border: 1px solid #bfdbfe;
display: flex;
flex-direction: column;
align-items: center;
}
.minesweeper-info-icon {
font-size: 1.5rem;
margin-bottom: 5px;
}
.minesweeper-info-value {
color: #1e40af;
font-weight: bold;
font-size: 1.2rem;
}
.minesweeper-info-label {
color: #2563eb;
font-size: 0.8rem;
}
.minesweeper-btn {
padding: 12px 24px;
background: linear-gradient(to right, #3b82f6, #2563eb);
color: white;
border: none;
border-radius: 10px;
cursor: pointer;
font-weight: bold;
font-size: 1rem;
transition: all 0.2s;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.minesweeper-btn:hover {
background: linear-gradient(to right, #2563eb, #1d4ed8);
transform: translateY(-1px);
box-shadow: 0 6px 8px rgba(0,0,0,0.15);
}
.minesweeper-status {
text-align: center;
margin: 20px 0;
}
.minesweeper-status-box {
display: inline-block;
padding: 15px 30px;
border-radius: 15px;
font-weight: bold;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.minesweeper-status-title {
font-size: 1.5rem;
margin-bottom: 5px;
}
.minesweeper-status-subtitle {
font-size: 1rem;
}
.status-ready {
background: linear-gradient(to right, #3b82f6, #2563eb);
color: white;
}
.status-won {
background: linear-gradient(to right, #10b981, #059669);
color: white;
}
.status-lost {
background: linear-gradient(to right, #ef4444, #dc2626);
color: white;
}
.minesweeper-board-container {
display: flex;
justify-content: center;
margin: 20px 0;
overflow-x: auto;
}
.minesweeper-board-wrapper {
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 15px;
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
}
.minesweeper-board {
background: #1e40af;
padding: 8px;
border-radius: 10px;
display: grid;
gap: 2px;
}
.minesweeper-cell {
width: 28px;
height: 28px;
border: 1px solid #93c5fd;
font-size: 0.8rem;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
transition: all 0.15s;
border-radius: 3px;
}
.cell-hidden {
background: linear-gradient(135deg, #bfdbfe, #93c5fd);
}
.cell-hidden:hover {
background: linear-gradient(135deg, #93c5fd, #60a5fa);
transform: scale(1.05);
}
.cell-flagged {
background: linear-gradient(135deg, #60a5fa, #3b82f6);
color: white;
}
.cell-revealed {
background: linear-gradient(135deg, #f8fafc, #e2e8f0);
}
.cell-mine {
background: linear-gradient(135deg, #ef4444, #dc2626);
color: white;
}
.cell-number-1 { color: #1d4ed8; }
.cell-number-2 { color: #059669; }
.cell-number-3 { color: #dc2626; }
.cell-number-4 { color: #7c3aed; }
.cell-number-5 { color: #ea580c; }
.cell-number-6 { color: #db2777; }
.cell-number-7 { color: #374151; }
.cell-number-8 { color: #6b7280; }
@media (max-width: 768px) {
.minesweeper-title {
font-size: 2rem;
}
.minesweeper-controls {
flex-direction: column;
gap: 15px;
}
.minesweeper-info {
justify-content: center;
}
.minesweeper-cell {
width: 24px;
height: 24px;
font-size: 0.7rem;
}
}
</style>
<div class="minesweeper-game">
<h1 class="minesweeper-title">지뢰찾기</h1>
<div class="minesweeper-title-line"></div>
<div class="minesweeper-controls">
<div class="minesweeper-difficulty">
<label class="minesweeper-difficulty-label">난이도</label>
<select id="minesweeper-difficulty" class="minesweeper-select">
<option value="beginner">초급 (9×9, 지뢰 10개)</option>
<option value="intermediate">중급 (16×16, 지뢰 40개)</option>
<option value="expert">고급 (16×30, 지뢰 99개)</option>
</select>
</div>
<div class="minesweeper-info">
<div class="minesweeper-info-item">
<div class="minesweeper-info-icon">💣</div>
<div class="minesweeper-info-value" id="minesweeper-mines-count">10</div>
<div class="minesweeper-info-label">지뢰</div>
</div>
<div class="minesweeper-info-item">
<div class="minesweeper-info-icon">⏱️</div>
<div class="minesweeper-info-value" id="minesweeper-timer">0</div>
<div class="minesweeper-info-label">초</div>
</div>
</div>
<button class="minesweeper-btn" onclick="minesweeperInitGame()">새 게임</button>
</div>
<div class="minesweeper-status">
<div id="minesweeper-status" class="minesweeper-status-box status-ready">
<div class="minesweeper-status-title">초급 준비완료!</div>
<div class="minesweeper-status-subtitle">셀을 클릭해서 게임을 시작하세요</div>
</div>
</div>
<div class="minesweeper-board-container">
<div class="minesweeper-board-wrapper">
<div id="minesweeper-board" class="minesweeper-board"></div>
</div>
</div>
</div>
<script>
var minesweeperGame = {
difficulty: 'beginner',
board: [],
gameStatus: 'ready',
flagCount: 0,
timer: 0,
firstClick: true,
timerInterval: null,
difficulties: {
beginner: { rows: 9, cols: 9, mines: 10, name: '초급' },
intermediate: { rows: 16, cols: 16, mines: 40, name: '중급' },
expert: { rows: 16, cols: 30, mines: 99, name: '고급' }
},
createEmptyBoard: function() {
var config = this.difficulties[this.difficulty];
var board = [];
for (var i = 0; i < config.rows; i++) {
var row = [];
for (var j = 0; j < config.cols; j++) {
row.push({
isMine: false,
isRevealed: false,
isFlagged: false,
neighborCount: 0
});
}
board.push(row);
}
return board;
},
placeMines: function(board, safeRow, safeCol) {
var config = this.difficulties[this.difficulty];
var minesPlaced = 0;
while (minesPlaced < config.mines) {
var row = Math.floor(Math.random() * config.rows);
var col = Math.floor(Math.random() * config.cols);
var isSafeZone = Math.abs(row - safeRow) <= 1 && Math.abs(col - safeCol) <= 1;
if (!board[row][col].isMine && !isSafeZone) {
board[row][col].isMine = true;
minesPlaced++;
}
}
for (var r = 0; r < config.rows; r++) {
for (var c = 0; c < config.cols; c++) {
if (!board[r][c].isMine) {
var count = 0;
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
var nr = r + dr;
var nc = c + dc;
if (nr >= 0 && nr < config.rows && nc >= 0 && nc < config.cols && board[nr][nc].isMine) {
count++;
}
}
}
board[r][c].neighborCount = count;
}
}
}
},
revealArea: function(board, startRow, startCol) {
var config = this.difficulties[this.difficulty];
var stack = [[startRow, startCol]];
var visited = {};
while (stack.length > 0) {
var pos = stack.pop();
var row = pos[0];
var col = pos[1];
var key = row + '-' + col;
if (row < 0 || row >= config.rows || col < 0 || col >= config.cols || visited[key]) continue;
if (board[row][col].isRevealed || board[row][col].isFlagged) continue;
visited[key] = true;
board[row][col].isRevealed = true;
if (board[row][col].neighborCount === 0) {
for (var dr = -1; dr <= 1; dr++) {
for (var dc = -1; dc <= 1; dc++) {
stack.push([row + dr, col + dc]);
}
}
}
}
},
checkWin: function() {
var config = this.difficulties[this.difficulty];
var revealed = 0;
for (var r = 0; r < config.rows; r++) {
for (var c = 0; c < config.cols; c++) {
if (this.board[r][c].isRevealed && !this.board[r][c].isMine) {
revealed++;
}
}
}
return revealed === (config.rows * config.cols - config.mines);
},
startTimer: function() {
var self = this;
if (this.timerInterval) clearInterval(this.timerInterval);
this.timerInterval = setInterval(function() {
self.timer++;
document.getElementById('minesweeper-timer').textContent = self.timer;
}, 1000);
},
stopTimer: function() {
if (this.timerInterval) {
clearInterval(this.timerInterval);
this.timerInterval = null;
}
},
handleCellClick: function(row, col) {
if (this.gameStatus === 'won' || this.gameStatus === 'lost') return;
if (this.board[row][col].isRevealed || this.board[row][col].isFlagged) return;
if (this.firstClick) {
this.placeMines(this.board, row, col);
this.firstClick = false;
this.gameStatus = 'playing';
this.startTimer();
}
if (this.board[row][col].isMine) {
var config = this.difficulties[this.difficulty];
for (var r = 0; r < config.rows; r++) {
for (var c = 0; c < config.cols; c++) {
if (this.board[r][c].isMine) {
this.board[r][c].isRevealed = true;
}
}
}
this.gameStatus = 'lost';
this.stopTimer();
this.updateStatus();
this.renderBoard();
return;
}
if (this.board[row][col].neighborCount === 0) {
this.revealArea(this.board, row, col);
} else {
this.board[row][col].isRevealed = true;
}
if (this.checkWin()) {
this.gameStatus = 'won';
this.stopTimer();
this.updateStatus();
}
this.renderBoard();
},
handleRightClick: function(e, row, col) {
e.preventDefault();
if (this.gameStatus === 'won' || this.gameStatus === 'lost') return;
if (this.board[row][col].isRevealed) return;
if (this.board[row][col].isFlagged) {
this.board[row][col].isFlagged = false;
this.flagCount--;
} else {
this.board[row][col].isFlagged = true;
this.flagCount++;
}
this.updateMinesCount();
this.renderBoard();
},
renderBoard: function() {
var boardElement = document.getElementById('minesweeper-board');
var config = this.difficulties[this.difficulty];
boardElement.style.gridTemplateColumns = 'repeat(' + config.cols + ', 1fr)';
boardElement.innerHTML = '';
for (var r = 0; r < config.rows; r++) {
for (var c = 0; c < config.cols; c++) {
var cell = this.board[r][c];
var cellElement = document.createElement('button');
cellElement.className = 'minesweeper-cell';
if (cell.isFlagged) {
cellElement.textContent = '🚩';
cellElement.className += ' cell-flagged';
} else if (cell.isRevealed) {
if (cell.isMine) {
cellElement.textContent = '💣';
cellElement.className += ' cell-mine';
} else {
cellElement.className += ' cell-revealed';
if (cell.neighborCount > 0) {
cellElement.textContent = cell.neighborCount;
cellElement.className += ' cell-number-' + cell.neighborCount;
}
}
} else {
cellElement.className += ' cell-hidden';
}
var self = this;
cellElement.setAttribute('data-row', r);
cellElement.setAttribute('data-col', c);
cellElement.onclick = function() {
var row = parseInt(this.getAttribute('data-row'));
var col = parseInt(this.getAttribute('data-col'));
self.handleCellClick(row, col);
};
cellElement.oncontextmenu = function(e) {
var row = parseInt(this.getAttribute('data-row'));
var col = parseInt(this.getAttribute('data-col'));
self.handleRightClick(e, row, col);
};
boardElement.appendChild(cellElement);
}
}
},
updateStatus: function() {
var statusElement = document.getElementById('minesweeper-status');
var difficultyName = this.difficulties[this.difficulty].name;
statusElement.className = 'minesweeper-status-box';
if (this.gameStatus === 'won') {
statusElement.className += ' status-won';
statusElement.innerHTML = '<div class="minesweeper-status-title">🎉 축하합니다! 승리! 🎉</div><div class="minesweeper-status-subtitle">시간: ' + this.timer + '초</div>';
} else if (this.gameStatus === 'lost') {
statusElement.className += ' status-lost';
statusElement.innerHTML = '<div class="minesweeper-status-title">💥 게임 오버! 💥</div><div class="minesweeper-status-subtitle">다시 시도해보세요!</div>';
} else if (this.gameStatus === 'ready') {
statusElement.className += ' status-ready';
statusElement.innerHTML = '<div class="minesweeper-status-title">🚀 ' + difficultyName + ' 준비완료!</div><div class="minesweeper-status-subtitle">셀을 클릭해서 게임을 시작하세요</div>';
}
},
updateMinesCount: function() {
var remainingMines = this.difficulties[this.difficulty].mines - this.flagCount;
document.getElementById('minesweeper-mines-count').textContent = remainingMines;
}
};
function minesweeperInitGame() {
minesweeperGame.stopTimer();
minesweeperGame.board = minesweeperGame.createEmptyBoard();
minesweeperGame.gameStatus = 'ready';
minesweeperGame.flagCount = 0;
minesweeperGame.timer = 0;
minesweeperGame.firstClick = true;
document.getElementById('minesweeper-timer').textContent = '0';
minesweeperGame.updateMinesCount();
minesweeperGame.updateStatus();
minesweeperGame.renderBoard();
}
document.addEventListener('DOMContentLoaded', function() {
minesweeperInitGame();
document.getElementById('minesweeper-difficulty').addEventListener('change', function(e) {
minesweeperGame.difficulty = e.target.value;
minesweeperInitGame();
});
});
</script>