0% found this document useful (0 votes)
9 views

minesweeper-game.tsx

The document is a React component for a Minesweeper game, featuring difficulty levels (beginner, intermediate, expert) and a welcome screen for selecting difficulty. It includes game logic for initializing the game, placing mines, revealing cells, and handling user interactions such as flagging cells and clicking to reveal. The game also incorporates an alert dialog for game over and win conditions, allowing players to restart or return to the menu.

Uploaded by

maxgab201
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views

minesweeper-game.tsx

The document is a React component for a Minesweeper game, featuring difficulty levels (beginner, intermediate, expert) and a welcome screen for selecting difficulty. It includes game logic for initializing the game, placing mines, revealing cells, and handling user interactions such as flagging cells and clicking to reveal. The game also incorporates an alert dialog for game over and win conditions, allowing players to restart or return to the menu.

Uploaded by

maxgab201
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 6

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

import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle,


AlertDialogDescription, AlertDialogFooter, AlertDialogAction } from
'@/components/ui/alert-dialog';
import { Flag, Mouse, ArrowLeft } from 'lucide-react';

const DIFFICULTY_LEVELS = {
beginner: {
rows: 9,
cols: 9,
mines: 10,
name: 'Principiante',
cellSize: 'w-7 h-7'
},
intermediate: {
rows: 16,
cols: 16,
mines: 40,
name: 'Intermedio',
cellSize: 'w-5 h-5'
},
expert: {
rows: 16,
cols: 30,
mines: 99,
name: 'Experto',
cellSize: 'w-4 h-4'
}
};

const WelcomeScreen = ({ onStartGame }) => {


return (
<div className="flex flex-col items-center gap-8 p-8">
<h1 className="text-4xl font-bold text-blue-600">Buscaminas</h1>

<div className="text-xl text-gray-600 mb-4">


Selecciona un nivel de dificultad
</div>

<div className="flex flex-col gap-4 w-64">


{Object.entries(DIFFICULTY_LEVELS).map(([level, config]) => (
<button
key={level}
className="px-6 py-4 bg-blue-500 text-white rounded-lg hover:bg-blue-
600 transition-colors flex flex-col items-center"
onClick={() => onStartGame(level)}
>
<span className="text-lg font-bold">{config.name}</span>
<span className="text-sm">
{config.rows}x{config.cols}, {config.mines} minas
</span>
</button>
))}
</div>
</div>
);
};

const Cell = ({ value, revealed, flagged, onClick, cellSize }) => {


const getCellContent = () => {
if (!revealed && !flagged) return '';
if (flagged) return '🚩';
if (value === -1) return '💣';
return value === 0 ? '' : value;
};

const getBackgroundColor = () => {


if (!revealed) return 'bg-gray-300';
if (value === -1) return 'bg-red-500';
return 'bg-gray-100';
};

const getFontSize = () => {


if (cellSize.includes('w-4')) return 'text-xs';
if (cellSize.includes('w-5')) return 'text-sm';
return 'text-base';
};

return (
<button
className={`${cellSize} border border-gray-400 flex items-center justify-
center font-bold ${getBackgroundColor()} hover:bg-gray-200 ${getFontSize()}`}
onClick={onClick}
>
{getCellContent()}
</button>
);
};

const Minesweeper = () => {


const [difficulty, setDifficulty] = useState(null);
const [grid, setGrid] = useState([]);
const [revealed, setRevealed] = useState([]);
const [flagged, setFlagged] = useState([]);
const [gameOver, setGameOver] = useState(false);
const [gameWon, setGameWon] = useState(false);
const [firstClick, setFirstClick] = useState(true);
const [flagMode, setFlagMode] = useState(false);

const initializeGame = useCallback((selectedDifficulty) => {


const config = DIFFICULTY_LEVELS[selectedDifficulty];
const newGrid = Array(config.rows).fill().map(() =>
Array(config.cols).fill(0));
const newRevealed = Array(config.rows).fill().map(() =>
Array(config.cols).fill(false));
const newFlagged = Array(config.rows).fill().map(() =>
Array(config.cols).fill(false));

setDifficulty(selectedDifficulty);
setGrid(newGrid);
setRevealed(newRevealed);
setFlagged(newFlagged);
setGameOver(false);
setGameWon(false);
setFirstClick(true);
setFlagMode(false);
}, []);
const returnToMenu = () => {
setDifficulty(null);
};

const placeMines = (firstRow, firstCol) => {


const config = DIFFICULTY_LEVELS[difficulty];
const newGrid = Array(config.rows).fill().map(() =>
Array(config.cols).fill(0));
let minesPlaced = 0;

const safeZone = [];


for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
if (firstRow + i >= 0 && firstRow + i < config.rows && firstCol + j >= 0 &&
firstCol + j < config.cols) {
safeZone.push([firstRow + i, firstCol + j]);
}
}
}

