JavaScriptマインスイーパーのソースコード

<style>
#game-container { text-align: center; }
#minesweeper-board {
display: inline-grid;
gap: 1px;
background-color: #000;
padding: 1px;
}
.cell {
width: 40px;
height: 40px;
background-color: #CCC;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
font-weight: bold;
cursor: pointer;
}
.cell.revealed { background-color: #f5f5f5; }
#info-text { margin-bottom: 10px; font-size: 18px; }
#result-text { margin-top: 10px; font-size: 24px; font-weight: bold; }
#reset-button {
margin-top: 10px;
font-size: 16px;
padding: 10px 20px;
cursor: pointer;
}
</style>
<div id="game-container">
<div id="info-text"></div>
<div id="minesweeper-board"></div>
<div id="result-text"></div>
<button id="reset-button">リセット</button>
</div>
<script>
// ゲームの設定パラメータ
const params = {
boardSize: 7,// ボードの大きさ
minesCount: 5,// 地雷の数
cellSize: 40,// セルのサイズ
};

// ゲームの状態を管理するオブジェクト
const gameState = {
board: [],
minesLeft: 0,
status: 'playing'
};

// ボードを初期化する関数
function initializeBoard() {
gameState.board = Array(params.boardSize).fill().map(() =>
Array(params.boardSize).fill().map(() => ({
value: 0,
revealed: false,
}))
);
gameState.minesLeft = params.minesCount;
gameState.status = 'playing';

// 地雷をランダムに配置
let minesPlaced = 0;
while (minesPlaced < params.minesCount) {
const x = Math.floor(Math.random() * params.boardSize);
const y = Math.floor(Math.random() * params.boardSize);
if (gameState.board[y][x].value !== 'M') {
gameState.board[y][x].value = 'M';
minesPlaced++;
}
}

// 各セルの周囲の地雷の数を計算
for (let y = 0; y < params.boardSize; y++) {
for (let x = 0; x < params.boardSize; x++) {
if (gameState.board[y][x].value !== 'M') {
gameState.board[y][x].value = countAdjacentMines(x, y);
}
}
}
}

// 指定されたセルの周囲の地雷の数を数える関数
function countAdjacentMines(x, y) {
let count = 0;

// 周囲8マスをチェック
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
const nx = x + dx;
const ny = y + dy;

// ボード内かつ地雷の場合にカウントを増やす
if (nx >= 0 && nx < params.boardSize && ny >= 0 && ny < params.boardSize && gameState.board[ny][nx].value === 'M') {
count++;
}
}
}
return count;
}

// HTML上にゲームボードを作成する関数
function createBoard() {
const boardElement = document.getElementById('minesweeper-board');
boardElement.innerHTML = '';
boardElement.style.gridTemplateColumns = `repeat(${params.boardSize}, auto)`;

// 各セルを作成してボードに追加
for (let y = 0; y < params.boardSize; y++) {
for (let x = 0; x < params.boardSize; x++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.x = x;
cell.dataset.y = y;
cell.addEventListener('click', handleCellClick);
boardElement.appendChild(cell);
}
}
}

// セルがクリックされたときの処理
function handleCellClick(event) {
if (gameState.status !== 'playing') return;
const x = parseInt(event.target.dataset.x);
const y = parseInt(event.target.dataset.y);
revealCell(x, y);
}

// セルを開く関数
function revealCell(x, y) {
if (x < 0 || x >= params.boardSize || y < 0 || y >= params.boardSize || gameState.board[y][x].revealed) return;

gameState.board[y][x].revealed = true;

// 地雷を踏んだ場合、ゲームオーバー
if (gameState.board[y][x].value === 'M') {
gameState.status = 'lost';
showGameResult();
return;
}

// 周囲に地雷がない場合、隣接するセルも自動的に開く
if (gameState.board[y][x].value === 0) {
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
revealCell(x + dx, y + dy);
}
}
}

updateBoard();
checkWinCondition();
}

// 全ての非地雷セルが開かれたらゲームクリア
function checkWinCondition() {
const totalCells = params.boardSize * params.boardSize;
const revealedCount = gameState.board.flat().filter(cell => cell.revealed).length;

if (revealedCount === totalCells - params.minesCount) {
gameState.status = 'won';
showGameResult();
}
}

// ゲームボード全体の表示を更新
function updateBoard() {
const cells = document.querySelectorAll('.cell');
cells.forEach(cell => {
const x = parseInt(cell.dataset.x);
const y = parseInt(cell.dataset.y);
const cellData = gameState.board[y][x];

if (cellData.revealed) {
cell.classList.add('revealed');
if (cellData.value > 0) {
cell.textContent = cellData.value;
cell.style.color = getNumberColor(cellData.value);
} else if (cellData.value === 'M') {
cell.textContent = '💣';
}
} else {
cell.classList.remove('revealed');
cell.textContent = '';
}
});

updateInfoText();
}

// 数字の色を決定する関数
function getNumberColor(value) {
const colors = ['#0000FF', '#008000', '#FF0000', '#000080', '#800000', '#008080', '#000000', '#808080'];
return colors[value - 1] || '#000000';
}

// 情報テキストを更新する関数
function updateInfoText() {
const infoText = document.getElementById('info-text');
infoText.textContent = `地雷の数: ${gameState.minesLeft}`;
}

// ゲーム結果を表示する関数
function showGameResult() {
const resultText = document.getElementById('result-text');
if (gameState.status === 'won') {
resultText.textContent = '勝利しました!';
resultText.style.color = '#00FF00';
} else {
resultText.textContent = '地雷を踏みました';
resultText.style.color = '#FF0000';
}

document.querySelectorAll('.cell').forEach(cell => {
const x = parseInt(cell.dataset.x);
const y = parseInt(cell.dataset.y);
if (gameState.board[y][x].value === 'M') {
cell.textContent = '💣';
cell.classList.add('revealed');
}
});
}

// ゲームをリセットする関数
function resetGame() {
initializeBoard();
createBoard();
updateBoard();
document.getElementById('result-text').textContent = '';
gameState.status = 'playing';
}

// リセットボタンにイベントリスナーを追加
document.getElementById('reset-button').addEventListener('click', resetGame);

// ゲーム開始
resetGame();
</script>

コメント