Minesweeper by Claude 3.5 Sonnet

Play Here

https://promptjourneys.com/files/minesweeper.html

HTML, CSS, and Javascript

Conversion from React Component by ChatGPT 4o
<!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>
        /* Add your CSS styles here */
        .minesweeper-button {
            width: 30px;
            height: 30px;
            border: 1px solid gray;
            background: lightgray;
            margin: 2px;
        }

        .minesweeper-button.revealed {
            background: white;
        }
    </style>
</head>
<body><div id="minesweeper-root"></div>
<style>
  .minesweeper-button {
    width: 30px;
    height: 30px;
    border: 1px solid gray;
    background: lightgray;
    margin: 2px;
  }

  .minesweeper-button.revealed {
    background: white;
  }
</style>
<script>
  const GRID_SIZE = 8;
  const NUM_MINES = 10;

  const createBoard = () => {
    const board = [];
    for (let i = 0; i < GRID_SIZE; i++) {
      board.push(Array(GRID_SIZE).fill().map(() => ({ isMine: false, isRevealed: false, isFlagged: false, neighborMines: 0 })));
    }
    return board;
  };

  const placeMines = (board, safeX, safeY) => {
    let minesPlaced = 0;
    while (minesPlaced < NUM_MINES) {
      const x = Math.floor(Math.random() * GRID_SIZE);
      const y = Math.floor(Math.random() * GRID_SIZE);
      if (!board[y][x].isMine && (x !== safeX || y !== safeY)) {
        board[y][x].isMine = true;
        minesPlaced++;
      }
    }

    for (let y = 0; y < GRID_SIZE; y++) {
      for (let x = 0; x < GRID_SIZE; x++) {
        if (!board[y][x].isMine) {
          board[y][x].neighborMines = countAdjacentMines(board, x, y);
        }
      }
    }
  };

  const countAdjacentMines = (board, x, y) => {
    let count = 0;
    for (let dy = -1; dy <= 1; dy++) {
      for (let dx = -1; dx <= 1; dx++) {
        const ny = y + dy;
        const nx = x + dx;
        if (ny >= 0 && ny < GRID_SIZE && nx >= 0 && nx < GRID_SIZE && board[ny][nx].isMine) {
          count++;
        }
      }
    }
    return count;
  };

  const revealCell = (board, x, y, firstClick, gameOver, gameWon) => {
    if (gameOver || gameWon || board[y][x].isFlagged) return { board, gameOver, gameWon };

    const newBoard = JSON.parse(JSON.stringify(board));

    if (firstClick) {
      placeMines(newBoard, x, y);
      firstClick = false;
    }

    revealRecursive(newBoard, x, y);

    if (newBoard[y][x].isMine) {
      gameOver = true;
    } else {
      const { won } = checkWinCondition(newBoard);
      gameWon = won;
    }

    return { board: newBoard, firstClick, gameOver, gameWon };
  };

  const revealRecursive = (board, x, y) => {
    if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE || board[y][x].isRevealed) return;

    board[y][x].isRevealed = true;

    if (board[y][x].neighborMines === 0) {
      for (let dy = -1; dy <= 1; dy++) {
        for (let dx = -1; dx <= 1; dx++) {
          revealRecursive(board, x + dx, y + dy);
        }
      }
    }
  };

  const toggleFlag = (board, x, y, gameOver, gameWon) => {
    if (gameOver || gameWon || board[y][x].isRevealed) return board;

    const newBoard = JSON.parse(JSON.stringify(board));
    newBoard[y][x].isFlagged = !newBoard[y][x].isFlagged;
    return newBoard;
  };

  const checkWinCondition = (board) => {
    const allNonMinesRevealed = board.every(row =>
      row.every(cell => cell.isMine || cell.isRevealed)
    );
    return { won: allNonMinesRevealed };
  };

  const resetGame = () => {
    return createBoard();
  };

  document.addEventListener('DOMContentLoaded', () => {
    const root = document.getElementById('minesweeper-root');

    let board = createBoard();
    let gameOver = false;
    let gameWon = false;
    let firstClick = true;

    const render = () => {
      root.innerHTML = `
        <div style="font-family: Arial, sans-serif; text-align: center; margin-top: 20px;">
          <h1>Minesweeper</h1>
          <div style="display: inline-block; border: 1px solid black; padding: 10px;">
            ${board.map((row, y) => `
              <div style="display: flex;">
                ${row.map((cell, x) => `
                  <button
                    onclick="handleClick(${x}, ${y})"
                    oncontextmenu="handleRightClick(event, ${x}, ${y})"
                    class="minesweeper-button ${cell.isRevealed ? 'revealed' : ''}"
                    style="width: 30px; height: 30px; border: 1px solid gray; background: ${cell.isRevealed ? 'white' : 'lightgray'}; margin: 2px;"
                  >
                    ${cell.isRevealed 
                      ? (cell.isMine ? '💣' : cell.neighborMines || '')
                      : cell.isFlagged ? '🚩' : ''}
                  </button>
                `).join('')}
              </div>
            `).join('')}
          </div>
          ${gameOver ? '<p>Game Over! You hit a mine.</p>' : ''}
          ${gameWon ? '<p>Congratulations! You won!</p>' : ''}
          ${(gameOver || gameWon) ? '<button onclick="handleReset()">New Game</button>' : ''}
        </div>
      `;
    };

    window.handleClick = (x, y) => {
      const result = revealCell(board, x, y, firstClick, gameOver, gameWon);
      board = result.board;
      firstClick = result.firstClick;
      gameOver = result.gameOver;
      gameWon = result.gameWon;
      render();
    };

    window.handleRightClick = (event, x, y) => {
      event.preventDefault();
      board = toggleFlag(board, x, y, gameOver, gameWon);
      render();
    };

    window.handleReset = () => {
      board = resetGame();
      gameOver = false;
      gameWon = false;
      firstClick = true;
      render();
    };

    render();
  });
