Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nick/on click on reset #28

Merged
merged 6 commits into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,6 @@
### Cell component tests
### Custom hooks
### Game Field (grid) component

### Static game

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/26)

### Components library review

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/18)
Expand All @@ -164,6 +159,23 @@

## Section 8: React hooks advanced

### Static game

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/26)

### Game logic

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/24)

#### Components refactoring and update dependencies

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/27/files)

### useState and player field generator
### Test player field generator
### Generate game field and open cell handler
### Debug session and useMemo
### Click to the cell test cases
### Reset game by TDD

[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/28/files)
4,955 changes: 2,687 additions & 2,268 deletions package-lock.json

Large diffs are not rendered by default.

46 changes: 23 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,46 +34,46 @@
},
"homepage": "https://github.com/nickovchinnikov/minesweeper#readme",
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/preset-env": "^7.14.5",
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@emotion/jest": "^11.3.0",
"@storybook/addon-actions": "^6.3.4",
"@storybook/addon-essentials": "^6.3.4",
"@storybook/addon-links": "^6.3.4",
"@storybook/builder-webpack5": "^6.3.4",
"@storybook/manager-webpack5": "^6.3.4",
"@storybook/react": "^6.3.4",
"@stryker-mutator/core": "^5.2.2",
"@stryker-mutator/jest-runner": "^5.2.2",
"@storybook/addon-actions": "^6.3.6",
"@storybook/addon-essentials": "^6.3.6",
"@storybook/addon-links": "^6.3.6",
"@storybook/builder-webpack5": "^6.3.6",
"@storybook/manager-webpack5": "^6.3.6",
"@storybook/react": "^6.3.6",
"@stryker-mutator/core": "^5.2.3",
"@stryker-mutator/jest-runner": "^5.2.3",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/react-hooks": "^7.0.1",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^26.0.23",
"@types/react": "^17.0.13",
"@types/react-dom": "^17.0.8",
"@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.27.0",
"@types/jest": "^26.0.24",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"babel-loader": "^8.2.2",
"chromatic": "^5.9.2",
"css-loader": "^5.2.6",
"eslint": "^7.29.0",
"css-loader": "^6.2.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"html-webpack-plugin": "^5.3.2",
"husky": "^7.0.1",
"jest": "^27.0.5",
"prettier": "^2.3.1",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"stryker": "^0.35.1",
"stryker-cli": "^1.0.2",
"style-loader": "^3.0.0",
"typescript": "^4.3.4",
"style-loader": "^3.2.1",
"typescript": "^4.3.5",
"url-loader": "^4.1.1",
"webpack": "^5.43.0",
"webpack": "^5.48.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/Game/Game.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ GameExample.args = {
bombs="000"
levels={['beginner', 'intermediate', 'expert']}
onReset={() => null}
onChange={() => null}
onChangeLevel={() => null}
/>
<GameOver onClick={() => null} isWin={true} />
<Grid onClick={() => null} onContextMenu={() => null}>
Expand Down
1 change: 1 addition & 0 deletions src/components/Scoreboard/Level.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ const Template: Story<LevelProps> = (args) => <Level {...args} />;
export const LevelExample = Template.bind({});
LevelExample.args = {
children: ['beginner', 'intermediate', 'expert'],
value: 'intermediate',
};
8 changes: 6 additions & 2 deletions src/components/Scoreboard/Level.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ export interface LevelProps {
* Array of possible game levels
*/
children: string[];
/**
* Default value
*/
value?: string;
/**
* Select new lvl handler
*/
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
}

