Polaris

我想创建一个扫雷小游戏

gemini-exp-1206 Web 工件创建者

好的,请问您对这个扫雷游戏有什么具体的要求吗?比如:

  1. 游戏界面和外观
    • 希望是什么样的主题风格?(例如:经典 Windows 风格、现代简约风格、卡通风格等)
    • 颜色、字体等方面有什么偏好?
    • 是否需要一些特殊的视觉效果?
  2. 游戏功能和交互
    • 游戏的初始棋盘大小?(例如:9x9、16x16、自定义大小)
    • 地雷数量?
    • 是否需要标记地雷的功能(例如:右键插旗)?
    • 是否需要计时器或计步器?
    • 是否需要显示剩余地雷数量?
    • 游戏结束时(踩到地雷或成功排雷)有什么样的提示?
    • 是否需要重新开始游戏的按钮?
  3. 其他
    • 是否需要响应式设计,以适应不同大小的屏幕?
    • 是否有其他特殊要求或限制?

请您尽可能详细地描述您的想法,我会根据您的需求来设计和构建这个扫雷游戏。

Polaris
  1. 游戏界面和外观
  • 希望是什么样的主题风格?卡通风格
  • 颜色、字体等方面有什么偏好?苹果字体
  • 是否需要一些特殊的视觉效果?踩中地雷有爆炸效果
  1. 游戏功能和交互
  • 游戏的初始棋盘大小?10x10
  • 地雷数量,分为容易,中等,困难三个难度
  • 是否需要标记地雷的功能,右键插旗
  • 是否需要计时器或计步器,计步器
  • 是否需要显示剩余地雷数量,需要。
  • 游戏结束时(踩到地雷或成功排雷)有什么样的提示?您来设计
  • 是否需要重新开始游戏的按钮?需要
  1. 其他
  • 是否需要响应式设计,以适应不同大小的屏幕?适应discourse帖子的web工件
gemini-exp-1206 Web 工件创建者

好的,我了解了您的需求。我将为您创建一个卡通风格的扫雷游戏,具体规格如下:

目的和功能:

  • 创建一个经典的扫雷游戏,让用户在 10x10 的棋盘上排雷。
  • 提供三种难度级别:容易、中等和困难,分别对应不同数量的地雷。
  • 用户可以通过左键点击方块来排雷,右键点击方块来标记/取消标记地雷(插旗)。
  • 游戏会记录用户的步数,并显示剩余地雷数量。

视觉设计要求:

  • 采用卡通风格设计,色彩鲜明、活泼。
  • 使用苹果字体。
  • 踩到地雷时有爆炸效果。
  • 方块、旗帜、地雷等元素要有清晰的视觉区分。

交互元素和行为:

  • 棋盘:10x10 的方格,初始状态下所有方块都未被打开。
  • 方块
    • 未打开状态:显示为统一的背景图案。
    • 已打开状态:
      • 无雷:显示周围地雷数量的数字(1-8),如果没有地雷则自动展开周围的方块。
      • 有雷:显示地雷图标,触发爆炸效果,游戏结束。
    • 标记状态:右键点击可在未打开的方块上插旗/取消插旗,表示怀疑该方块有雷。
  • 难度选择:提供容易、中等、困难三个按钮,点击后开始对应难度的新游戏。
  • 计步器:显示用户当前已经点击(左键)的次数。
  • 剩余地雷数:显示当前棋盘上未被标记的 সম্ভাব্য的地雷数量(总地雷数 - 已插旗数量)。
  • 重新开始按钮:点击后重置游戏,棋盘、计步器、剩余地雷数都恢复初始状态,并根据当前选择的难度重新开始。
  • 游戏结束提示
    • 踩到地雷:弹出提示框,显示“Game Over!”,并显示所有地雷位置。
    • 成功排雷:弹出提示框,显示“You Win!”,并显示用户的步数和用时(如果需要计时)。
    • 爆炸效果。