</script>

</body>
</html>

React Component

import React, { useState, useEffect } from 'react';

const GRID_SIZE = 8;
const NUM_MINES = 10;

const createBoard = () => {
  const board = [];
  for (let i = 0; i < GRID_SIZE; i++) {
    board.push(Array(GRID_SIZE).fill().map(() => ({ isMine: false, isRevealed: false, isFlagged: false, neighborMines: 0 })));
  }
  return board;
};

const placeMines = (board, safeX, safeY) => {
  let minesPlaced = 0;
  while (minesPlaced < NUM_MINES) {
    const x = Math.floor(Math.random() * GRID_SIZE);
    const y = Math.floor(Math.random() * GRID_SIZE);
    if (!board[y][x].isMine && (x !== safeX || y !== safeY)) {
      board[y][x].isMine = true;
      minesPlaced++;
    }
  }

  for (let y = 0; y < GRID_SIZE; y++) {
    for (let x = 0; x < GRID_SIZE; x++) {
      if (!board[y][x].isMine) {
        board[y][x].neighborMines = countAdjacentMines(board, x, y);
      }
    }
  }
};

const countAdjacentMines = (board, x, y) => {
  let count = 0;
  for (let dy = -1; dy <= 1; dy++) {
    for (let dx = -1; dx <= 1; dx++) {
      const ny = y + dy;
      const nx = x + dx;
      if (ny >= 0 && ny < GRID_SIZE && nx >= 0 && nx < GRID_SIZE && board[ny][nx].isMine) {
        count++;
      }
    }
  }
  return count;
};

const Minesweeper = () => {
  const [board, setBoard] = useState(createBoard);
  const [gameOver, setGameOver] = useState(false);
  const [gameWon, setGameWon] = useState(false);
  const [firstClick, setFirstClick] = useState(true);

  const revealCell = (x, y) => {
    if (gameOver || gameWon || board[y][x].isFlagged) return;

    const newBoard = JSON.parse(JSON.stringify(board));

    if (firstClick) {
      placeMines(newBoard, x, y);
      setFirstClick(false);
    }

    revealRecursive(newBoard, x, y);

    if (newBoard[y][x].isMine) {
      setGameOver(true);
    } else {
      checkWinCondition(newBoard);
    }

    setBoard(newBoard);
  };

  const revealRecursive = (board, x, y) => {
    if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE || board[y][x].isRevealed) return;

    board[y][x].isRevealed = true;

    if (board[y][x].neighborMines === 0) {
      for (let dy = -1; dy <= 1; dy++) {
        for (let dx = -1; dx <= 1; dx++) {
          revealRecursive(board, x + dx, y + dy);
        }
      }
    }
  };

  const toggleFlag = (e, x, y) => {
    e.preventDefault();
    if (gameOver || gameWon || board[y][x].isRevealed) return;

    const newBoard = JSON.parse(JSON.stringify(board));
    newBoard[y][x].isFlagged = !newBoard[y][x].isFlagged;
    setBoard(newBoard);
  };

  const checkWinCondition = (board) => {
    const allNonMinesRevealed = board.every(row =>
      row.every(cell => cell.isMine || cell.isRevealed)
    );
    if (allNonMinesRevealed) {
      setGameWon(true);
    }
  };

  const resetGame = () => {
    setBoard(createBoard());
    setGameOver(false);
    setGameWon(false);
    setFirstClick(true);
  };

  return (
    <div style={{ fontFamily: 'Arial, sans-serif', textAlign: 'center', marginTop: '20px' }}>
      <h1>Minesweeper</h1>
      <div style={{ display: 'inline-block', border: '1px solid black', padding: '10px' }}>
        {board.map((row, y) => (
          <div key={y} style={{ display: 'flex' }}>
            {row.map((cell, x) => (
              <button
                key={x}
                onClick={() => revealCell(x, y)}
                onContextMenu={(e) => toggleFlag(e, x, y)}
                disabled={gameOver || gameWon}
                style={{
                  width: '30px',
                  height: '30px',
                  border: '1px solid gray',
                  background: cell.isRevealed ? 'white' : 'lightgray',
                  margin: '2px',
                }}
              >
                {cell.isRevealed 
                  ? (cell.isMine ? '💣' : cell.neighborMines || '')
                  : cell.isFlagged ? '🚩' : ''}
              </button>
            ))}
          </div>
        ))}
      </div>
      {gameOver && <p>Game Over! You hit a mine.</p>}
      {gameWon && <p>Congratulations! You won!</p>}
      {(gameOver || gameWon) && (
        <button onClick={resetGame}>New Game</button>
      )}
    </div>
  );
};

export default Minesweeper;

Leave a Reply