✨ (game): Generate grid & User Interactions
Generate grid with bombs & numbers, user interactions like, click on a cell, pin a flag
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
//! Imports
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------ React --------------------------------------------------------
|
||||
import { MouseEvent, useCallback, useEffect, useState } from 'react';
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------ Utils --------------------------------------------------------
|
||||
import { Difficulty } from '../../types/game';
|
||||
import { discoverAroundCell, genBombs, genGrid, genNumbers } from '../../utils/game';
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------- Assets --------------------------------------------------------
|
||||
import Bomb from '../../assets/bomb.png';
|
||||
import Flag from '../../assets/flag.png';
|
||||
import './styles.scss';
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
const Grid = ({ size = 12, difficulty = 'beginner' }) => {
|
||||
const [grid, setGrid] = useState(genGrid(size));
|
||||
const [gameStatus, setGameStatus] = useState('idle');
|
||||
|
||||
const [, updateGrid] = useState({});
|
||||
const forceUpdate = useCallback(() => updateGrid({}), []);
|
||||
|
||||
// ----------------------------------------------------- Actions -------------------------------------------------------
|
||||
const handleLeftClick = (ev: MouseEvent, rowIndex: number, colIndex: number) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (grid[rowIndex][colIndex].hidden && !grid[rowIndex][colIndex].flag) {
|
||||
setGrid((prev) => {
|
||||
if (gameStatus === 'idle') {
|
||||
do {
|
||||
prev = genNumbers(genBombs(prev, difficulty as Difficulty));
|
||||
} while (prev[rowIndex][colIndex].value !== 'empty');
|
||||
setGameStatus('started');
|
||||
}
|
||||
|
||||
return discoverAroundCell(prev, rowIndex, colIndex);
|
||||
});
|
||||
forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
const handleRightClick = (ev: MouseEvent, rowIndex: number, colIndex: number) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (grid[rowIndex][colIndex].hidden) {
|
||||
setGrid((prev) => {
|
||||
return prev.map((row, idx) => {
|
||||
if (idx === rowIndex) {
|
||||
return row.map((col, idy) => {
|
||||
if (idy === colIndex) {
|
||||
return {
|
||||
...col,
|
||||
flag: !col.flag,
|
||||
};
|
||||
}
|
||||
return col;
|
||||
});
|
||||
}
|
||||
return row;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// --- Development
|
||||
useEffect(() => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
document.addEventListener('keyup', (ev) => {
|
||||
if (ev.key === 'v') {
|
||||
setGrid(() => {
|
||||
let newGrid = genGrid(size);
|
||||
|
||||
return genNumbers(genBombs(newGrid, difficulty as Difficulty));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [size, difficulty]);
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
return (
|
||||
<div
|
||||
className='grid'
|
||||
style={{
|
||||
gridTemplateRows: `repeat(${size}, var(--size))`,
|
||||
}}
|
||||
>
|
||||
{grid.map((row, rowIndex) => (
|
||||
<div
|
||||
key={`row${rowIndex}`}
|
||||
className='row'
|
||||
style={{
|
||||
gridTemplateColumns: `repeat(${size}, var(--size))`,
|
||||
}}
|
||||
>
|
||||
{row.map((cell, cellIndex) => {
|
||||
return (
|
||||
<button
|
||||
key={`cell${cellIndex}`}
|
||||
className={`cell ${cell.hidden ? 'hidden' : cell.value !== 'empty' ? `v${cell.value}` : ''}`}
|
||||
onClick={(ev) => handleLeftClick(ev, rowIndex, cellIndex)}
|
||||
onContextMenu={(ev) => handleRightClick(ev, rowIndex, cellIndex)}
|
||||
>
|
||||
{cell.hidden && cell.flag && (
|
||||
<img
|
||||
src={Flag}
|
||||
alt='flat'
|
||||
/>
|
||||
)}
|
||||
{!cell.hidden &&
|
||||
(cell.value === 'bomb' ? (
|
||||
<img
|
||||
src={Bomb}
|
||||
alt='bomb'
|
||||
/>
|
||||
) : cell.value !== 'empty' ? (
|
||||
cell.value
|
||||
) : (
|
||||
''
|
||||
))}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Grid;
|
||||
@@ -0,0 +1,76 @@
|
||||
.grid {
|
||||
--size: 30px;
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
padding: 2px;
|
||||
border-radius: 5px;
|
||||
background-color: #ffff;
|
||||
overflow: hidden;
|
||||
|
||||
& > .row {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
height: var(--size);
|
||||
|
||||
& > .cell {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: var(--size);
|
||||
border: none;
|
||||
background-color: #f6f6f6;
|
||||
font-weight: 700;
|
||||
outline: none;
|
||||
|
||||
&.hidden {
|
||||
background-color: #dedede;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.v1 {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
&.v2 {
|
||||
color: green;
|
||||
}
|
||||
|
||||
&.v3 {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
&.v4 {
|
||||
color: red;
|
||||
}
|
||||
|
||||
&.v5 {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
& > img {
|
||||
width: 70%;
|
||||
height: 70%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child > .cell {
|
||||
&:first-child {
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
}
|
||||
&:last-child > .cell {
|
||||
&:first-child {
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user