数据处理:

  • 游戏开始时,根据选择的难度随机生成地雷位置。
  • 记录用户的每一步操作(点击、标记)。
  • 计算并显示剩余地雷数量。

具体要求或约束:

  • 响应式设计,适应 Discourse 帖子的 Web 工件的宽度。

接下来,我将使用这些规范来创建扫雷游戏。

View Source

HTML

<div id="app">
    <div class="header">
        <div class="difficulty">
            <button id="easy">Easy</button>
            <button id="medium">Medium</button>
            <button id="hard">Hard</button>
        </div>
        <div class="stats">
            <div class="steps">Steps: <span id="step-count">0</span></div>
            <div class="mines">Mines: <span id="mine-count">0</span></div>
        </div>
        <button id="reset">Reset</button>
    </div>
    <div id="board"></div>
</div>

CSS

#app {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    width: 100%;
    max-width: 500px;
    margin: 0 auto;
    user-select: none;
}

.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
    padding: 10px;
    background-color: #f0f0f0;
    border-radius: 5px;
}

.difficulty button {
    padding: 8px 12px;
    margin: 0 5px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #fff;
    cursor: pointer;
}

.difficulty button:hover, #reset:hover {
  background-color:#ADD8E6
}

.difficulty button:focus, #reset:focus {
    outline: none;
    box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
}


.stats {
    display: flex;
    gap: 20px;
}

#board {
    display: grid;
    grid-template-columns: repeat(10, 1fr);
    grid-template-rows: repeat(10, 1fr);
    gap: 2px;
    border: 2px solid #777;
    background-color: #777;
    width: 100%;
    aspect-ratio: 1 / 1;

}

.cell {
    width: 100%;
    height: 100%;
    background-color: #bbb;
    border: 2px outset #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 1.5em;
    font-weight: bold;
    cursor: pointer;
}

.cell.revealed {
    border: 1px solid #777;
    background-color: #ddd;
    cursor: default;
}

.cell.flagged {
    color: red;
}

.cell.mine {
    background-color: red;
}

.cell.exploded {
  background-color: orange;
}

#reset {
    padding: 8px 12px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #fff;
    cursor: pointer;
}

.hidden {
  display: none;
}

JavaScript

// Your complete JavaScript here
const boardElement = document.getElementById('board');
const stepCountElement = document.getElementById('step-count');
const mineCountElement = document.getElementById('mine-count');
const easyButton = document.getElementById('easy');
const mediumButton = document.getElementById('medium');
const hardButton = document.getElementById('hard');
const resetButton = document.getElementById('reset');

let boardSize = 10;
let mines = 10;
let steps = 0;
let board = [];
let revealedCells = 0;
let flaggedCells = 0;
let gameRunning = false;


function createBoard() {
    board = [];
    revealedCells = 0;
    flaggedCells = 0;
    steps = 0;
    stepCountElement.textContent = steps;
    mineCountElement.textContent = mines;
    boardElement.innerHTML = '';
    gameRunning = true;

    // Create cells
    for (let row = 0; row < boardSize; row++) {
        let rowArray = [];
        for (let col = 0; col < boardSize; col++) {
            const cell = {
                row,
                col,
                isMine: false,
                isRevealed: false,
                isFlagged: false,
                neighborMines: 0
            };
            rowArray.push(cell);

            const cellElement = document.createElement('div');
            cellElement.classList.add('cell');
            cellElement.addEventListener('click', () => handleCellClick(cell));
            cellElement.addEventListener('contextmenu', (e) => {
                e.preventDefault();
                handleCellRightClick(cell);
            });
            boardElement.appendChild(cellElement);
        }
        board.push(rowArray);
    }

    // Place mines randomly
    let minesPlaced = 0;
    while (minesPlaced < mines) {
        const randomRow = Math.floor(Math.random() * boardSize);
        const randomCol = Math.floor(Math.random() * boardSize);
        if (!board[randomRow][randomCol].isMine) {
            board[randomRow][randomCol].isMine = true;
            minesPlaced++;
        }
    }

    // Calculate neighbor mines
    for (let row = 0; row < boardSize; row++) {
        for (let col = 0; col < boardSize; col++) {
            if (!board[row][col].isMine) {
                board[row][col].neighborMines = countNeighborMines(row, col);
            }
        }
    }
}

