This is a browser implementation of the classic game Minesweeper using Angular 16.
Make sure you have NPM and Yarn installed on your machine, install dependencies with yarn
. Then, just run yarn start
to open the project. yarn test
and yarn test-coverage
will run the Jest unit tests.
- Angular 16
- TailwindCSS with SASS
- Redux (NgRx + RxJs)
- Jest (jest-preset-angular)
- Github Actions
- This project uses SASS for styling (plus TailwindCSS)
- The application can be tested using
yarn test
- The project auto-lints the files, also,
yarn lint:fix
will try to fix linting problems with Prettier - A coverage report is available with jest
yarn test-coverage
- This projects includes load and save features. You can navigate to 'save-and-load' and upload the
saves/savewith35mines.json
file.
This project uses two notable algorithms:
One of the most challenging parts of the game is to implement the following behavior:
If the clicked cell number of adjacent mines is 0, it behaves as if the user had clicked on every cell around it.
This is reached by the Deep Search First algorithm (https://en.wikipedia.org/wiki/Depth-first_search)
Another really useful algorithm used in this project is the Fisher–Yates shuffle. It ensures the creation of truly randomized grids, so each game you generate in this game is unique! (https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)
This project aims to have a high unit test coverage for statements.
-----------------------------------------------------|---------|----------|---------|---------|-------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s -----------------------------------------------------|---------|----------|---------|---------|-------------------- All files | 94.01 | 86 | 90.69 | 93.81 | app | 100 | 100 | 100 | 100 | app.component.html | 100 | 100 | 100 | 100 | app.component.ts | 100 | 100 | 100 | 100 | app/features/game/components/main-game | 93.93 | 100 | 83.33 | 93.93 | main-game.component.html | 100 | 100 | 100 | 100 | main-game.component.ts | 93.75 | 100 | 83.33 | 93.75 | 59,71 app/features/game/containers/board | 100 | 100 | 100 | 100 | board.component.html | 100 | 100 | 100 | 100 | board.component.ts | 100 | 100 | 100 | 100 | app/features/game/containers/cell | 100 | 100 | 100 | 100 | cell.component.html | 100 | 100 | 100 | 100 | cell.component.ts | 100 | 100 | 100 | 100 | app/features/game/containers/reset-button | 100 | 100 | 100 | 100 | reset-button.component.html | 100 | 100 | 100 | 100 | reset-button.component.ts | 100 | 100 | 100 | 100 | app/features/game/containers/seven-segment | 100 | 100 | 100 | 100 | seven-segment-display.component.html | 100 | 100 | 100 | 100 | seven-segment-display.component.ts | 100 | 100 | 100 | 100 | app/features/save-and-load/components/save-and-load | 48.14 | 0 | 57.14 | 48.14 | save-and-load.component.html | 100 | 100 | 100 | 100 | save-and-load.component.ts | 46.15 | 0 | 57.14 | 46.15 | 30-58 app/features/settings/components | 100 | 100 | 100 | 100 | settings.component.html | 100 | 100 | 100 | 100 | settings.component.ts | 100 | 100 | 100 | 100 | app/features/settings/store | 100 | 100 | 100 | 100 | settings.actions.ts | 100 | 100 | 100 | 100 | settings.reducer.ts | 100 | 100 | 100 | 100 | settings.selectors.ts | 100 | 100 | 100 | 100 | app/features/shared/containers/error | 100 | 100 | 100 | 100 | error.component.html | 100 | 100 | 100 | 100 | error.component.ts | 100 | 100 | 100 | 100 | app/features/shared/containers/header | 100 | 100 | 100 | 100 | header.component.html | 100 | 100 | 100 | 100 | header.component.ts | 100 | 100 | 100 | 100 | app/features/shared/containers/loader | 100 | 100 | 100 | 100 | loader.component.html | 100 | 100 | 100 | 100 | loader.component.ts | 100 | 100 | 100 | 100 | app/features/shared/containers/page-wrapper | 100 | 100 | 100 | 100 | page-wrapper.component.html | 100 | 100 | 100 | 100 | page-wrapper.component.ts | 100 | 100 | 100 | 100 | app/models | 100 | 100 | 100 | 100 | cell.model.ts | 100 | 100 | 100 | 100 | gameStatus.model.ts | 100 | 100 | 100 | 100 | level.model.ts | 100 | 100 | 100 | 100 | sessionTypes.ts | 100 | 100 | 100 | 100 | app/services | 89.38 | 84.21 | 78.78 | 88.99 | click-handler.service.ts | 84.31 | 92.85 | 72.72 | 84.31 | 32,37,42,59-64,123 create-level.service.ts | 89.18 | 0 | 60 | 88.23 | 24,28,32,36 storage.service.ts | 100 | 100 | 100 | 100 | timer.service.ts | 100 | 75 | 100 | 100 | 45 app/state | 100 | 100 | 100 | 100 | app.actions.ts | 100 | 100 | 100 | 100 | app.effects.ts | 100 | 100 | 100 | 100 | app.reducer.ts | 100 | 100 | 100 | 100 | app.selectors.ts | 100 | 100 | 100 | 100 | app/utils | 100 | 100 | 100 | 100 | fisher-yates-shuffle.ts | 100 | 100 | 100 | 100 | mock-board.ts | 100 | 100 | 100 | 100 | mock-cell.ts | 100 | 100 | 100 | 100 | mock-settings.ts | 100 | 100 | 100 | 100 | neighbor-offsets.ts | 100 | 100 | 100 | 100 | predefinedLevels.ts | 100 | 100 | 100 | 100 | store-utils.ts | 100 | 100 | 100 | 100 | -----------------------------------------------------|---------|----------|---------|---------|--------------------
Thanks to eugeneloza that provided the assets here https://opengameart.org/content/minesweeper-tile-set. Also, to the developers behind each of the imported GitHub Actions. This project uses the DSEG7(https://github.com/keshikan/DSEG) font.