export const Level: FC<LevelProps> = ({ children, onChange }) => (
<Select onChange={onChange}>
export const Level: FC<LevelProps> = ({ children, value, onChange }) => (
<Select onChange={onChange} value={value}>
{children.map((item: string) => (
<Option key={item} value={item}>
{item}
Expand Down
1 change: 1 addition & 0 deletions src/components/Scoreboard/Scoreboard.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ export const ScoreboardExample = Template.bind({});
ScoreboardExample.args = {
time: '000',
levels: ['beginner', 'intermediate', 'expert'],
defaultLevel: 'intermediate',
bombs: '010',
};
4 changes: 2 additions & 2 deletions src/components/Scoreboard/Scoreboard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Scoreboard test cases', () => {
levels={['beginner', 'intermediate', 'expert']}
bombs="010"
onReset={() => null}
onChange={() => null}
onChangeLevel={() => null}
/>
);

Expand All @@ -26,7 +26,7 @@ describe('Scoreboard test cases', () => {
levels={['beginner', 'intermediate', 'expert']}
bombs="010"
onReset={() => null}
onChange={onChange}
onChangeLevel={onChange}
/>
);

Expand Down
13 changes: 10 additions & 3 deletions src/components/Scoreboard/Scoreboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ export interface ScoreboardProps {
* Possible game scenarios
*/
levels: string[];
/**
* Default selected level
*/
defaultLevel?: string;
/**
* Action handler when the GameReset button is clicked
*/
onReset: () => void;
/**
* Action handler when select new lvl
*/
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
onChangeLevel: (event: ChangeEvent<HTMLSelectElement>) => void;
/**
* Bombs in the field
*/
Expand All @@ -31,14 +35,17 @@ export interface ScoreboardProps {
export const Scoreboard: FC<ScoreboardProps> = ({
time,
levels,
defaultLevel,
bombs,
onReset,
onChange,
onChangeLevel: onChange,
}) => (
<Wrapper>
<Counter>{time}</Counter>
<div>
<Level onChange={onChange}>{levels}</Level>
<Level onChange={onChange} value={defaultLevel}>
{levels}
</Level>
<Reset onReset={onReset} />
</div>
<Counter>{bombs}</Counter>
Expand Down
8 changes: 2 additions & 6 deletions src/helpers/CellsManipulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,12 @@ export const openCell = (

for (const [y, x] of Object.values(items)) {
if (checkItemInField([y, x], gameField)) {
const gameCell = gameField[y][x];
const playerCell = playerField[y][x];
const gameCell = gameField[y][x];

if (gameCell === empty && playerCell === hidden) {
if (playerCell === hidden && gameCell !== bomb) {
playerField = openCell([y, x], playerField, gameField);
}

if (gameCell < bomb) {
playerField[y][x] = gameCell;
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const CellState: Record<string, Cell> = {
* @param {Cell} state
* @returns {Field}
*/
export const emptyFieldGenerator = (
export const generateFieldWithDefaultState = (
size: number,
state: Cell = CellState.empty
): Field => new Array(size).fill(null).map(() => new Array(size).fill(state));
Expand Down Expand Up @@ -57,7 +57,7 @@ export const fieldGenerator = (size: number, probability: number): Field => {
let unprocessedCells = size * size;
let restCellsWithBombs = unprocessedCells * probability;

const result: Field = emptyFieldGenerator(size);
const result: Field = generateFieldWithDefaultState(size);

for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
Expand Down
13 changes: 9 additions & 4 deletions src/helpers/Fileld.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { emptyFieldGenerator, fieldGenerator, CellState, Cell } from './Field';
import {
generateFieldWithDefaultState,
fieldGenerator,
CellState,
Cell,
} from './Field';

const { empty, bomb, hidden } = CellState;

Expand All @@ -7,19 +12,19 @@ const cellWithBombFilter = (cell: Cell) => cell === bomb;
describe('Field Generator', () => {
describe('emptyFieldGenerator tests', () => {
it('2x2', () => {
expect(emptyFieldGenerator(2)).toStrictEqual([
expect(generateFieldWithDefaultState(2)).toStrictEqual([
[empty, empty],
[empty, empty],
]);
});
it('3x3', () =>
expect(emptyFieldGenerator(3)).toStrictEqual([
expect(generateFieldWithDefaultState(3)).toStrictEqual([
[empty, empty, empty],
[empty, empty, empty],
[empty, empty, empty],
]));
it('3x3 with hidden state', () =>
expect(emptyFieldGenerator(3, hidden)).toStrictEqual([
expect(generateFieldWithDefaultState(3, hidden)).toStrictEqual([
[hidden, hidden, hidden],
[hidden, hidden, hidden],
[hidden, hidden, hidden],
Expand Down
74 changes: 74 additions & 0 deletions src/helpers/__mocks__/Field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
export const fieldGenerator = (size: number) => {
switch (size) {
case 9:
default:
return [
[0, 0, 0, 0, 0, 0, 1, 9, 1],
[0, 0, 0, 0, 1, 1, 2, 2, 2],
[0, 0, 0, 0, 1, 9, 1, 1, 9],
[0, 0, 1, 1, 3, 2, 2, 1, 1],
[0, 0, 1, 9, 2, 9, 1, 0, 0],
[1, 1, 2, 1, 2, 1, 1, 1, 1],
[1, 9, 1, 0, 0, 0, 0, 1, 9],
[1, 1, 1, 1, 1, 2, 1, 3, 2],
[0, 0, 0, 1, 9, 2, 9, 2, 9],
];
case 16:
return [
[9, 2, 2, 9, 2, 9, 1, 0, 0, 1, 9, 1, 0, 0, 0, 0],
[4, 9, 3, 2, 3, 2, 1, 0, 0, 2, 2, 2, 0, 1, 1, 1],
[9, 9, 2, 1, 9, 1, 0, 0, 0, 1, 9, 1, 0, 1, 9, 2],
[2, 2, 1, 1, 1, 2, 2, 3, 2, 2, 1, 1, 0, 1, 2, 9],
[0, 0, 0, 0, 0, 1, 9, 9, 9, 1, 0, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 1, 2, 4, 3, 3, 1, 2, 9, 2, 1, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 9, 2, 9, 2, 2, 9, 1, 0],
[0, 1, 1, 1, 1, 9, 1, 1, 1, 2, 1, 2, 2, 2, 2, 1],
[0, 1, 9, 1, 1, 1, 1, 1, 2, 2, 1, 1, 9, 1, 2, 9],
[1, 2, 2, 1, 1, 1, 2, 2, 9, 9, 1, 1, 1, 1, 3, 9],
[1, 9, 1, 0, 1, 9, 3, 9, 4, 2, 1, 0, 0, 1, 3, 9],
[1, 1, 1, 0, 1, 1, 3, 9, 2, 1, 1, 1, 0, 1, 9, 2],
[0, 1, 1, 1, 1, 1, 3, 3, 3, 2, 9, 1, 0, 2, 2, 2],
[0, 1, 9, 1, 2, 9, 4, 9, 9, 4, 3, 2, 0, 1, 9, 1],
[0, 1, 1, 2, 3, 9, 9, 3, 3, 9, 9, 3, 1, 2, 1, 1],
[0, 0, 0, 1, 9, 3, 2, 1, 1, 3, 9, 3, 9, 1, 0, 0],
];
case 22:
return [
[1, 9, 3, 9, 2, 1, 1, 2, 2, 3, 9, 1, 1, 2, 9, 2, 9, 9, 2, 9, 3, 9],
[2, 2, 4, 9, 3, 3, 9, 4, 9, 9, 2, 1, 1, 9, 2, 2, 3, 3, 3, 2, 4, 9],
[1, 9, 3, 2, 2, 9, 9, 5, 9, 3, 1, 0, 2, 3, 3, 2, 3, 9, 2, 1, 9, 2],
[3, 4, 9, 1, 1, 3, 9, 3, 1, 2, 1, 2, 3, 9, 9, 2, 9, 9, 2, 2, 2, 2],
[9, 9, 3, 1, 0, 2, 2, 2, 0, 1, 9, 3, 9, 9, 3, 2, 2, 3, 2, 2, 9, 1],
[4, 9, 3, 1, 0, 1, 9, 1, 0, 1, 3, 9, 4, 2, 1, 0, 0, 1, 9, 3, 2, 1],
[9, 3, 9, 2, 1, 3, 2, 3, 1, 1, 3, 9, 3, 0, 1, 1, 1, 1, 3, 9, 2, 0],
[2, 3, 2, 2, 9, 3, 9, 3, 9, 1, 2, 9, 3, 1, 2, 9, 1, 0, 2, 9, 2, 0],
[1, 9, 1, 2, 2, 4, 9, 4, 2, 2, 1, 1, 3, 9, 3, 1, 1, 0, 1, 2, 3, 2],
[1, 1, 1, 1, 9, 3, 2, 3, 9, 1, 1, 1, 3, 9, 2, 0, 0, 0, 0, 1, 9, 9],
[0, 0, 0, 1, 1, 2, 9, 2, 2, 2, 2, 9, 2, 1, 2, 1, 1, 0, 0, 1, 2, 2],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 9, 3, 2, 2, 1, 2, 9, 2, 2, 2, 1, 0, 0],
[0, 1, 2, 9, 1, 0, 0, 1, 2, 3, 9, 1, 1, 9, 2, 1, 2, 9, 9, 1, 0, 0],
[0, 1, 9, 3, 3, 2, 1, 1, 9, 3, 2, 1, 1, 1, 1, 0, 1, 2, 2, 1, 0, 0],
[0, 1, 1, 2, 9, 9, 2, 2, 4, 9, 2, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0],
[0, 0, 0, 2, 3, 4, 3, 9, 5, 9, 3, 2, 9, 2, 0, 0, 0, 0, 1, 9, 2, 1],
[1, 1, 0, 1, 9, 3, 9, 4, 9, 9, 3, 2, 9, 2, 0, 1, 1, 1, 1, 2, 9, 1],
[9, 2, 0, 2, 3, 5, 9, 3, 3, 9, 2, 1, 1, 1, 0, 2, 9, 3, 1, 2, 2, 2],
[9, 3, 1, 1, 9, 9, 2, 1, 1, 2, 2, 1, 0, 0, 0, 3, 9, 5, 9, 2, 2, 9],
[3, 9, 2, 1, 2, 2, 1, 0, 0, 1, 9, 1, 0, 0, 0, 2, 9, 4, 9, 2, 2, 9],
[2, 9, 3, 1, 0, 1, 1, 2, 1, 2, 1, 2, 1, 1, 0, 1, 1, 2, 2, 2, 2, 1],
[1, 2, 9, 1, 0, 1, 9, 2, 9, 1, 0, 1, 9, 1, 0, 0, 0, 0, 1, 9, 1, 0],
];
}
};

export const CellState = {
empty: 0,
bomb: 9,
hidden: 10,
flag: 11,
weakFlag: 12,
};

export const generateFieldWithDefaultState = (
size: number,
state = CellState.empty
) => new Array(size).fill(null).map(() => new Array(size).fill(state));