Alias game

This repository contains the final project for SOLVD's Node.js development course.

Table of Contents


Task

Develop the Alias game, a multiplayer game built with Node.js. It includes chat functionality and a feature to check for similar words.

Game Description

Alias is a word-guessing game where players form teams. Each team takes turns where one member describes a word and others guess it. The game includes a chat for players to communicate and a system to check for similar words.

Objective

Teams try to guess as many words as possible from their teammates' descriptions.

Turns

Each turn is timed. Describers cannot use the word or its derivatives.

Scoring

Points are awarded for each correct guess. Similar words are checked for validation.

End Game

The game concludes after a predetermined number of rounds, with the highest-scoring team winning.

System Requirements

  • Backend: Node.js
  • Database: CouchDB

Setup and Installation

To install the project you must follow these steps:

  1. Install Node.js
  2. Clone this repository by running git clone https://github.com/danielDeVita/solvd.laba.aliasProject.git wherever you want to store the repository
  3. Run npm install inside the repository directory to install all project dependencies.
  4. To run the app locally run npm start

Database setup

To setup and run the database you must follow these steps:

  1. Install Docker
  2. Make sure the Docker Daemon is running. You can check this using the docker info command.
  3. Run docker compose up -d inside the repository directory to build the database docker container
  4. You can now access the database user interface here

Database Schema

User

Contains information about users

Property name Type Description
email string user's email address
password string the user's hashed password
firstName string the user's first name
lastName string the user's last name
role enum [user, admin, inactive] the user's role
createdAt date the date the user entry was created
updatedAt date the date when the user was last updated

Room

Contains information about a game room and its configuration

Property name Type Description
_id string the user id generated by the db
teamNumberOfPlayers number the max amount of players each team can have
roundTime number The amount of time each round will take (in seconds)
teamAPlayers string[] the emails of the users playing in team A
teamBPlayers string[] the emails of the users playing in team B
teamAPoints number the amount of points scored by team A
teamBPoints number the amount of points scored by team B
createdAt date the date the room entry was created
updatedAt date the date when the room was last updated

Chat Message

Contains messages sent to the chat

Property name Type Description
_id string the message id generated by th db
message string the message sent
roomId string the id of the room the message was sent in
createdBy string the id of the user that created the room
createdAt date the date when the message was sent

Security and Authentication

This project uses JWT tokens for authentication, clients must log in to receive a token which they must include as a header (named Authorization) in every HTTP request they make (other than register and login).

API Endpoints

GET chat/messages/

Returns all chat messages

Example response:

[
  {
    "_id": "47471e5c-f695-43a3-883f-24eb133096b6",
    "message": "Hello World!",
    "roomId": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
    "createdBy": "johndoe@example.com",
    "createdAT": "2007-11-09 T 11:20 UTC"
  },
  ...
]

GET chat/:room/messages/

Returns all chat messages sent to a given room

Request param type description
roomId string the id of the room we want to retrieve the messages from

Example:

GET chat/c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1/messages/

[
  {
    "_id": "47471e5c-f695-43a3-883f-24eb133096b6",
    "message": "Hello World!",
    "roomId": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
    "createdBy": "johndoe@example.com",
    "createdAT": "2007-11-09 T 11:20 UTC"
  },
  ...
]

If roomId is not found
HTTP 404 NOT FOUND
{
  "error": "Room not found"
}

POST /room/

Creates a room

Request body must include max. number of players for each team, round time, and number of rounds to play. Example:

{
    "teamNumberOfPlayers": 2,
    "roundTime": 60,
    "roundsToPlay": 4
}

It returns the created room

{
  "teamNumberOfPlayers": 2,
  "roundTime": 60,
  "roundsToPlay": 4,
  "_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
  "createdBy": "johndoe@example.com",
  "createdAt": "2007-11-09 T 11:20 UTC",
  "updatedAt": "2007-11-09 T 11:20 UTC",
  "teamAPlayers": [],
  "teamBPlayers": [],
  "teamAPoints": 0,
  "teamBPoints": 0
}

GET room/:id

Returns a room by its id

Request param type description
roomId string the id of the room we want to retrieve

Example:

GET room/c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1

{
  "teamNumberOfPlayers": 2,
  "roundTime": 60,
  "roundsToPlay": 4,
  "_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
  "createdBy": "johndoe@example.com",
  "createdAt": "2007-11-09 T 11:20 UTC",
  "updatedAt": "2007-11-09 T 11:20 UTC",
  "teamAPlayers": [],
  "teamBPlayers": [],
  "teamAPoints": 0,
  "teamBPoints": 0
}

If roomId is not found
HTTP 404 NOT FOUND
{
  "error": "Room not found"
}

GET room/

Retuns all created rooms. Example response:

GET room/
[
  {
    "teamNumberOfPlayers": 2,
    "roundTime": 60,
    "roundsToPlay": 4,
    "_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
    "createdBy": "johndoe@example.com",
    "createdAt": "2007-11-09 T 11:20 UTC",
    "updatedAt": "2007-11-09 T 11:20 UTC",
    "teamAPlayers": [],
    "teamBPlayers": [],
    "teamAPoints": 0,
    "teamBPoints": 0
  },
  {
    "teamNumberOfPlayers": 4,
    "roundTime": 50,
    "roundsToPlay": 7,
    "_id": "6bd5462f-4726-4d76-a3ef-4b4bc41cfc7d",
    "createdBy": "janedoe@example.com",
    "createdAt": "2007-11-09 T 11:20 UTC",
    "updatedAt": "2007-11-09 T 11:20 UTC",
    "teamAPlayers": [],
    "teamBPlayers": [],
    "teamAPoints": 0,
    "teamBPoints": 0
  },
  ...
]

