From 722f3e0ac7fd3bc8e9d4728f94cb2811052b0e33 Mon Sep 17 00:00:00 2001 From: Nikita Ovchinnikov Date: Wed, 13 Oct 2021 13:40:08 +0400 Subject: [PATCH] Game module by TDD (Ducks) --- README.md | 4 ++ src/modules/GameWithRedux/game.test.ts | 70 +++++++++++++++++++ src/modules/GameWithRedux/game.ts | 94 ++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 src/modules/GameWithRedux/game.test.ts create mode 100644 src/modules/GameWithRedux/game.ts diff --git a/README.md b/README.md index 257c3ad..2a662e3 100644 --- a/README.md +++ b/README.md @@ -298,3 +298,7 @@ ### Redux basic example [Pull request](https://github.com/nickovchinnikov/minesweeper/pull/53/files) + +### Game module by TDD (Ducks) + +[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/54/files) diff --git a/src/modules/GameWithRedux/game.test.ts b/src/modules/GameWithRedux/game.test.ts new file mode 100644 index 0000000..fd5bffd --- /dev/null +++ b/src/modules/GameWithRedux/game.test.ts @@ -0,0 +1,70 @@ +import { GameSettings } from '@/modules/GameSettings'; +import { CellState, Field } from '@/core/Field'; + +const { empty: e, hidden: h, bomb: b, flag: f, weakFlag: w } = CellState; + +import { reducer, openCell, State } from './game'; + +describe('Game reducer', () => { + const level = 'beginner'; + const baseInitialState: State = { + level, + time: 0, + isGameOver: false, + isGameStarted: false, + isWin: false, + settings: GameSettings[level], + bombs: 0, + flagCounter: 0, + gameField: [ + [9, 1], + [1, 1], + ], + playerField: [ + [h, h], + [h, h], + ], + }; + + describe('Action openCell simple case', () => { + it('Check openCell to cell with a number', () => { + expect(reducer(baseInitialState, openCell([1, 1]))).toEqual({ + ...baseInitialState, + isGameStarted: true, + playerField: [ + [h, h], + [h, 1], + ], + }); + }); + it('Check openCell to cell with a bomb', () => { + expect(reducer(baseInitialState, openCell([0, 0]))).toEqual({ + ...baseInitialState, + isGameStarted: false, + isWin: false, + isGameOver: true, + playerField: baseInitialState.gameField, + }); + }); + it('Check openCell to cell with a flag', () => { + const playerFieldWithFlag = [ + [h, h], + [h, f], + ] as Field; + expect( + reducer( + { + ...baseInitialState, + isGameStarted: true, + playerField: playerFieldWithFlag, + }, + openCell([1, 1]) + ) + ).toEqual({ + ...baseInitialState, + isGameStarted: true, + playerField: playerFieldWithFlag, + }); + }); + }); +}); diff --git a/src/modules/GameWithRedux/game.ts b/src/modules/GameWithRedux/game.ts new file mode 100644 index 0000000..36ffe89 --- /dev/null +++ b/src/modules/GameWithRedux/game.ts @@ -0,0 +1,94 @@ +import { AnyAction, Reducer } from 'redux'; + +import { + Field, + CellState, + generateFieldWithDefaultState, + fieldGenerator, + Coords, +} from '@/core/Field'; +import { LevelNames, GameSettings } from '@/modules/GameSettings'; +import { openCell as openCellHandler } from '@/core/openCell'; + +export interface State { + level: LevelNames; + time: number; + bombs: number; + isGameOver: boolean; + isGameStarted: boolean; + isWin: boolean; + settings: [number, number]; + playerField: Field; + gameField: Field; + flagCounter: number; +} + +export const getInitialState = (): State => { + const level = 'beginner'; + const settings = GameSettings[level]; + const [size, bombs] = settings; + + return { + level, + time: 0, + bombs, + isGameOver: false, + isGameStarted: false, + isWin: false, + settings, + flagCounter: 0, + playerField: generateFieldWithDefaultState(size, CellState.hidden), + gameField: fieldGenerator(size, bombs / (size * size)), + }; +}; + +// Actions +const OPEN_CELL = 'modules/GameWithRedux/OPEN_CELL'; + +// Action Creators +export const openCell = (coords: Coords): AnyAction => ({ + type: OPEN_CELL, + payload: { coords }, +}); + +// Reducer +export const reducer: Reducer = ( + state = getInitialState(), + action: AnyAction +) => { + const { playerField, gameField } = state; + + switch (action.type) { + case OPEN_CELL: { + const { + payload: { coords }, + } = action; + + try { + const [newPlayerField, isSolved] = openCellHandler( + coords, + playerField, + gameField + ); + + return { + ...state, + isGameStarted: true, + isGameOver: isSolved, + isWin: isSolved, + playerField: newPlayerField, + }; + } catch (error) { + return { + ...state, + isGameStarted: false, + isGameOver: true, + isWin: false, + playerField: gameField, + }; + } + } + default: + return state; + } +};