while (minesPlaced < config.mines) {


const row = Math.floor(Math.random() * config.rows);
const col = Math.floor(Math.random() * config.cols);

const isSafe = safeZone.some(([r, c]) => r === row && c === col);

if (!isSafe && newGrid[row][col] !== -1) {


newGrid[row][col] = -1;
minesPlaced++;

for (let i = -1; i <= 1; i++) {


for (let j = -1; j <= 1; j++) {
if (row + i >= 0 && row + i < config.rows && col + j >= 0 && col + j <
config.cols) {
if (newGrid[row + i][col + j] !== -1) {
newGrid[row + i][col + j]++;
}
}
}
}
}
}

setGrid(newGrid);

const newRevealed = [...revealed];


safeZone.forEach(([row, col]) => {
revealCellAndNeighbors(newGrid, newRevealed, row, col);
});
setRevealed(newRevealed);
};

const revealCellAndNeighbors = (grid, revealed, row, col) => {


const config = DIFFICULTY_LEVELS[difficulty];
if (row < 0 || row >= config.rows || col < 0 || col >= config.cols ||
revealed[row][col]) {
return;
}
revealed[row][col] = true;

if (grid[row][col] === 0) {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
revealCellAndNeighbors(grid, revealed, row + i, col + j);
}
}
}
};

const handleCellClick = (row, col) => {


if (gameOver || gameWon) return;

if (flagMode) {
if (!revealed[row][col]) {
const newFlagged = [...flagged];
newFlagged[row][col] = !newFlagged[row][col];
setFlagged(newFlagged);
}
} else {
if (!flagged[row][col]) {
if (firstClick) {
setFirstClick(false);
placeMines(row, col);
} else {
revealCell(row, col);
}
}
}
};

const revealCell = (row, col) => {


if (revealed[row][col] || flagged[row][col] || gameOver || gameWon) return;

const newRevealed = [...revealed];


newRevealed[row][col] = true;
setRevealed(newRevealed);

if (grid[row][col] === -1) {


setGameOver(true);
return;
}

const config = DIFFICULTY_LEVELS[difficulty];


if (grid[row][col] === 0) {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
if (row + i >= 0 && row + i < config.rows && col + j >= 0 && col + j <
config.cols) {
if (!revealed[row + i][col + j] && !flagged[row + i][col + j]) {
revealCell(row + i, col + j);
}
}
}
}
}

const totalRevealed = newRevealed.flat().filter(cell => cell).length;


if (totalRevealed === (config.rows * config.cols) - config.mines) {
setGameWon(true);
}
};

React.useEffect(() => {
if (difficulty) {
initializeGame(difficulty);
}
}, [difficulty, initializeGame]);

if (!difficulty) {
return <WelcomeScreen onStartGame={initializeGame} />;
}

const config = DIFFICULTY_LEVELS[difficulty];

return (
<div className="flex flex-col items-center gap-4">
<div className="flex items-center w-full justify-between px-4">
<button
className="flex items-center gap-2 text-blue-500 hover:text-blue-600"
onClick={returnToMenu}
>
<ArrowLeft size={20} />
Volver al menú
</button>
<div className="text-2xl font-bold">
{config.name}
</div>
</div>

<div className="flex gap-4 mb-4">


<button
className={`px-4 py-2 rounded flex items-center gap-2 ${
!flagMode ? 'bg-blue-500 text-white' : 'bg-gray-200'
}`}
onClick={() => setFlagMode(false)}
>
<Mouse size={20} /> Revelar
</button>
<button
className={`px-4 py-2 rounded flex items-center gap-2 ${
flagMode ? 'bg-blue-500 text-white' : 'bg-gray-200'
}`}
onClick={() => setFlagMode(true)}
>
<Flag size={20} /> Colocar Bandera
</button>
</div>

<div className="bg-gray-100 p-4 rounded-lg shadow-lg overflow-auto max-h-


[70vh]">
<div className="flex flex-col">
{grid.map((row, rowIndex) => (
<div key={rowIndex} className="flex">
{row.map((cell, colIndex) => (
<Cell
key={`${rowIndex}-${colIndex}`}
value={cell}
revealed={revealed[rowIndex][colIndex]}
flagged={flagged[rowIndex][colIndex]}
onClick={() => handleCellClick(rowIndex, colIndex)}
cellSize={config.cellSize}
/>
))}
</div>
))}
</div>
</div>

<button
className="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
onClick={() => initializeGame(difficulty)}
>
Nuevo Juego
</button>

<AlertDialog open={gameOver || gameWon}>


<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{gameOver ? '¡Game Over!' : '¡Felicidades!'}
</AlertDialogTitle>
<AlertDialogDescription>
{gameOver
? '¿Quieres intentarlo de nuevo?'
: '¡Has ganado! ¿Quieres jugar otra partida?'}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogAction onClick={() => initializeGame(difficulty)}>
Nuevo Juego
</AlertDialogAction>
<AlertDialogAction onClick={returnToMenu}>
Volver al Menú
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
};

export default Minesweeper;

You might also like