GET room/user/:userId

Returns rooms created by a given user

Request param type description
userId string the id of the user

Example:

GET room/user/johndoe@example.com

{
  "teamNumberOfPlayers": 2,
  "roundTime": 60,
  "roundsToPlay": 4,
  "_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
  "createdBy": "johndoe@example.com",
  "createdAt": "2007-11-09 T 11:20 UTC",
  "updatedAt": "2007-11-09 T 11:20 UTC",
  "teamAPlayers": [],
  "teamBPlayers": [],
  "teamAPoints": 0,
  "teamBPoints": 0
}

If userId is not a string
HTTP 400 BAD REQUEST
{
  "error": "Invalid userId"
}

PATCH room/:id

Joins a user to a room

Request param type description
id string the id of the room we want to retrieve

Body must include what team they want to join

{
    "team": "teamA"
}

Returns info of the room they joined Example:

{
  "teamNumberOfPlayers": 2,
  "roundTime": 60,
  "roundsToPlay": 4,
  "_id": "c3631e53-06e5-4e22-aa11-a4f1ea1efc3b1",
  "createdBy": "johndoe@example.com",
  "createdAt": "2007-11-09 T 11:20 UTC",
  "updatedAt": "2007-11-09 T 11:20 UTC",
  "teamAPlayers": [],
  "teamBPlayers": [],
  "teamAPoints": 0,
  "teamBPoints": 0
}

If roomId is not found
HTTP 404 NOT FOUND
{
  "error": "Room not found"
}

If user is already in a team
HTTP 403 FORBIDDEN
{
  "error": "User is already in a team"
}

If selected team is already complete
HTTP 403 FORBIDDEN
{
  "error": "Selected team is complete"
}

POST /user/register

Registers a new user Body must include email, password, first name, last name and role. EXample:

{
    "email": "johndoe@gmail.com",
    "firstName": "john",
    "lastName": "doe",
    "role": "user",
    "password": "12345678"
}

Returns the created user.

{
  "email": "johndoe@example.com",
  "firstName": "john",
  "lastName": "doe",
  "role": "user",
  "password": "12345678",
  "createdAt": "2007-11-09 T 11:20 UTC",
  "updatedAt": "2007-11-09 T 11:20 UTC"
}

If user already exists
HTTP 409 CONFLICT
{
  "error": "User already exists"
}

POST /user/login

Logs a user in. Request body must contain email and password. Example:

{
    "email": "johndoe@example.com",
    "password": "12345678"
}

Returns the generated JWT token.

GET room/user/johndoe@example.com

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJFbWFpbGxAZ21haWwuY29tIiwicm9sZSI6InVzZXJhIiwiaWF0IjoxNzAwODM1MjU3LCJleHAiOjE3MDA4Mzg4NTd9.N_Nei7roLTAYbMAMUMwNGlvH2mpS-EZQi7zgp1ksHUc"
}

If user is not found
HTTP 404 NOT FOUND
{
  "error": "User not found"
}

If password is incorrect
HTTP 400 BAD REQUEST
{
  "error": "Invalid password"
}

Socket Events

In order for the chat and game to work across multiple clients in real time, this project uses socket.io to communicate different events across clients joined in the same room.

All events must include:

Event headers type description
roomId string the id of the room
Authorization string users jwt token

Chat events

joinRoom

Joins a player to a chat room.

leaveRoom

Removes a player from the chat room.

message

Sends a message to the chat room. Event message should look like:

{
  "message": "Hello World!"
}

Game events

ready

Indicated that a user is ready to play the game.

guess-word

A user tries to guess the word. Event message should look like:

{
    "word": "humus"
}

send-hint

A user sends a hint to help his teammates guess the word. Event message should look like:

{
    "hint": "A dip made of chickpeas"
}

start-room-game

The game begins. Event message should look like:

{
    "roomId": "{{roomId}}"
}

Testing

This project uses jest for unit testing, to run said tests execute npm test

CI/CD

This repository uses GitHub actions for continuous integration. This action builds, tests and lints the project's code. The workflow file can be fount in .github/workflows/pipeline.yml

FAQ

  • How do I start a game?

    To start a game you must first posses a user, you can create a new one through the register functionality. Once you are a valid user, you must log in to obtain you JWT authentication token (Remember you must include this token in all HTTP request and Socket events as a header). The next step is to create a room, indicating the configuration for your game. Now all you have to do is wait for other users to join your room, once both teams have at least 2 members and everyone has sent the ready socket event, the start game socket event can be sent and the game will begin!

  • What happens if my team does't guess a word?

    If your team hasn't guessed a particular word before the time runs out and a hint for that word has already been sent the other team will get 30 seconds to try and guess that same word using the hints your teammate has already sent. Once they guess the word or the 30 seconds are up, their normal turn will begin.

  • Why isn't my game starting?

    Make sure you created the room with all the correct configurations, also check that the roomId fellow teammates are using to join the room is correct. Remember at least 2 players of each team mush emit the ready socket event to start the game.