Skip to content

Commit

Permalink
feat(gameplay): add ability to specify rows, cols, and mines counts
Browse files Browse the repository at this point in the history
Add inputs for specifying number of rows, columns, and mines to adjust difficulty.

closes #37
  • Loading branch information
JacobTheEldest committed May 26, 2023
1 parent 7c18d13 commit 4b0e77a
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Versioning and Build
- [x] Each cell is an object that stores pertinent information for that cell (ie. wasClicked, isBomb, adjacentBombCount...)
- [x] One way to store these values to keep track of position is an adjacency matrix .
- [x] One could also have a property pointing to adjacent cells directly on the cell object.
- [ ] Add the ability to change the difficulty (size of the board and quantity of mines).
- [x] Add the ability to change the difficulty (size of the board and quantity of mines).
- [ ] Add a timer to the game.
- [ ] View a list of recently played user times and difficulty setting.
- [ ] Define a RESTful API that connects to a postgres database.
Expand Down
51 changes: 21 additions & 30 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,36 @@
margin-bottom: 10px;
}

.game-result {
color: red;
.game-difficulty {
text-align: center;
margin-bottom: 10px;
display: grid;
grid: 1fr 1fr 1fr / 1fr auto 1fr;
}

/* .App {
text-align: center;
.game-difficulty span {
grid-column: 1 / 2;
text-align: end;
}

.App-logo {
height: 40vmin;
pointer-events: none;
.game-difficulty input {
grid-column: 2 / 3;
max-width: 50px;
margin: auto 10px;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
.game-difficulty .columns {
grid-row: 1 / 2;
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
.game-difficulty .rows {
grid-row: 2 / 3;
}

.App-link {
color: #61dafb;
.game-difficulty button {
grid-area: 1/3/4/4;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
} */
.game-result {
color: red;
}
70 changes: 60 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export interface BoardContextInterface {
setAttributeCount: Dispatch<React.SetStateAction<AttributeCount>>;
gameResult: GameResult;
setGameResult: Dispatch<React.SetStateAction<GameResult>>;
rows: number;
cols: number;
mines: number;
setCurrentMines: Dispatch<React.SetStateAction<number>>;
}
export const BoardContext = createContext<BoardContextInterface | undefined>(
undefined,
Expand All @@ -30,13 +34,25 @@ const App: React.FC = () => {

const [gameResult, setGameResult] = useState<GameResult>('');

const global = {
rows: 20,
cols: 30,
mines: 10,
const [rows, setRows] = useState(10);
const [cols, setCols] = useState(10);
const [mines, setMines] = useState(10);
const [currentMines, setCurrentMines] = useState(10);

const handleColumnsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setCols(parseInt(e.target.value));
};

const handleRowsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setRows(parseInt(e.target.value));
};

const handleMinesChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setMines(parseInt(e.target.value));
};

const handleGameRestartClick = () => {
const handleNewGameClick = (e: React.FormEvent) => {
e.preventDefault();
setGameResult('');
setGameId(gameId + 1);

Expand All @@ -62,6 +78,10 @@ const App: React.FC = () => {
setAttributeCount,
gameResult,
setGameResult,
rows,
cols,
mines,
setCurrentMines,
}}
>
<div className="">
Expand All @@ -70,17 +90,47 @@ const App: React.FC = () => {
Game ID: {gameId}
<br />
Remaining Unflagged Mines:{' '}
{global.mines - attributeCount.flaggedCount >= 0
? global.mines - attributeCount.flaggedCount
{/* TODO: store initial mine count for this */}
{currentMines - attributeCount.flaggedCount >= 0
? currentMines - attributeCount.flaggedCount
: 0}
<br />
<form className="game-difficulty" onSubmit={handleNewGameClick}>
<span className="columns">Columns:</span>
<input
type="number"
name="columns"
className="columns"
onChange={handleColumnsChange}
value={cols}
/>

<span className="rows">Rows:</span>
<input
type="number"
name="rows"
className="rows"
onChange={handleRowsChange}
value={rows}
/>

<span className="mines">Mines:</span>
<input
type="number"
name="mines"
className="mines-input"
onChange={handleMinesChange}
value={mines}
/>

<button type="submit">New Game</button>
</form>
<span className="game-result">{endMessage}</span>
<br />
{gameResult !== '' ? (
<button onClick={handleGameRestartClick}>Restart</button>
<button onClick={handleNewGameClick}>Restart</button>
) : null}
</div>
<Board global={global} />
<Board />
<br />
</div>
</BoardContext.Provider>
Expand Down
37 changes: 16 additions & 21 deletions src/components/Board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import './Board.css';
import { useContext, useState } from 'react';
import { BoardContext, BoardContextInterface } from '../App.tsx';

interface Global {
rows: number;
cols: number;
mines: number;
}
const Board = ({ global }: { global: Global }) => {
const Board = () => {
const tmpBoardContext = useContext(BoardContext);
const { gameId, setAttributeCount, gameResult, setGameResult } =
tmpBoardContext as BoardContextInterface;
const {
gameId,
setAttributeCount,
gameResult,
setGameResult,
rows,
cols,
mines,
setCurrentMines,
} = tmpBoardContext as BoardContextInterface;

type Board = Array<Array<cell>>;
const [board, setBoard] = useState<Board>([]);
Expand Down Expand Up @@ -79,11 +82,7 @@ const Board = ({ global }: { global: Global }) => {
};
}

const buildBoard = (
rows = global.rows,
cols = global.cols,
mines = global.mines,
) => {
const buildBoard = (rows: number, cols: number, mines: number) => {
const emptyBoard = generateEmptyBoard(rows, cols);

const minedBoard = placeMines(rows, cols, mines, emptyBoard);
Expand All @@ -98,10 +97,7 @@ const Board = ({ global }: { global: Global }) => {
setBoard(minedBoard);
};

const generateEmptyBoard = (
rows = global.rows,
cols = global.cols,
): Board => {
const generateEmptyBoard = (rows: number, cols: number): Board => {
const grid = [];
for (let row = 0; row < rows; row++) {
const rowArray = [];
Expand All @@ -119,14 +115,13 @@ const Board = ({ global }: { global: Global }) => {
mines: number,
board: Board,
) => {
setCurrentMines(mines);
const updatedBoard = board;

while (mines > 0) {
const randRow = randInRange(0, rows - 1);
const randCol = randInRange(0, cols - 1);

// console.log('placing mine at:', randRow, randCol);
// mines--;
if (!updatedBoard[randRow][randCol].mine) {
updatedBoard[randRow][randCol].setMined();
mines--;
Expand All @@ -153,7 +148,7 @@ const Board = ({ global }: { global: Global }) => {

const hiddenCount = board.length * board[0].length - revealedCount;

if (hiddenCount === global.mines) {
if (hiddenCount === mines) {
setGameResult('win');
}

Expand Down Expand Up @@ -263,7 +258,7 @@ const Board = ({ global }: { global: Global }) => {

// Build new boards when necessary
if (renderedGame !== gameId) {
buildBoard();
buildBoard(rows, cols, mines);
setRenderedGame(gameId);
}

Expand Down

0 comments on commit 4b0e77a

Please sign in to comment.