Idnan-Haider / angular-tensorflow-snake

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Angular TensorFlow.js Snake

Snake Header

An old school snake game with a twist - your hand is the controller!

The app aims to demonstrate the power of combining Angular and TensorFlow.js to build highly interactive and reactive applications.

Built for Google I/O 2023 with Angular 16.0.0-next.5 and running in two versions:

  • One using RxJS.
  • And another using the new and awesome Angular signals.

Want to play right away? Try the live app now deployed from the main branch to Firebase.

Built by This Dot in partnership with the Angular Team at Google


1. Getting Started

First, run

npm install

to install all the dependencies.

To run the game locally on a development server, you can run the following command:

npm start

To build the app, run the following command:

npm run build

Linting and Formatting

To check for any linting issues, you can run the following command:

npm run lint

To fix automatically fixable linting issues, run:

npm run lint:fix

To check for any formatting issues, you can run the following command:

npm run format:check

To format all files to conform to the project's Prettier configuration, you can run:

npm run format

Running Tests

To run all unit tests use the following command:

npm run test:once

To run unit tests in watch mode, run:

npm run test

To generate a coverage report, you can run

npm run test:coverage

To view the generated coverage report you can open coverage/index.html in a browser of your choice.

2. Architecture Overview

The app is a fully standalone Angular application and it contains two different implementations of the game - one where communication is done with signals and another where the communication is realized via RxJs.

2.1 The Game Service(s)

The game itself is realized via a Game Service which holds the information about the game state, moves the snake in ticks according to given speed and exposes methods to setup(), start() and pause() the game.

The setup() method takes an optional config argument, in which the following options can be set:

Option Type Description Default Value
gridSize GridSize An object specifying width and height of the grid (number of tiles on the x and y axis). { width: 10, height: 10 }
snake Snake An instance of the Snake class containing the snake segments coordinates (e.g. new Snake({ x: 1, y: 1})). A Snake instance with one segment placed at a random position within the grid.
food Coordinates An object specifying x and y coordinates of the "apple" on the grid. A random position on the grid.
initialSpeed number A number that determines how many times per second the snake moves in the game, with higher values leading to faster movement. 2 (Two ticks per second.)

Furthemore, the service exposes a setDirection(direction: Direction) as a way to inform the game about where the snake should be heading, meaning that, in theory, any kind of controller can be used to control the game. The Direction enum is specified as follows:

Key Value
Left ๐Ÿ‘ˆ
Right ๐Ÿ‘‰
Up โ˜๏ธ
Down ๐Ÿ‘‡

There are two different implementation of the game service within the app:

  • SignalsGameService, which communicates the game state using Angular signals.
  • RxjsGameService, which communicates the state using RxJs Observables.

Both classes inherit from a common abstract GameServiceBase class which contains some common code and specifies the setup(), start(), pause() and setDirection() methods as abstract methods to ensure common public interface:

Diagram of the Game Service

The classes, models and utility functions are located in the src/app/game folder.

2.2 The Game UI

The components that handle the game UI live in the src/app/game/game-board folder and, similar to the services, there are two different components:

  • SignalsGameBoardComponent, which injects the SignalsGameService and communicates with it via Angular signals.
  • RxjsGameBoardComponent, which injects the RxjsGameService and communicates with it via RxJS Observables and async pipes.

Both components share the same game-board.component.scss file for styles and utility functions defined in game-board.utils.ts file.

2.3 Routing

The two different implementations are available via routing:

  • The /signals route provides the SignalsGameService and displays the SignalsGameBoardComponent.
  • The /rxjs route provides the RxjsGameService and displays the RxjsGameBoardComponent.

Other components are shared between implementations and they inject the GameServiceBase which resolves to the correct instance of the game service specified in the routing configuration via the useExisting Angular providers mechanism.

By default, the signals route is displayed. If you want to check out the RxJs version, you'll need to manually change the url in your browser from "/signals" to "/rxjs".

Refer to src/main.ts for more details about how the routing and providers are configured.

2.4 The Game Controller

The game controller code lives in the src/app/controller folder. The UI is handled by the ControllerComponent which further references the ArrowsComponent to display the direction pad which visualizes the current direction the user is pointing to, the ControlButtonsComponent which displays the buttons to start, pause and reset the game and calls the game service accordingly and the WebcamComponent which displays the camera feed.

2.4.1 The Game Controlling Mechanism

The actual mechanism to control the game is realized by two directives which are meant to be added to a <video> tag:

  • The CameraFeedDirective which uses the NavigatorService to capture user's camera and output the feed into the <video> tag.
  • The HandDetectorDirective which imports TensorFlow.js WebGl backend and the pre-trained hand pose detection model, which is then used to estimate a direction where the user's hand is pointing and sends it upwards using the directionChange EventEmitter. The WebcamComponent then reacts to this event and calls the game service's setDirection() method.

2.5 Class Diagram & Further Documentation

A high level UML diagram of the application was generated using tplant, you can click on the image to open the full view:

Snake Class Diagram

For further documentation, you can build and serve a Compodoc instance inside the app by running

npm run compodoc:build-and-serve

To build the Compodoc documentation into the docs folder without serving it, you can run

npm run compodoc:build

An actual version of the UML diagram will be generated every time you run the above commands. To only regenerate the UML diagram, you can run

npm run uml:overview

To serve the already built docs, you can run

npm run compodoc:serve

3. Contributing

Pull Requests (PR) with bug fixes or features are welcome! Please see the contributing guidelines before submitting a PR.

If you have an idea for a new feature or a question regarding the app, please open an issue first so it can be discussed and vetted before contributing.

About

License:MIT License


Languages

Language:TypeScript 54.8%Language:SCSS 33.5%Language:HTML 10.1%Language:JavaScript 1.6%