function countNeighborMines(row, col) {
    let count = 0;
    for (let r = Math.max(0, row - 1); r <= Math.min(boardSize - 1, row + 1); r++) {
        for (let c = Math.max(0, col - 1); c <= Math.min(boardSize - 1, col + 1); c++) {
            if (board[r][c].isMine) {
                count++;
            }
        }
    }
    return count;
}

function handleCellClick(cell) {
  if (!gameRunning || cell.isRevealed || cell.isFlagged) {
        return;
    }

    if(!cell.isRevealed) {
        steps++;
        stepCountElement.textContent = steps;
    }


    if (cell.isMine) {
        revealAllMines();
        cell.isRevealed = true;
         const cellElement = boardElement.children[cell.row * boardSize + cell.col];
        cellElement.classList.add('exploded');

        gameOver();
    } else {
        revealCell(cell);
        if (revealedCells === boardSize * boardSize - mines) {
            gameWin();
        }
    }
}

function handleCellRightClick(cell) {
  if (!gameRunning || cell.isRevealed) {
    return;
  }

  if (!cell.isFlagged) {
        if(flaggedCells < mines) {
            cell.isFlagged = true;
            flaggedCells++;
            const cellElement = boardElement.children[cell.row * boardSize + cell.col];
            cellElement.classList.add('flagged');
            cellElement.textContent = "🚩";
        }
    }
  else {
    cell.isFlagged = false;
    flaggedCells--;
    const cellElement = boardElement.children[cell.row * boardSize + cell.col];
     cellElement.classList.remove('flagged');
     cellElement.textContent = "";

  }
   mineCountElement.textContent = mines - flaggedCells;
}

function revealCell(cell) {
    if (cell.isRevealed || cell.isFlagged) {
        return;
    }

    cell.isRevealed = true;
    revealedCells++;
    const cellElement = boardElement.children[cell.row * boardSize + cell.col];
    cellElement.classList.add('revealed');

    if (cell.neighborMines > 0) {
        cellElement.textContent = cell.neighborMines;
    } else {
        // Reveal neighboring cells recursively
        for (let r = Math.max(0, cell.row - 1); r <= Math.min(boardSize - 1, cell.row + 1); r++) {
            for (let c = Math.max(0, cell.col - 1); c <= Math.min(boardSize - 1, cell.col + 1); c++) {
                revealCell(board[r][c]);
            }
        }
    }
}


function revealAllMines() {
    for (let row = 0; row < boardSize; row++) {
        for (let col = 0; col < boardSize; col++) {
            if (board[row][col].isMine) {
                const cellElement = boardElement.children[row * boardSize + col];
                cellElement.classList.add('mine');
                cellElement.textContent = '💣';
            }
        }
    }
}


function gameOver() {
    gameRunning = false;
    alert("Game Over!");
}

function gameWin() {
    gameRunning = false;
    alert(`You Win! Steps: ${steps}`);
}

function setDifficulty(difficulty) {
    switch (difficulty) {
        case 'easy':
            mines = 10;
            break;
        case 'medium':
            mines = 20;
            break;
        case 'hard':
            mines = 30;
            break;
    }
    createBoard();
}

easyButton.addEventListener('click', () => setDifficulty('easy'));
mediumButton.addEventListener('click', () => setDifficulty('medium'));
hardButton.addEventListener('click', () => setDifficulty('hard'));
resetButton.addEventListener('click', createBoard);

// Initialize game
createBoard();

Preview