Last active
April 5, 2025 02:11
-
-
Save yujiorama/32a565edcf968e6e4155f205275d234f to your computer and use it in GitHub Desktop.
my ms
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="ja"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Minesweeper</title> | |
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet"> | |
<style> | |
body { | |
background-color: #c0c0c0; | |
font-family: 'Press Start 2P', cursive; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
height: 100vh; | |
margin: 0; | |
} | |
.controls { | |
margin-bottom: 20px; | |
display: flex; | |
gap: 10px; | |
align-items: center; | |
} | |
.controls label { | |
color: #333; | |
font-size: 0.8rem; | |
} | |
.minesweeper { | |
background: #808080; | |
padding: 15px; | |
border: 5px solid #ffffff; | |
box-shadow: inset -3px -3px #404040, inset 3px 3px #dfdfdf; | |
text-align: center; | |
position: relative; | |
} | |
.board { | |
display: inline-block; | |
background: #c0c0c0; | |
padding: 5px; | |
border: 2px solid #808080; | |
box-shadow: 1px 1px 2px rgba(0,0,0,0.3); | |
} | |
.row { | |
display: flex; | |
} | |
.cell { | |
width: 30px; | |
height: 30px; | |
background: #c0c0c0; | |
border: 2px solid #808080; | |
box-shadow: inset -2px -2px #404040, inset 2px 2px #dfdfdf; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
font-size: 1rem; | |
cursor: pointer; | |
user-select: none; | |
transition: background-color 0.2s, transform 0.1s; | |
} | |
.cell:hover { | |
background-color: #b0b0b0; | |
transform: scale(1.05); | |
} | |
.cell.revealed { | |
background: #dfdfdf; | |
box-shadow: inset 2px 2px #808080, inset -2px -2px #ffffff; | |
} | |
.cell.flagged { | |
background: #c0c0c0; | |
color: red; | |
font-size: 1.2rem; | |
} | |
.info { | |
margin-top: 15px; | |
font-size: 0.9rem; | |
color: #555; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
.info p { | |
margin: 5px 0; | |
} | |
#reset-button, #undo-button { | |
padding: 10px 20px; | |
font-family: 'Press Start 2P', cursive; | |
font-size: 0.7rem; | |
background-color: #c0c0c0; | |
border: 2px solid #808080; | |
box-shadow: 2px 2px 4px rgba(0,0,0,0.3); | |
cursor: pointer; | |
transition: background-color 0.2s, transform 0.1s; | |
margin-top: 10px; | |
} | |
#reset-button:hover, #undo-button:hover { | |
background-color: #b0b0b0; | |
transform: scale(1.05); | |
} | |
#message-box { | |
position: absolute; | |
top: -35px; | |
left: 50%; | |
transform: translateX(-50%); | |
background-color: rgba(255, 255, 255, 0.8); | |
padding: 10px 20px; | |
border: 2px solid #808080; | |
border-radius: 5px; | |
font-size: 0.9rem; | |
z-index: 10; | |
opacity: 0; | |
transition: opacity 0.3s ease-in-out, top 0.3s ease-in-out; | |
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); | |
text-align: center; | |
min-width: 200px; | |
} | |
#message-box.show { | |
opacity: 1; | |
top: 10px; | |
} | |
#win-message { | |
position: absolute; | |
top: 20px; | |
background-color: rgba(255, 255, 255, 0.8); | |
padding: 15px; | |
border: 2px solid #808080; | |
border-radius: 5px; | |
font-size: 1rem; | |
animation: fadeIn 1s ease-in-out, pulse 2s infinite alternate; | |
z-index: 10; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; } | |
to { opacity: 1; } | |
} | |
@keyframes pulse { | |
from { transform: scale(1); } | |
to { transform: scale(1.1); } | |
} | |
</style> | |
</head> | |
<body> | |
<div class="minesweeper"> | |
<div id="message-box"></div> | |
<h1 class="title">Minesweeper</h1> | |
<div id="board" class="board"></div> | |
<div class="controls"> | |
<button id="reset-button" onclick="startGame()">Reset</button> | |
<button id="undo-button" onclick="undoMove()" disabled>Undo</button> | |
</div> | |
</div> | |
<div class="controls"> | |
<label>Rows: <input type="number" id="rows" value="10" min="5" max="20"></label> | |
<label>Cols: <input type="number" id="cols" value="10" min="5" max="20"></label> | |
<label>Mines: <input type="number" id="mineRatio" value="15" min="5" max="50">%</label> | |
</div> | |
<div class="info"> | |
<p>Left Click: Reveal Cell</p> | |
<p>Right Click: Toggle Flag</p> | |
</div> | |
<div id="win-message" style="display:none;">You Win!</div> | |
<script> | |
let rows, cols, mineRatio, board, firstClick; | |
let gameOver = false; | |
let messageBox; | |
let history = []; // 手順を保存する | |
function startGame() { | |
rows = parseInt(document.getElementById("rows").value); | |
cols = parseInt(document.getElementById("cols").value); | |
mineRatio = parseInt(document.getElementById("mineRatio").value) / 100; | |
firstClick = false; | |
gameOver = false; | |
document.getElementById("win-message").style.display = "none"; | |
board = generateBoard(rows, cols, mineRatio); | |
renderBoard(); | |
messageBox = document.getElementById("message-box"); | |
messageBox.classList.remove("show"); | |
history = []; // Reset history | |
document.getElementById("undo-button").disabled = true; // Disable undo button | |
} | |
function generateBoard(rows, cols, mineRatio) { | |
let board = Array.from({ length: rows }, () => Array(cols).fill(null).map(() => ({ mine: false, number: 0, revealed: false, flagged: false }))); | |
let mines = Math.floor(rows * cols * mineRatio); | |
let minesPlaced = 0; | |
while (minesPlaced < mines) { | |
let r = Math.floor(Math.random() * rows); | |
let c = Math.floor(Math.random() * cols); | |
if (!board[r][c].mine) { | |
board[r][c].mine = true; | |
minesPlaced++; | |
} | |
} | |
for (let r = 0; r < rows; r++) { | |
for (let c = 0; c < cols; c++) { | |
if (board[r][c].mine) continue; | |
let count = 0; | |
for (let dr of [-1, 0, 1]) { | |
for (let dc of [-1, 0, 1]) { | |
let nr = r + dr, nc = c + dc; | |
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && board[nr][nc].mine) count++; | |
} | |
} | |
board[r][c].number = count; | |
} | |
} | |
return board; | |
} | |
function revealCell(r, c) { | |
if (gameOver || board[r][c].revealed || board[r][c].flagged) return; | |
// Save current state for undo | |
history.push(board.map(row => row.map(cell => ({ ...cell })))); | |
document.getElementById("undo-button").disabled = false; // Enable undo button | |
if (!firstClick) { | |
firstClick = true; | |
if (board[r][c].mine) { | |
// Move mine on first click | |
let moved = false; | |
for (let i = 0; i < rows; i++) { | |
for (let j = 0; j < cols; j++) { | |
if (!board[i][j].mine) { | |
board[i][j].mine = true; | |
board[r][c].mine = false; | |
moved = true; | |
break; | |
} | |
} | |
if (moved) break; | |
} | |
// Recalculate numbers | |
for (let i = 0; i < rows; i++) { | |
for (let j = 0; j < cols; j++) { | |
if (board[i][j].mine) continue; | |
let count = 0; | |
for (let dr of [-1, 0, 1]) { | |
for (let dc of [-1, 0, 1]) { | |
let nr = i + dr, nc = j + dc; | |
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && board[nr][nc].mine) count++; | |
} | |
} | |
board[i][j].number = count; | |
} | |
} | |
} | |
} | |
if (board[r][c].mine) { | |
gameOver = true; | |
for (let i = 0; i < rows; i++) { | |
for (let j = 0; j < cols; j++) { | |
if (board[i][j].mine) { | |
board[i][j].revealed = true; | |
} | |
} | |
} | |
renderBoard(); | |
showMessage("Game Over!"); | |
return; | |
} | |
board[r][c].revealed = true; | |
if (board[r][c].number === 0) { | |
for (let dr of [-1, 0, 1]) { | |
for (let dc of [-1, 0, 1]) { | |
let nr = r + dr, nc = c + dc; | |
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) revealCell(nr, nc); | |
} | |
} | |
} | |
renderBoard(); | |
checkWin(); | |
} | |
function renderBoard() { | |
const boardDiv = document.getElementById("board"); | |
boardDiv.innerHTML = ""; | |
board.forEach((row, r) => { | |
const rowDiv = document.createElement("div"); | |
rowDiv.classList.add("row"); | |
row.forEach((cell, c) => { | |
const cellDiv = document.createElement("div"); | |
cellDiv.classList.add("cell"); | |
if (cell.revealed) { | |
cellDiv.classList.add("revealed"); | |
cellDiv.textContent = cell.mine ? "💣" : cell.number || ""; | |
if (cell.mine) { | |
cellDiv.style.backgroundColor = '#ff4444'; | |
} | |
} | |
if (cell.flagged) { | |
cellDiv.classList.add("flagged"); | |
cellDiv.textContent = "🚩"; | |
} | |
cellDiv.addEventListener("click", () => revealCell(r, c)); | |
cellDiv.addEventListener("contextmenu", (e) => { | |
e.preventDefault(); | |
if (!cell.revealed) { | |
board[r][c].flagged = !board[r][c].flagged; | |
renderBoard(); | |
} | |
}); | |
rowDiv.appendChild(cellDiv); | |
}); | |
boardDiv.appendChild(rowDiv); | |
}); | |
} | |
function checkWin() { | |
let revealedCount = 0; | |
for (let r = 0; r < rows; r++) { | |
for (let c = 0; c < cols; c++) { | |
if (board[r][c].revealed && !board[r][c].mine) { | |
revealedCount++; | |
} | |
} | |
} | |
if (revealedCount === rows * cols - Math.floor(rows * cols * mineRatio)) { | |
gameOver = true; | |
showMessage("You Win!"); | |
} | |
} | |
function showMessage(message) { | |
messageBox.textContent = message; | |
messageBox.classList.add("show"); | |
setTimeout(() => { | |
messageBox.classList.remove("show"); | |
}, 3000); | |
} | |
function undoMove() { | |
if (history.length > 0) { | |
board = history.pop(); | |
gameOver = false; // Reset game over state | |
renderBoard(); | |
if (history.length === 0) { | |
document.getElementById("undo-button").disabled = true; | |
} | |
} | |
} | |
startGame(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment